How do you use sockets to send / receive data without waiting. I have a very basic knowledge of networking, and I can now finially send and receive data...but I have to wait for the data. This works fine in a turn based game, but what about real time gaming? I've never dealt with multi threading before...and all of my windows programming uses allegro and is all single threaded using lock step processing. I've looked at libnet...but can't get it to compile with my program. I keep getting link errors about default libs...and when I ignore default libs...even more link errors. So...I've just started playing with WinSock. Also...how to keep from waiting for a client to join, or at least allow for additional processing to happen while I wait for a client. And what about if you run client before server...you get an error! How do you correct this? I can't guarranty that the server will always be ran first!
I've included the (crappy) source for the client and server below:
Server:
1 | ////////////////////////////////////////////////////////////////////////////////////////////////// |
2 | // <Main.cpp> |
3 | ////////////////////////////////////////////////////////////////////////////////////////////////// |
4 | // Network Server using sockets |
5 | ////////////////////////////////////////////////////////////////////////////////////////////////// |
6 | #include "Allegro.h" |
7 | #include "WinAlleg.h" |
8 | #include <WinSock.h> |
9 | ////////////////////////////////////////////////////////////////////////////////////////////////// |
10 | #define MAXDATASIZE 100 |
11 | ////////////////////////////////////////////////////////////////////////////////////////////////// |
12 | int main( void ) |
13 | { |
14 | ////////////////////////////////////////////////////////////////////////////////////////////// |
15 | SOCKET iListenSocket; |
16 | SOCKET iSocket; |
17 | struct sockaddr_in sLocalAddress; |
18 | struct sockaddr_in sRemoteAddress; |
19 | int sin_size; |
20 | int iYes = 1; |
21 | int iPort = 3490; |
22 | char *buf; |
23 | char recBuf[MAXDATASIZE]; |
24 | ////////////////////////////////////////////////////////////////////////////////////////////// |
25 | allegro_init(); |
26 | set_color_depth(32); |
27 | set_gfx_mode(GFX_DIRECTX_WIN,400,400,0,0); |
28 | set_color_conversion(COLORCONV_TOTAL); |
29 | install_timer(); |
30 | install_keyboard(); |
31 | install_mouse(); |
32 | ////////////////////////////////////////////////////////////////////////////////////////////// |
33 | WSADATA wsData; |
34 | if ( WSAStartup(MAKEWORD(2,2),&wsData) != 0 ) |
35 | { |
36 | return 1; |
37 | } |
38 | if ( (iListenSocket = socket( PF_INET, SOCK_STREAM, NULL )) == -1 ) |
39 | { |
40 | WSACleanup(); |
41 | return 2; |
42 | } |
43 | if ( setsockopt( iListenSocket, SOL_SOCKET, SO_REUSEADDR, (char FAR *)&iYes, sizeof(int)) != 0 ) |
44 | { |
45 | close(iListenSocket); |
46 | WSACleanup(); |
47 | return 3; |
48 | } |
49 | sLocalAddress.sin_family = AF_INET; |
50 | sLocalAddress.sin_port = htons(iPort); |
51 | sLocalAddress.sin_addr.s_addr = INADDR_ANY; |
52 | memset(&(sLocalAddress.sin_zero),'\0',8); |
53 | if ( bind( iListenSocket, (struct sockaddr*)&sLocalAddress, sizeof(struct sockaddr)) != 0 ) |
54 | { |
55 | close(iListenSocket); |
56 | WSACleanup(); |
57 | return 4; |
58 | } |
59 | if ( listen( iListenSocket, 10 ) != 0 ) |
60 | { |
61 | close(iListenSocket); |
62 | WSACleanup(); |
63 | return 5; |
64 | } |
65 | sin_size = sizeof(struct sockaddr_in); |
66 | if ( (iSocket = accept( iListenSocket, (struct sockaddr *)&sRemoteAddress, &sin_size) ) == INVALID_SOCKET ) |
67 | { |
68 | close(iListenSocket); |
69 | WSACleanup(); |
70 | return 6; |
71 | } |
72 | close(iListenSocket); |
73 | ////////////////////////////////////////////////////////////////////////////////////////////// |
74 | // All ready to send and receive... |
75 | buf = "Server connected."; |
76 | textprintf(screen,font,0,0,makecol(255,255,255),"Connection complete..."); |
77 | if ( send(iSocket,buf,sizeof(char),0) == SOCKET_ERROR ) |
78 | { |
79 | textprintf(screen,font,0,8,makecol(255,255,255),"Failed to send data..."); |
80 | } |
81 | else |
82 | { |
83 | textprintf(screen,font,0,16,makecol(255,255,255),"Sent %s to client...",buf); |
84 | } |
85 | if ( recv(iSocket,recBuf,MAXDATASIZE-1,0) == SOCKET_ERROR ) |
86 | { |
87 | textprintf(screen,font,0,24,makecol(255,255,255),"Error recieving data from client..."); |
88 | textprintf(screen,font,0,32,makecol(255,255,255),"Error code: %i",WSAGetLastError()); |
89 | textprintf(screen,font,0,64,makecol(255,255,255),"Error code: %i", WSAEFAULT ); |
90 | } |
91 | else |
92 | { |
93 | textprintf(screen,font,0,32,makecol(255,255,255),"Recieved %s from client...",recBuf); |
94 | } |
95 | clear_keybuf(); |
96 | while ( true ) |
97 | { |
98 | if ( keypressed() ) |
99 | { |
100 | if ( key[KEY_ESC] ) |
101 | { |
102 | clear_keybuf(); |
103 | break; |
104 | } |
105 | clear_keybuf(); |
106 | } |
107 | } |
108 | ////////////////////////////////////////////////////////////////////////////////////////////// |
109 | close(iSocket); |
110 | WSACleanup(); |
111 | return 0; |
112 | ////////////////////////////////////////////////////////////////////////////////////////////// |
113 | } |
114 | END_OF_MAIN() |
115 | ////////////////////////////////////////////////////////////////////////////////////////////////// |
Client:
1 | ////////////////////////////////////////////////////////////////////////////////////////////////// |
2 | // <Main.cpp> |
3 | ////////////////////////////////////////////////////////////////////////////////////////////////// |
4 | // Client using streams |
5 | ////////////////////////////////////////////////////////////////////////////////////////////////// |
6 | #include <Allegro.h> |
7 | #include <WinAlleg.h> |
8 | #include <WinSock.h> |
9 | ////////////////////////////////////////////////////////////////////////////////////////////////// |
10 | #define MAXDATASIZE 100 |
11 | ////////////////////////////////////////////////////////////////////////////////////////////////// |
12 | int main( void ) |
13 | { |
14 | ////////////////////////////////////////////////////////////////////////////////////////////// |
15 | allegro_init(); |
16 | set_color_depth(32); |
17 | set_gfx_mode(GFX_DIRECTX_WIN,400,400,0,0); |
18 | set_color_conversion(COLORCONV_TOTAL); |
19 | install_timer(); |
20 | install_keyboard(); |
21 | install_mouse(); |
22 | ////////////////////////////////////////////////////////////////////////////////////////////// |
23 | int iPort = 3490; |
24 | SOCKET iSocket; |
25 | //char buf[MAXDATASIZE]; |
26 | struct hostent *sHE; |
27 | struct sockaddr_in sServerAddress; |
28 | WSADATA wsaData; |
29 | WSAStartup(MAKEWORD(2,2),&wsaData); |
30 | if ( (sHE = gethostbyname("DON")) == NULL ) |
31 | { |
32 | WSACleanup(); |
33 | return 1; |
34 | } |
35 | if ( (iSocket = socket( PF_INET, SOCK_STREAM, 0 )) == -1 ) |
36 | { |
37 | WSACleanup(); |
38 | return 2; |
39 | } |
40 | sServerAddress.sin_family = AF_INET; |
41 | sServerAddress.sin_addr = *((struct in_addr*)sHE->h_addr_list[0]); |
42 | sServerAddress.sin_port = htons(iPort); |
43 | memset(&(sServerAddress.sin_zero),'\0',8); |
44 | if ( connect( iSocket, (struct sockaddr*)&sServerAddress,sizeof(struct sockaddr) ) != 0 ) |
45 | { |
46 | int i = WSAGetLastError(); |
47 | textprintf(screen,font,0,0,makecol(255,255,255),"%i",i); |
48 | while ( !keypressed()){} |
49 | close(iSocket); |
50 | WSACleanup(); |
51 | return 3; |
52 | } |
53 | ////////////////////////////////////////////////////////////////////////////////////////////// |
54 | // Ready to send and recieve... |
55 | clear_keybuf(); |
56 | while ( true ) |
57 | { |
58 | if ( keypressed() ) |
59 | { |
60 | if ( key[KEY_ESC] ) |
61 | { |
62 | clear_keybuf(); |
63 | break; |
64 | } |
65 | clear_keybuf(); |
66 | } |
67 | } |
68 | ////////////////////////////////////////////////////////////////////////////////////////////// |
69 | close(iSocket); |
70 | WSACleanup(); |
71 | return 0; |
72 | ////////////////////////////////////////////////////////////////////////////////////////////// |
73 | } |
74 | END_OF_MAIN() |
75 | ////////////////////////////////////////////////////////////////////////////////////////////////// |
Thanks in advance.
In liue of someone posting something more helpful look up the 'select' function. Basically it lets you poll the sockets for information and if theres anything there do something with it. If not move on.
Check this (the whole thread is interesting too). You have two ways, marking the socket as asynchronic or using select to multiplex the socket.
Check into nonblocking socket I/O, and MSG_DONTWAIT for recv().
This is how I do it:
int blockParam = 1;//used to specify a non-blocking socket if (ioctlsocket(winSocket, FIONBIO, (u_long FAR*)&blockParam) != 0) // use WSAGetLastError() to figure out what went wrong else //it's all good
And now you need to check if your send or receive would have blocked, and distinguish that case from "real" errors.
result = send(winSocket, (char*)message, messageSize, 0); if (result == SOCKET_ERROR) { winsockCode = WSAGetLastError(); if ((winsockCode != WSAEWOULDBLOCK) && (winsockCode != WSAENOBUFS)) //we hit some socket error, probably best to close the socket else //couldn't send anything this time, try again later }//if result
result = recv(winSocket, (char*)buffer, dyBufferSize, 0); if (result == SOCKET_ERROR) { winsockCode = WSAGetLastError(); if ((winsockCode != WSAEWOULDBLOCK) && (winsockCode != WSAEMSGSIZE)) //hit an error else //just need to wait, nothing received this time }//if result
Ok...I'm using 'select' right now. It lets me tell when a socket is ready to send or receive data...so I can call them without having to wait! Now...how do I allow for other processing or a timeout for connection requests?!? Can 'select' do this as well? I don't want someone to click host game and then.................wait for eternity for someone to join or not! I like how DirectPlay connections work (or at least how they look and feel, don't know about the programming aspect). I don't want to turn to DirectPlay...portability issues. Thanks for all the excellent advice and help so far...
Can 'select' do this as well?
yes.
when a socket that is listening is marked as "readable" by select a new connection attempt was made - check out the msdn winsock page
Check my example. You put the listening socket in as ever. However, when one of the sockets is set, you first check the listening socket to see if it is the one that is ready. If so, you process the accept call; otherwise you know it is one of the connected sockets that sent some data.
Thanks! That's kinda what I thought...just wanted to make sure that if I tried it...it was my fault if I did it wrong! Also...in the above code sample, you MUST start the server first...is there a way around that? Like I said, I can't always guarranty that a server will be ran first. One more thing...I've noticed that when I use select as below, I get a timeout error and then it seems that it doesn't reset itself to try again. Is there some way to reset it to check for more data (I'm sure there is)? Sorry...one last question for now...this pertains to the below code example as well...I'm I using select correctly? Or should this be seperate from the main loop? It seems that it needs to be polled in the main loop, but I am not for certain.
1 | ////////////////////////////////////////////////////////////////////////////////////////////////// |
2 | // <Main.cpp> |
3 | ////////////////////////////////////////////////////////////////////////////////////////////////// |
4 | // Network Server using sockets |
5 | ////////////////////////////////////////////////////////////////////////////////////////////////// |
6 | #include "Allegro.h" |
7 | #include "WinAlleg.h" |
8 | #include <WinSock.h> |
9 | ////////////////////////////////////////////////////////////////////////////////////////////////// |
10 | #define MAXDATASIZE 100 |
11 | #define TEXTCOLOR makecol(255,255,255) |
12 | ////////////////////////////////////////////////////////////////////////////////////////////////// |
13 | int main( void ) |
14 | { |
15 | ////////////////////////////////////////////////////////////////////////////////////////////// |
16 | SOCKET iListenSocket; |
17 | SOCKET iSocket; |
18 | struct sockaddr_in sLocalAddress; |
19 | struct sockaddr_in sRemoteAddress; |
20 | int sin_size; |
21 | int iYes = 1; |
22 | int iPort = 3490; |
23 | char *buf; |
24 | char recBuf[MAXDATASIZE]; |
25 | fd_set readfds; |
26 | struct timeval tv; |
27 | tv.tv_sec = 10; |
28 | tv.tv_usec = 50; |
29 | FD_ZERO(&readfds); |
30 | ////////////////////////////////////////////////////////////////////////////////////////////// |
31 | allegro_init(); |
32 | set_color_depth(32); |
33 | set_gfx_mode(GFX_DIRECTX_WIN,400,400,0,0); |
34 | set_color_conversion(COLORCONV_TOTAL); |
35 | install_timer(); |
36 | install_keyboard(); |
37 | install_mouse(); |
38 | ////////////////////////////////////////////////////////////////////////////////////////////// |
39 | textprintf(screen,font,0,0,TEXTCOLOR,"Waiting for client..."); |
40 | WSADATA wsData; |
41 | if ( WSAStartup(MAKEWORD(2,2),&wsData) != 0 ) |
42 | { |
43 | return 1; |
44 | } |
45 | if ( (iListenSocket = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP )) == -1 ) |
46 | { |
47 | WSACleanup(); |
48 | return 2; |
49 | } |
50 | if ( setsockopt( iListenSocket, SOL_SOCKET, SO_REUSEADDR, (char FAR *)&iYes, sizeof(int)) != 0 ) |
51 | { |
52 | close(iListenSocket); |
53 | WSACleanup(); |
54 | return 3; |
55 | } |
56 | sLocalAddress.sin_family = AF_INET; |
57 | sLocalAddress.sin_port = htons(iPort); |
58 | sLocalAddress.sin_addr.s_addr = INADDR_ANY; |
59 | memset(&(sLocalAddress.sin_zero),'\0',8); |
60 | if ( bind( iListenSocket, (struct sockaddr*)&sLocalAddress, sizeof(struct sockaddr)) != 0 ) |
61 | { |
62 | close(iListenSocket); |
63 | WSACleanup(); |
64 | return 4; |
65 | } |
66 | if ( listen( iListenSocket, 10 ) != 0 ) |
67 | { |
68 | close(iListenSocket); |
69 | WSACleanup(); |
70 | return 5; |
71 | } |
72 | // Do I add the select option here and check to see if I need to accept? |
73 | // And if so, what option(s) do I check for to see if I need to accept? |
74 | sin_size = sizeof(struct sockaddr_in); |
75 | if ( (iSocket = accept( iListenSocket, (struct sockaddr *)&sRemoteAddress, &sin_size) ) == INVALID_SOCKET ) |
76 | { |
77 | close(iListenSocket); |
78 | WSACleanup(); |
79 | return 6; |
80 | } |
81 | close(iListenSocket); |
82 | ////////////////////////////////////////////////////////////////////////////////////////////// |
83 | // All ready to send and receive... |
84 | buf = "Server connected."; |
85 | textprintf(screen,font,0,8,TEXTCOLOR,"Connection complete..."); |
86 | ////////////////////////////////////////////////////////////////////////////////////////////// |
87 | FD_SET(iSocket,&readfds); |
88 | int rv = 0; |
89 | ////////////////////////////////////////////////////////////////////////////////////////////// |
90 | clear_keybuf(); |
91 | while ( true ) |
92 | { |
93 | if ( keypressed() ) |
94 | { |
95 | if ( key[KEY_ESC] ) |
96 | { |
97 | clear_keybuf(); |
98 | break; |
99 | } |
100 | clear_keybuf(); |
101 | } |
102 | // Do I continue to use select in the while(...) loop? |
103 | rv = select(iSocket+1,&readfds,NULL,NULL,&tv); |
104 | if ( rv == -1 ) |
105 | { |
106 | textprintf(screen,font,0,16,TEXTCOLOR,"Error during 'select' call!"); |
107 | } |
108 | else if ( rv == 0 ) |
109 | { |
110 | // If I get the timeout error...how do I reset select to retry for more data? |
111 | // Do I just issue another FD_SET(iSocket,&readfds) and then call select? |
112 | textprintf(screen,font,0,16,TEXTCOLOR,"Timeout error! No data received during receive window."); |
113 | } |
114 | else |
115 | { |
116 | textprintf(screen,font,0,16,TEXTCOLOR,"Select ready..."); |
117 | if ( FD_ISSET(iSocket,&readfds) ) |
118 | { |
119 | textprintf(screen,font,0,24,TEXTCOLOR,"Data is ready to be received."); |
120 | textprintf(screen,font,0,32,TEXTCOLOR,"Reading data..."); |
121 | recv(iSocket,recBuf,sizeof(recBuf),0); |
122 | textprintf(screen,font,0,40,TEXTCOLOR,"%s",recBuf); |
123 | } |
124 | } |
125 | } |
126 | ////////////////////////////////////////////////////////////////////////////////////////////// |
127 | close(iSocket); |
128 | WSACleanup(); |
129 | return 0; |
130 | ////////////////////////////////////////////////////////////////////////////////////////////// |
131 | } |
132 | END_OF_MAIN() |
133 | ////////////////////////////////////////////////////////////////////////////////////////////////// |
Thanks again all...