How to send and receive sockets with out waiting for a reply...
Don Freeman

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//////////////////////////////////////////////////////////////////////////////////////////////////
12int 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}
114END_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//////////////////////////////////////////////////////////////////////////////////////////////////
12int 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}
74END_OF_MAIN()
75//////////////////////////////////////////////////////////////////////////////////////////////////

Thanks in advance.

kazzmir

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.

ReyBrujo

Check this (the whole thread is interesting too). You have two ways, marking the socket as asynchronic or using select to multiplex the socket.

BAF

Check into nonblocking socket I/O, and MSG_DONTWAIT for recv().

Myrdos

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

Don Freeman

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...

Frank Drebin
Quote:

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

ReyBrujo

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.

Don Freeman

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//////////////////////////////////////////////////////////////////////////////////////////////////
13int 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}
132END_OF_MAIN()
133//////////////////////////////////////////////////////////////////////////////////////////////////

Thanks again all...

Thread #564257. Printed from Allegro.cc