|
|
| WinSock 2 Asynchronous |
|
Fishcake
Member #8,704
June 2007
|
I'm learning network programming using WinSock 2 based on a tutorial on GameDev.net http://www.gamedev.net/reference/articles/article1059.asp Currently, I'm just trying to create a simple console program (no window!). I want the server to be able to do stuff like send some strings to the clients and responds when a client disconnected. But I couldn't get this line to work (I'm really new to this) : WSAAsyncSelect (s, hWnd, WM_ONSOCKET, (FD_READ | FD_CONNECT | FD_CLOSE)); I don't know where the second parameter came from... CLIENT
SERVER
|
|
Synapse Jumps
Member #3,073
December 2002
|
It seems like you're quite new to this; MSDN is your friend: It seems like the second argument is for a window handle. Have you done windows programming at all? If not, this is gonna be a brick wall for you. I don't know if you need this line, truly. I assume you're using it so that the call to connect or accept will not be blocking (ie: it will not hang). For this you'll get to enter a magical new world: THREADS! A thread is like another process running within your main process. In the linux world you can create a thread via the fork() function. In the Windows world, however, you have a whole nother barrel of monkeys to open to use threads. For this you'll want to look into the CreateThread Function. Just to get you started, you should note that most of the parameters to this function will be useless to you until you start getting into higher levels of threads. For now, the most important parameters are lpStartAddress (the function you want the thread to run; in other words- the main function of your thread), lpParameter (the parameter that you want to pass to the main function of your thread), and lpThreadID which is only important if you need to close your thread from within your main process. For a great, MS filled example look here. |
|
Fishcake
Member #8,704
June 2007
|
Thanks for the links! Looks like I have to learn Windows Programming first
|
|
aj5555
Member #9,033
September 2007
|
Synapse Jumps, you make out like Windows Threads are harder than linux threads. I use a single function to make thread. with 3 parameteres, 1 is 0, the other is a pointer to your own function, and one is the start function. Thats it! |
|
Fishcake
Member #8,704
June 2007
|
Okay, I've found a tutorial on Windows Programming and learned a bit about the basics. The client is now using asynchronous socket. But I'm facing this problem right now. The client is able to connect to the server (I detected it using FD_CONNECT by prompting a message box) but as soon as I clicked the "OK" button on the message box, another message box saying "Unable to connect!" pops up. The second message box came from an error checking to check whether the client is able to connect to the server: if (connect (s, (LPSOCKADDR)&target, sizeof(target)) == SOCKET_ERROR) { MessageBox(NULL, "Unable to connect!", "Error!", MB_ICONEXCLAMATION | MB_OK); WSACleanup (); return 0; } What's the problem here? And why I can't connect in the first place? I didn't face this problem when I wrote a simple client program (without asynchronous socket). CLIENT
SERVER
|
|
ImLeftFooted
Member #3,935
October 2003
|
Using threads is overkill. All the packet processing is done behind your back anyway. All your application is doing is reading memory... Using threads for networking is like using an oversized truck to move a harddrive. Silly and a total waste of gas. |
|
Synapse Jumps
Member #3,073
December 2002
|
aj: Well... pretty much everything in Windows is complicated. I mean, if you've never done any Windows programming before, anything is bound to be hard, especially something as deeply abstract and complicated as threads. Linux threads are just as abstract and complicated, but I feel their implementation just makes more sense. shrugs To each his own? Dustin: Oh please. If he wants to learn how to do network programming he damn sure well better know how to use threads. Maybe he doesn't need them just yet, but its a really useful tool to have in your 'programming arsenal.' I mean, unless you just want to serve one client forever, or only wait for those blocking accept calls to serve more than one client. Fish: I think you're thinking about this a little backwards. Usually servers are the heavily asynchronous sides of the networked world. They both listen for incoming connections and serve information to their clients. Their trick is that they often accept and serve several clients simultaneously- a feat nearly impossible without the helping hands of threads. What you're trying to do here really doesn't require asynchronous activity. I suppose I should apologize for leading you astray with threads, but, as mentioned above: they're terribly useful and a great thing to know about. Anyways, I think I'vefigured out why you're getting this strange error. Check out the MSDN on connect. Specifically the part about the return values: MSDN said: On a blocking socket, the return value indicates success or failure of the connection attempt. With a nonblocking socket, the connection attempt cannot be completed immediately. In this case, connect will return SOCKET_ERROR, and WSAGetLastError will return WSAEWOULDBLOCK. In this case, there are three possible scenarios... As you can see, your socket is nonblocking, and therefore will force a return of SOCKET_ERROR. Not that you really have an error, just that you can't connect at just that moment. Get it? You're on the right track, though, for sure. |
|
Fishcake
Member #8,704
June 2007
|
I don't really get what you're saying but the problem was solved when I changed the arrangement of the codes : if (connect (s, (LPSOCKADDR)&target, sizeof(target)) == SOCKET_ERROR) { MessageBox(NULL, "Unable to connect!", "Error!", MB_ICONEXCLAMATION | MB_OK); WSACleanup (); return 0; } WSAAsyncSelect (s, hwnd, WM_ONSOCKET, (FD_READ | FD_CONNECT | FD_CLOSE));
|
|
aj5555
Member #9,033
September 2007
|
Quote: Well... pretty much everything in Windows is complicated. No its not. Quote: I mean, if you've never done any Windows programming before, anything is bound to be hard If you've never done any Windows programming, its bound to be UNKNOWN, it might be easy, it might be hard. Is everything new to you difficult? Have you a learning disability? Quote: especially something as deeply abstract and complicated as threads.
what is so complicated about one very simple idea.. instead of having one piece of code running, you have several. What is so hard to comprehend about that? |
|
Synapse Jumps
Member #3,073
December 2002
|
Fishcake: Ya, that would fix the problem. When your sockets are blocking, ie: synchronous, if you ask them to connect, they'll keep trying to connect untl it happens or times out, without halting to let ANYTHING else in your program happen. When your sockets are asynchronous, ie: nonblocking, if you ask them to connect, they will immediately return a value of SOCKET_ERROR so as to not continue blocking your programs execution. They will, however, connect, simply in the background where they don't have to keep your code from executing. This is most likely accomplished via some sort of thread or seperate Windows process. aj: Damn, dude. Lets argue semantics a little more, shall we? Yes, new things are generally more difficult for me to grasp than things that I currently have a good understanding of. Damn this learning disability of mine! the false AJ said:
what is so complicated about one very simple idea.. instead of having one piece of code running, you have several. What is so hard to comprehend about that? Right. That's all there is to threads. You don't need to specify a stack size for them, nor do you need to free their memories. Opening and closing several threads at run-time, that's not important, or complicated! Just two pieces of code running, right? An operating system is just several pieces of code running at once; what's so hard about that? When do we get AJOS? Will the information provided be in sentence fragments and structured as grammatical nightmares? Honestly, writing is simple: just put letters together and punctuate it correctly. Is everything simple for mankind difficult for you? Really, its not rocket science. PS: Speaking of rockets, please preform surgery on a rocket. We're seriously lacking in Darwin Awards on Allegro.cc |
|
Fishcake
Member #8,704
June 2007
|
Following Beej's Guide to Network Programming, I was able to turn the server into non-blocking using the select() function. How do I use select() to turn the client into non-blocking ( without using WSAAsyncSelect() ) so that send() and recv() do not block? Or should I just stick to WSAAsyncSelect()? (I hate using WINAPI with allegro
|
|
Synapse Jumps
Member #3,073
December 2002
|
Umm... If you've got the server you should be able to get the client. You just have to check in your main loop if the socket is ready to recieve stuff... Is send even blocking? Anyways this is pseudocode for it:
|
|
Fishcake
Member #8,704
June 2007
|
fcntl(s, F_SETFL, O_NONBLOCK); I have problem with this line. I have included fcntl.h, but F_SETFL and O_NONBLOCK are not defined ------------------------------------------------------------------------------------ About my server, I want to allow only 2 clients to connect. If an extra client tries to connect, the server will not accept it. But if one or more connected client disconnected, the server will accept connections again. How do I do that? Currently, it accepts every connection. Here's the source code :
|
|
Synapse Jumps
Member #3,073
December 2002
|
Indeed, it looks like fcntl is the Linux version of this function call. The windows version is ioctlsocket. As for recv() not running: Well... Yah. Did you even read the documentation on select() or the Beej guide? The way that code is set up, the socket MUST have something to read (and select() must say it does) before it will actually receive anything. As for setting the last parameter of select() to NULL: MSDN said: timeout Maximum time for select to wait, provided in the form of a TIMEVAL structure. Set the timeout parameter to null for blocking operations. It would seem that it is not recv() that's blocking, but rather select(). To fix these problems, you need to set the socket to nonblocking mode with the Windows function call (the one mentioned above; there's a tutorial further down on the MSDN page). Then everything should fall into place. As for your server: //the line if(i == listener) //should be if(i == listener && num_clients < 2)
|
|
ImLeftFooted
Member #3,935
October 2003
|
Synapse said: I mean, unless you just want to serve one client forever, or only wait for those blocking accept calls to serve more than one client. If you believe threads to be the ideal solution for multiple clients you should not be giving out advice. It is not. |
|
Fishcake
Member #8,704
June 2007
|
if(i == listener && num_clients < 2) -------------------------------------------------------- Okay, I know how to send strings, but I have problem sending other stuff like floats and doubles. Following Beej's Guide, I tried to send double through the second method : double d = 3490.15926535; send ( s, &d, sizeof ( d ), 0 ); /* DANGER--non-portable! */
But heck, the second parameter only accept char.
|
|
Edgar Reynaldo
Major Reynaldo
May 2007
|
You're only sending a group of bytes at a time , that's why it only accepts a char* , use something like reinterpret_cast<char*>(&d) for the char* parameter and see if it likes that better. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
|
Synapse Jumps
Member #3,073
December 2002
|
Dustin: I did misunderstand his OP and I already said that I shouldn't have suggested threads. The fact still stands, however, that they are terribly useful and worth learning about with network programming. They are a viable solution to several problems that this kind of coding presents. Fish: Oops! Indeed. Try this one: //change this line if ((newfd = accept(listener, (LPSOCKADDR)&remoteaddr, &addrlen)) == SOCKET_ERROR) //to this: if (num_clients > 1 || (newfd = accept(listener, (LPSOCKADDR)&remoteaddr, &addrlen)) == SOCKET_ERROR)
And ya, for that send() you need some sort of cast. I don't remember all the C++ cast types, but I think you could just use a standard C-cast if you want. |
|
Fishcake
Member #8,704
June 2007
|
//change this line if ((newfd = accept(listener, (LPSOCKADDR)&remoteaddr, &addrlen)) == SOCKET_ERROR) //to this: if (num_clients > 1 || (newfd = accept(listener, (LPSOCKADDR)&remoteaddr, &addrlen)) == SOCKET_ERROR) Maybe it would work, but I found a way to prevent extra clients from connecting. All I did was close the listener socket when client_num == MAX_CLIENTS and open it back when client_num < MAX_CLIENTS. It's working perfectly now. Okay, now I stumble upon another problem. Right now I'm trying to develop a network pong game based on my MiniPong game. When the client presses the UP/DOWN directional button, it will send a message to the server, telling it to increase/decrease the y-velocity of the paddle. The server will then process the data ( adds the y-vel to the y-coor, multiply y-vel with friction, check collision, etc... ). I got this part working. In the next stage, the server will send the y-vel of player 1 ( the client itself ) and also the y-vel of player 2 ( the other client ) back to both the clients ( or should I send the y-coordinates instead? ). Here's my source codes, after reading Beej's Guide ( I'm not sure whether I'm following it correctly /* ----------- Server -------------- */ size_t packetsize; //If client is player 1 if ( j == 0 ) packetsize = pack ( sendBuff, "ff", player_1.dy, player_2.dy ); //If client is player 2 else packetsize = pack ( sendBuff, "ff", player_2.dy, player_1.dy ); //I don't understand what's the use of this packi16 ( sendBuff + 1, packetsize ); //Send it! send ( csockets[j], reinterpret_cast<char*>( &sendBuff ), sizeof ( sendBuff ), 0 );
/* ----------- Client -------------- */ unsigned char recvBuffer[1024]; int serverErr = recv ( s, reinterpret_cast<char*> ( &recvBuffer ), sizeof ( recvBuffer ), 0 ); if ( serverErr == 0 || serverErr == INVALID_SOCKET ) { MessageBox ( hWnd, "Disconnected from server!", "Error", MB_ICONEXCLAMATION | MB_OK | MB_SYSTEMMODAL ); SendMessage ( hwnd, WM_DESTROY, wParam, lParam ); else { unpack ( recvBuffer, "ff", &player_1.dy, &player_2.dy ); }
When I test them, the clients hang! [EDIT] [EDIT2]
|
|
Don Freeman
Member #5,110
October 2004
|
I will answer...even if cookies have already been handed out!::) I have attached a crude, but working example on how to use socket programming and allegro in a console. It is called MyChat, but it is not a chat program. I decided to change it to see how fast the network code was. I have made several improvements since (the power of knowledge::)), but this should help you anyway. I set the gfx mode to show the output, but you could rip the networking code out and just make it a totally console app. The code is FAR from perfect, so don't expect any magical jellybeans or anything.:P This should help you some. Beej's guide and these have really helped: http://www.sockets.com/a_c2.htm I am still learning all of the little "fun" stuff about winsock, but I can help you a bit. Enjoy the code, and the journey...;) -- |
|
|