Alright, I decided to write a winsock tutorial. The code snips and examples I give are for Microsoft Visual C++, not using MFC. I'll probably write an MFC friendly version later, but not now. Most will probably work in Borland too, but I don't know.
Well, there's that. Now down to business...
Section 1. Initializing Winsock
Before you can use Winsock, it must be initialized. Before that, you must include all the neccessary headers and libraries. This is done as follows:
At the top of any module (source file) that needs to use winsock, you must include the winsock.h header:
#include <winsock.h>
Also, you must include the wsock32.lib library at the top of one module. It's best to put this in main.cpp (or whatever your main module is)
#pragma comment(lib, "wsock32.lib")
[Added 5-22-02]An alternative to the pragma is to update the project settings. Press Alt+F7, select your project in the list. Select "All Configurations" from the drop-down list above it. Go to the "Link" tab, and in the text-box for "Object/library modules" you will see a bunch of other .lib files. Just append wsock32.lib to this list.
...Now that you have those, you're ready to start calling functions. But, before you start opening sockets, you have to initialize Winsock still. Put this code in your startup function... ie. WinMain()
WSAData wsa;
WSAStartup(MAKEWORD(1, 1), &wsa);
This tells Winsock to start up and be ready to work for you. If you forget to do this, you'll end up with a bunch of 10093 (WSANOTINITIALISED ) errors.
That's it for initialization. Next I'll show you how to create a socket to listen for connections.
Section 2. Creating A Server Socket
To open a socket for listening, you must do the following things:
Allocate a socket
Bind the socket to a port
Tell the socket to begin listening
Set it to asynchronous mode (non blocking)
Here are the preliminary variable declarations: sin is the socket address struct, and sock is the handle to the listening socket. Note, sock should usually be made globally accessible.
sockaddr_in sin;
SOCKET sock;
First, allocate a socket. Before you can do anything with a socket, you have to call the socket() function to get a socket handle. It returns a SOCKET, or SOCKET_ERROR in the event of an error.
if((sock = socket(PF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR)
return FALSE;
Next, you must bind the newly created socket to a port. This does what it sounds like, it tells the socket which port it can use. If you're making a client, usually you would bind to port 0, so that Winsock would pick a random port. Note that for anything other than port 0, you have to use the htons() function. This converts the number into a network-order short (It's different, trust me).
/* Set the family for the socket to internet */
sin.sin_family = AF_INET;
/* Set to use port 1234 */
sin.sin_port = htons(1234);
if(bind(sock, (sockaddr *)&sin, sizeof(sockaddr_in) == SOCKET_ERROR)
return FALSE;
Then, you have to tell the socket to start listening. This is accomplished with the listen() function. It has several parameters, but the only notable one is the backlog. This is how many pending connections it'll keep -- That is, how many connections it will hold in storage that you have not responded to yet (not called accept on). Usually, just set this to about 100, and you shouldn't have any problems.
if(listen(sock, 200) == SOCKET_ERROR)
return FALSE;
Finally, you will most always want to set the socket to non-blocking mode. This will tell winsock to send your window a message when something happens, instead of just blocking your code (not exiting the function until it succeeds). This is done with WSAAsyncSelect(), using the FD_ACCEPT event. This function is complex, so I will discuss it more than the others.
/* Main.hWnd should contain the HWND of the window you're using for socket stuff */
WSAAsyncSelect(sock, Main.hWnd, WM_USER + 1, FD_ACCEPT);
First off, you can only WSAAsyncSelect() one time for any particular SOCKET. Any further calls will cancel out the ones before. So, to combine multiple events, you use the bitwise or operator | . ie. FD_READ | FD_CLOSE
Secondly, it will send the message you specify to the window you specify, and when that message is received you will also get the SOCKET on which the event occured in wParam , and lParam contains the event occuring in the low word, and the error code in the high word. More about that in Section 7. Recieving data from a socket and Section 9. Accepting Connections . The message is pretty much arbitrary, but needs to be unique.
Now you have a listening socket. Of course, this may not always succeed, and there is minimal error checking in the above code. More about error handling in Section 8. Winsock error handling
Section 3. Creating A Client Socket
Now that you have a listening socket, here's how to make something connect to it. The client socket must have several things done, in order, like the server socket. These things are as follows:
Create the socket
Bind it to a port (but this time, bind it to port 0, so it'll pick a random port)
Attempt connection
Set non-blocking
Here are the preliminary declarations, Note again, that the SOCKET sock should be global.
SOCKET sock;
sockaddr_in LocalSin, RemoteSin;
/* This is the family for the socket */
LocalSin.sin_family = AF_INET;
/* This is the port to connect from. Setting 0 means use random port */
LocalSin.sin_port = 0;
RemoteSin.sin_family = AF_INET;
/* This is the port to connect to */
RemoteSin.sin_port = htons(nPort);
Now, you need to finish the remote address struct with the IP that you're connecting to. However, it doesn't use a string, it uses network-byte order numbers... You convert from a string to network-byte order using the function inet_addr(), as follows:
if((RemoteSin.sin_addr.S_un.S_addr = inet_addr(szIP)) == INADDR_NONE)
return false; // Error setting IP
Creation of the socket is done that same way as in Section 2...
if((sock = socket(PF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR)
return false; // Error creating socket
Now we bind the socket to the local port using the bind() function, much the way we did for the server socket.
if(bind(sock, (sockaddr *)&LocalSin, sizeof(sockaddr_in)) == SOCKET_ERROR)
return false; // Error binding socket
Now the good part, actually telling the socket to connect. As you could assume, this is accomplished with the connect() function.
if(connect(sock, (sockaddr *)&RemoteSin, sizeof(sockaddr_in)) == SOCKET_ERROR)
return false;
Once again, you will want to set Winsock to send you a message when an event occurs. Since the only two events of interest now are when you receive data, and when the socket closes, we use FD_READ | FD_CLOSE. The WM_USER + 2 is pretty much arbitrary, but it's important that it's unique.
/* Main.hWnd should contain the HWND of the window you're using for socket stuff */
WSAAsyncSelect(sock, Main.hWnd, WM_USER + 2, FD_READ | FD_CLOSE);
Section 6. Sending Data
What good do sockets do you if you can't send data over them?
You can send data over a socket using the send() function. Note that it requires the length of data to be sent. If you're sending text, get this with strlen(), but for anything else, use the sizeof operator.
send(sock, "Test String", 12, 0);
-or-
MyStruct a;
send(sock, (char *)&a, sizeof(MyStruct), 0);
Now that you know that, this makes it easier to send lots of strings. If that's all you ever send, this would be sufficient.
SendData(sock, "This is some data!");
...
int SendData(SOCKET sock, char * lpszData)
{
return send(sock, lpszData, strlen(lpszData), 0);
}
Section 7. Receiving Data
What good is sending data if the other side isn't receiving it? This code will receive data from a socket, and make a message box containing the data.
This should be placed in the WinProc code for both server and client:
switch(uMsg)
{
...
case WM_USER + 2:
HandleData(wParam, lParam);
break;
...
int HandleData(WPARAM wParam, LPARAM lParam)
{
SOCKET sock = (SOCKET)wParam;
WORD event = LOWORD(lParam);
WORD error = HIWORD(lParam);
if(event == FD_CLOSE)
{
closesocket(sock);
}
else if(event == FD_READ)
{
char szBuffer[1024];
ZeroMemory(szBuffer, 1024);
recv(sock, szBuffer, 1024, 0);
MessageBox(Main.hWnd, szBuffer, "Received Data!", MB_OK);
closesocket(sock);
}
return TRUE;
}
Now when data is read it is stuck into a message box and the socket closes, and if the port wants to close, the program will acknowledge it.
Section 9. Accepting Connections
Once you have a listening socket, you will need to be able to handle connection requests.
In the code where you created the listening socket, you told it to send a specific message (I used WM_USER + 1) to a specific window (Main.hWnd). How it's time to tell that window how to handle that message. In the WindowProc, include the following code:
switch(uMsg)
{
...
case WM_USER + 1:
HandleAccept(wParam, lParam);
break;
...
}
int HandleAccept(WPARAM wParam, LPARAM lParam)
{
SOCKET sock = (SOCKET)wParam;
WORD event = LOWORD(lParam);
WORD error = HIWORD(lParam);
if(event == FD_ACCEPT)
{
sockaddr_in NewRemoteSin;
SOCKET newsock = accept(sock, (sockaddr*)&NewRemoteSin, NULL);
/* NOTE: Now NewRemoteSin contains the address of the new client.
And newsock contains the socket that has the new connection */
WSAAsyncSelect(newsock, Main.hWnd, WM_USER + 2, FD_READ | FD_CLOSE);
SendData(newsock, "Hello, welcome to my server.");
}
return TRUE;
}
Now that window will accept incoming connections, and send the new client the string "Hello, welcome to my server." and wait for response
That's about it now, the rest of the tutorial is just reference. Best of luck with your newfound Winsock programming knowledge!
Section C. Download Example Source
Download the source code used in the examples here (plus a little extra, to make the program actually work...) It's really simple, but I think a good example of Winsock client/server architecture.
Well, I hope that was helpful for you. If you have any contributions, comments, or corrections -- post them here , or email me at
This email address is being protected from spam bots, you need Javascript enabled to view it
. Thanks, and happy coding!
This page copyright 2000-2006 Jonathan Overholt. You may use it as you wish, to include reproduction and derivative works. You must give me credit in derivative works, though.