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...
Here's my current source codes of the client and the server (no asynchronous socket yet) :
CLIENT
| 1 | #include <winsock2.h> |
| 2 | #include <stdio.h> |
| 3 | #include <stdlib.h> |
| 4 | |
| 5 | #define WM_SOCKET WM_USER+1 |
| 6 | |
| 7 | |
| 8 | |
| 9 | int main() |
| 10 | { |
| 11 | |
| 12 | WSADATA w; |
| 13 | int error = WSAStartup (0x0202,&w); |
| 14 | if (error) |
| 15 | { |
| 16 | printf("Error: You need WinSock 2.2!\n"); |
| 17 | return 0; |
| 18 | } |
| 19 | if (w.wVersion!=0x0202) |
| 20 | { |
| 21 | printf("Error: Wrong WinSock version!\n"); |
| 22 | WSACleanup (); |
| 23 | return 0; |
| 24 | } |
| 25 | |
| 26 | printf("WinSock Version 2.2 Initialized\n"); |
| 27 | |
| 28 | SOCKET s = socket(AF_INET,SOCK_STREAM,0); |
| 29 | sockaddr_in target; |
| 30 | |
| 31 | target.sin_family = AF_INET; |
| 32 | target.sin_port = htons (5555); |
| 33 | target.sin_addr.s_addr = inet_addr ("127.0.0.1"); |
| 34 | |
| 35 | printf("Connecting...\n"); |
| 36 | |
| 37 | if (connect(s, (LPSOCKADDR)&target, sizeof(target)) == SOCKET_ERROR) |
| 38 | { |
| 39 | printf("Error in connection!\n"); |
| 40 | WSACleanup (); |
| 41 | return 0; |
| 42 | } |
| 43 | closesocket (s); |
| 44 | printf("Connected!\n"); |
| 45 | |
| 46 | while(true){ |
| 47 | |
| 48 | } |
| 49 | |
| 50 | } |
SERVER
| 1 | #include <winsock2.h> |
| 2 | #include <stdio.h> |
| 3 | #include <stdlib.h> |
| 4 | |
| 5 | #define MAX_CLIENTS 5 |
| 6 | |
| 7 | int main() |
| 8 | { |
| 9 | |
| 10 | WSADATA w; |
| 11 | int error = WSAStartup (0x0202,&w); |
| 12 | if (error) |
| 13 | { |
| 14 | printf("Error: You need WinSock 2.2!\n"); |
| 15 | return 0; |
| 16 | } |
| 17 | if (w.wVersion!=0x0202) |
| 18 | { |
| 19 | printf("Error: Wrong WinSock version!\n"); |
| 20 | WSACleanup (); |
| 21 | return 0; |
| 22 | } |
| 23 | |
| 24 | printf("WinSock Version 2.2 Initialized\n"); |
| 25 | |
| 26 | SOCKET s = socket(AF_INET,SOCK_STREAM,0); |
| 27 | sockaddr_in addr; |
| 28 | |
| 29 | addr.sin_family = AF_INET; |
| 30 | addr.sin_port = htons (5555); |
| 31 | addr.sin_addr.s_addr = htonl (INADDR_ANY); |
| 32 | |
| 33 | if (bind(s,(LPSOCKADDR)&addr,sizeof(addr))==SOCKET_ERROR) |
| 34 | { |
| 35 | printf("Error: Unable to bind socket!\n"); |
| 36 | WSACleanup (); |
| 37 | return 0; |
| 38 | } |
| 39 | printf("Socket binded\n"); |
| 40 | |
| 41 | if (listen(s,MAX_CLIENTS)==SOCKET_ERROR) |
| 42 | { |
| 43 | printf("Error: Unable to listen!\n"); |
| 44 | WSACleanup (); |
| 45 | return 0; |
| 46 | } |
| 47 | printf("Listening for connections...\n"); |
| 48 | |
| 49 | int number_of_clients = 0; |
| 50 | SOCKET client[MAX_CLIENTS]; |
| 51 | sockaddr client_sock[MAX_CLIENTS]; |
| 52 | int addr_size = sizeof(sockaddr); |
| 53 | char buffer[255]; |
| 54 | |
| 55 | while (number_of_clients < MAX_CLIENTS) |
| 56 | { |
| 57 | client[number_of_clients] = accept (s, &client_sock[number_of_clients], &addr_size); |
| 58 | |
| 59 | if (client[number_of_clients] == INVALID_SOCKET) |
| 60 | { |
| 61 | printf("Error: Unable to accept connection!\n"); |
| 62 | WSACleanup (); |
| 63 | return 0; |
| 64 | } |
| 65 | |
| 66 | else |
| 67 | { |
| 68 | number_of_clients++; |
| 69 | printf("Connection accepted!\n"); |
| 70 | } |
| 71 | } |
| 72 | |
| 73 | printf("Enough Clients!\n"); |
| 74 | closesocket (s); |
| 75 | |
| 76 | while(true){ |
| 77 | } |
| 78 | |
| 79 | } |
It seems like you're quite new to this; MSDN is your friend:
MSDN for WSAAsyncSelect
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.
Thanks for the links! Looks like I have to learn Windows Programming first
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!
Could it get any simplier?
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
| 1 | #include <windows.h> |
| 2 | #include <winsock2.h> |
| 3 | #include <stdio.h> |
| 4 | #include <stdlib.h> |
| 5 | |
| 6 | #define WM_ONSOCKET WM_USER+1 |
| 7 | |
| 8 | const char g_szClassName[] = "myWindowClass"; |
| 9 | |
| 10 | LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) |
| 11 | { |
| 12 | switch(msg) |
| 13 | { |
| 14 | |
| 15 | case WM_ONSOCKET: |
| 16 | { |
| 17 | if (WSAGETSELECTERROR(lParam)) |
| 18 | { |
| 19 | WSACleanup (); |
| 20 | return 0; |
| 21 | } |
| 22 | switch (WSAGETSELECTEVENT(lParam)) |
| 23 | { |
| 24 | case FD_CONNECT : { |
| 25 | MessageBox(NULL, "Connected!", "Yay!", MB_OK); |
| 26 | } break; |
| 27 | } |
| 28 | } break; |
| 29 | |
| 30 | case WM_CLOSE: |
| 31 | DestroyWindow(hwnd); |
| 32 | break; |
| 33 | case WM_DESTROY: |
| 34 | PostQuitMessage(0); |
| 35 | break; |
| 36 | default: |
| 37 | return DefWindowProc(hwnd, msg, wParam, lParam); |
| 38 | } |
| 39 | return 0; |
| 40 | } |
| 41 | |
| 42 | int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, |
| 43 | LPSTR lpCmdLine, int nCmdShow) |
| 44 | { |
| 45 | WNDCLASSEX wc; |
| 46 | HWND hwnd; |
| 47 | MSG Msg; |
| 48 | |
| 49 | wc.cbSize = sizeof(WNDCLASSEX); |
| 50 | wc.style = 0; |
| 51 | wc.lpfnWndProc = WndProc; |
| 52 | wc.cbClsExtra = 0; |
| 53 | wc.cbWndExtra = 0; |
| 54 | wc.hInstance = hInstance; |
| 55 | wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); |
| 56 | wc.hCursor = LoadCursor(NULL, IDC_ARROW); |
| 57 | wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); |
| 58 | wc.lpszMenuName = NULL; |
| 59 | wc.lpszClassName = g_szClassName; |
| 60 | wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); |
| 61 | |
| 62 | if(!RegisterClassEx(&wc)) |
| 63 | { |
| 64 | MessageBox(NULL, "Window Registration Failed!", "Error!", |
| 65 | MB_ICONEXCLAMATION | MB_OK); |
| 66 | return 0; |
| 67 | } |
| 68 | |
| 69 | hwnd = CreateWindowEx( |
| 70 | WS_EX_CLIENTEDGE, |
| 71 | g_szClassName, |
| 72 | "The title of my window", |
| 73 | WS_OVERLAPPEDWINDOW, |
| 74 | CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, |
| 75 | NULL, NULL, hInstance, NULL); |
| 76 | |
| 77 | if(hwnd == NULL) |
| 78 | { |
| 79 | MessageBox(NULL, "Window Creation Failed!", "Error!", |
| 80 | MB_ICONEXCLAMATION | MB_OK); |
| 81 | return 0; |
| 82 | } |
| 83 | |
| 84 | ShowWindow(hwnd, nCmdShow); |
| 85 | UpdateWindow(hwnd); |
| 86 | |
| 87 | WSADATA w; |
| 88 | |
| 89 | int error = WSAStartup (0x0202, &w); |
| 90 | |
| 91 | if (error) |
| 92 | { |
| 93 | MessageBox(NULL, "Failed to initialize WinSock!", "Error!", |
| 94 | MB_ICONEXCLAMATION | MB_OK); |
| 95 | return 0; |
| 96 | } |
| 97 | if (w.wVersion != 0x0202) |
| 98 | { |
| 99 | MessageBox(NULL, "Wrong version of WinSock!", "Error!", |
| 100 | MB_ICONEXCLAMATION | MB_OK); |
| 101 | WSACleanup (); |
| 102 | return 0; |
| 103 | } |
| 104 | |
| 105 | SOCKET s = socket (AF_INET, SOCK_STREAM, 0); |
| 106 | sockaddr_in target; |
| 107 | |
| 108 | target.sin_family = AF_INET; |
| 109 | target.sin_port = htons (5001); |
| 110 | target.sin_addr.s_addr = inet_addr ("127.0.0.1"); |
| 111 | |
| 112 | WSAAsyncSelect (s, hwnd, WM_ONSOCKET, (FD_READ | FD_CONNECT | FD_CLOSE)); |
| 113 | |
| 114 | if (connect (s, (LPSOCKADDR)&target, sizeof(target)) == SOCKET_ERROR) |
| 115 | { |
| 116 | MessageBox(NULL, "Unable to connect!", "Error!", |
| 117 | MB_ICONEXCLAMATION | MB_OK); |
| 118 | WSACleanup (); |
| 119 | return 0; |
| 120 | } |
| 121 | |
| 122 | while(GetMessage(&Msg, NULL, 0, 0) > 0) |
| 123 | { |
| 124 | TranslateMessage(&Msg); |
| 125 | DispatchMessage(&Msg); |
| 126 | } |
| 127 | |
| 128 | closesocket (s); |
| 129 | WSACleanup (); |
| 130 | |
| 131 | return Msg.wParam; |
| 132 | } |
SERVER
| 1 | #include <stdio.h> |
| 2 | #include <stdlib.h> |
| 3 | #include <winsock2.h> |
| 4 | |
| 5 | #define MAX_CLIENTS 3 |
| 6 | const int socket_port = 5001; |
| 7 | int client_num = 0; |
| 8 | int addr_size = sizeof (sockaddr); |
| 9 | |
| 10 | int main () |
| 11 | { |
| 12 | WSADATA w; |
| 13 | int error = WSAStartup (0x0202, &w); |
| 14 | if (error) |
| 15 | { |
| 16 | printf ("Unable to initialize WinSock.\n"); |
| 17 | return 0; |
| 18 | } |
| 19 | if (w.wVersion != 0x0202) |
| 20 | { |
| 21 | printf ("Wrong version of WinSock.\n"); |
| 22 | WSACleanup (); |
| 23 | return 0; |
| 24 | } |
| 25 | printf ("WinSock 2 initialized.\n"); |
| 26 | |
| 27 | SOCKET s = socket (AF_INET, SOCK_STREAM, 0); |
| 28 | sockaddr_in addr; |
| 29 | addr.sin_family = AF_INET; |
| 30 | addr.sin_port = htons (socket_port); |
| 31 | addr.sin_addr.s_addr = htonl (INADDR_ANY); |
| 32 | if (bind(s, (LPSOCKADDR)&addr, sizeof(addr)) == SOCKET_ERROR) |
| 33 | { |
| 34 | printf ("Cannot bind to port %d.\n", socket_port); |
| 35 | WSACleanup (); |
| 36 | return 0; |
| 37 | } |
| 38 | printf ("Socket binded to port %d.\n", socket_port); |
| 39 | |
| 40 | if (listen (s,MAX_CLIENTS) == SOCKET_ERROR) |
| 41 | { |
| 42 | printf ("Unable to listen to port %d.\n", socket_port); |
| 43 | WSACleanup (); |
| 44 | return 0; |
| 45 | } |
| 46 | printf ("Listening for connections at port %d....\n", socket_port); |
| 47 | |
| 48 | SOCKET client[MAX_CLIENTS]; |
| 49 | sockaddr client_sock[MAX_CLIENTS]; |
| 50 | |
| 51 | while (true){ |
| 52 | |
| 53 | while (client_num<MAX_CLIENTS) |
| 54 | { |
| 55 | client[client_num] = accept (s, &client_sock[client_num], &addr_size); |
| 56 | if (client[client_num] == INVALID_SOCKET) |
| 57 | { |
| 58 | printf ("Unable to accept connection.\n"); |
| 59 | WSACleanup (); |
| 60 | return 0; |
| 61 | } |
| 62 | else |
| 63 | { |
| 64 | printf ("A client has connected to the server.\n"); |
| 65 | client_num++; |
| 66 | } |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | closesocket (s); |
| 71 | WSACleanup (); |
| 72 | |
| 73 | } |
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.
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:
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.
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));
Well... pretty much everything in Windows is complicated.
No its not.
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?
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?
Complicated? There is only 1 rule for threads. "lock shared memory" its not rocket surgery.
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!
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?
Complicated? There is only 1 rule for threads. "lock shared memory" its not rocket surgery.
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
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
)
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:
| 1 | #include <winsock2.h> |
| 2 | #include <stdio.h> |
| 3 | #include <stdlib.h> |
| 4 | #include <fcntl.h> |
| 5 | |
| 6 | int main() |
| 7 | { |
| 8 | |
| 9 | WSADATA w; |
| 10 | int error = WSAStartup (0x0202,&w); |
| 11 | if (error) |
| 12 | { |
| 13 | printf("Error: You need WinSock 2.2!\n"); |
| 14 | return 0; |
| 15 | } |
| 16 | if (w.wVersion!=0x0202) |
| 17 | { |
| 18 | printf("Error: Wrong WinSock version!\n"); |
| 19 | WSACleanup (); |
| 20 | return 0; |
| 21 | } |
| 22 | |
| 23 | printf("WinSock Version 2.2 Initialized\n"); |
| 24 | |
| 25 | SOCKET s = socket(AF_INET,SOCK_STREAM,0); |
| 26 | fcntl(s, F_SETFL, O_NONBLOCK); |
| 27 | sockaddr_in target; |
| 28 | |
| 29 | target.sin_family = AF_INET; |
| 30 | target.sin_port = htons (5555); |
| 31 | target.sin_addr.s_addr = inet_addr ("127.0.0.1"); |
| 32 | |
| 33 | printf("Connecting...\n"); |
| 34 | |
| 35 | if (connect(s, (LPSOCKADDR)&target, sizeof(target)) == SOCKET_ERROR) |
| 36 | { |
| 37 | printf("Error in connection!\n"); |
| 38 | WSACleanup (); |
| 39 | return 0; |
| 40 | } |
| 41 | printf("Connected!\n"); |
| 42 | |
| 43 | fd_set readfds, writefds; |
| 44 | FD_ZERO(&readfds); FD_ZERO(&writefds); |
| 45 | FD_SET(s, &readfds); FD_SET(s, &writefds); //add our socket to the two sets sets we'll send to select |
| 46 | struct timeval tv; |
| 47 | tv.tv_sec = 0; tv.tv_usec = 50; |
| 48 | |
| 49 | while(true){ |
| 50 | select((int)s+1, &readfds, &writefds, NULL, &tv); //check if the socket can be read/written |
| 51 | |
| 52 | if (FD_ISSET(s, &readfds)) //the socket is ready to recieve |
| 53 | recv(stuff); |
| 54 | else if (FD_ISSET(s, &writefds)) //the socket is ready to write stuff.... but it should always be... |
| 55 | send(stuff); |
| 56 | } |
| 57 | |
| 58 | } |
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
. Oh wait, even the function itself is not defined.
When I commented the line out, the main loop ran but recv() was not called. And then I tried changing the last parameter of select() to NULL. recv() still blocks.
------------------------------------------------------------------------------------
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 :
| 1 | #include <winsock2.h> |
| 2 | #include <stdio.h> |
| 3 | |
| 4 | #define PORT 8000 |
| 5 | #define MAX_CLIENTS 2 |
| 6 | int client_num = 0; |
| 7 | WSADATA wsadata; |
| 8 | fd_set master; |
| 9 | fd_set read_fds; |
| 10 | sockaddr_in myaddr; |
| 11 | sockaddr_in remoteaddr; |
| 12 | int fdmax; |
| 13 | SOCKET listener; |
| 14 | int newfd; |
| 15 | char buf[256]; |
| 16 | int nbytes; |
| 17 | int yes = 1; |
| 18 | int addrlen = sizeof(remoteaddr); |
| 19 | int i; |
| 20 | int j; |
| 21 | |
| 22 | int main() |
| 23 | { |
| 24 | |
| 25 | int error = WSAStartup (0x0202, &wsadata); |
| 26 | |
| 27 | if (error) |
| 28 | { |
| 29 | printf("Unable to initialize WinSock.\n"); |
| 30 | return 0; |
| 31 | } |
| 32 | if (wsadata.wVersion != 0x0202) |
| 33 | { |
| 34 | printf("Wrong version of WinSock.\n"); |
| 35 | WSACleanup (); |
| 36 | return 0; |
| 37 | } |
| 38 | |
| 39 | printf("WinSock initialized.\n"); |
| 40 | |
| 41 | FD_ZERO(&master); |
| 42 | FD_ZERO(&read_fds); |
| 43 | |
| 44 | if ((listener = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR) { |
| 45 | printf("Unable to create socket.\n"); |
| 46 | WSACleanup (); |
| 47 | return 0; |
| 48 | } |
| 49 | |
| 50 | printf("Listener socket created.\n"); |
| 51 | |
| 52 | port_input: |
| 53 | int myport; |
| 54 | printf( "Enter a port number : " ); |
| 55 | scanf( "%d", &myport ); |
| 56 | if( myport <= 0) |
| 57 | { |
| 58 | printf( "Invalid port number.\n" ); |
| 59 | goto port_input; |
| 60 | } |
| 61 | |
| 62 | myaddr.sin_family = AF_INET; |
| 63 | myaddr.sin_port = htons(myport); |
| 64 | myaddr.sin_addr.s_addr = htonl(INADDR_ANY); |
| 65 | memset(myaddr.sin_zero, '\0', sizeof myaddr.sin_zero); |
| 66 | |
| 67 | if (bind(listener, (LPSOCKADDR)&myaddr, sizeof(myaddr)) == SOCKET_ERROR) { |
| 68 | printf("Unable to bind to port %d.\n", myport); |
| 69 | WSACleanup (); |
| 70 | return 0; |
| 71 | } |
| 72 | |
| 73 | printf("Listener socket binded to port %d.\n", myport); |
| 74 | |
| 75 | if (listen(listener, MAX_CLIENTS) == -1) { |
| 76 | printf("Unable to listen.\n"); |
| 77 | WSACleanup (); |
| 78 | return 0; |
| 79 | } |
| 80 | |
| 81 | printf("Listening for connections....\n"); |
| 82 | |
| 83 | FD_SET(listener, &master); |
| 84 | fdmax = listener; |
| 85 | |
| 86 | while(true) |
| 87 | { |
| 88 | read_fds = master; |
| 89 | if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) { |
| 90 | return 0; |
| 91 | } |
| 92 | |
| 93 | /* for loop starts here */ |
| 94 | for(i=0; i <= fdmax; i++) { |
| 95 | if(FD_ISSET(i, &read_fds)) |
| 96 | { |
| 97 | if(i == listener) |
| 98 | { |
| 99 | if ((newfd = accept(listener, (LPSOCKADDR)&remoteaddr, &addrlen)) == SOCKET_ERROR) |
| 100 | { |
| 101 | printf("Unable to accept connection.\n"); |
| 102 | return 0; |
| 103 | } else { |
| 104 | FD_SET(newfd, &master); |
| 105 | if (newfd > fdmax) { |
| 106 | fdmax = newfd; |
| 107 | } |
| 108 | printf("Accepted a new connection from %s on " \ |
| 109 | "socket %d\n", \ |
| 110 | inet_ntoa(remoteaddr.sin_addr), newfd); |
| 111 | client_num++; |
| 112 | printf("Number of clients : %d\n", client_num); |
| 113 | } |
| 114 | } else { |
| 115 | nbytes = recv(i, buf, sizeof(buf), 0); |
| 116 | if(nbytes == 0 || nbytes == INVALID_SOCKET) |
| 117 | { |
| 118 | printf("Socket %d hung up\n", i); |
| 119 | closesocket(i); |
| 120 | FD_CLR(i, &master); |
| 121 | client_num--; |
| 122 | printf("Number of clients : %d\n", client_num); |
| 123 | } |
| 124 | } |
| 125 | } |
| 126 | } |
| 127 | /* for loop ends here */ |
| 128 | |
| 129 | } |
| 130 | |
| 131 | closesocket (listener); |
| 132 | WSACleanup(); |
| 133 | return 0; |
| 134 | |
| 135 | } |
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:
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)
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.
if(i == listener && num_clients < 2)
It didn't work. The third client was unable to connect but it didn't show "Unable to connect" error. Plus, when the third client tried to connect, the num_clients was reduced by 1. So I ended up with num_clients = 1 eventhough there were two clients connected. And the server was not accepting any connections after that, even when a connected client disconnected. I hope you understand what I'm trying to say. 
--------------------------------------------------------
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.
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.
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.
send ( s, (char *)&d, sizeof ( d ), 0 );
//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!
I need to resolve this problem before moving on to the ball. ( a pong is no pong without the ball
)
[EDIT]
Oh wait, I think send() is blocking. Dang, I hate this...:'( I'll try and fix it.
[EDIT2]
Fixed it. But the game is running darn slow. I think I should use UDP.
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
http://www.sockets.com/winsock.htm
http://tangentsoft.net/wskfaq/examples/basics/ though tangetsoft.net seems to be down right now...maintenances?
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...;)