Alright, I decided to write a winsock tutorial. The code snips and examples I give are for Microsoft Visual C++, not using MFC.
Here are the sections that I'm going to do (In no real good order)
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")
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 integer. Network-order is big-endian, while the endian-ness of the system may vary. x86-compatible machines are generally little-endian, while most other architectures are big-endian. The 'htons' and 'ntohs' macros handle this conversation as appropriate, so be in the habit of using them. The names are easy, remember: "Host TO Network Short", "Network TO Host Short".
/* 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. We use WM_USER here as a base, which Windows provides for just this sort of purpose. You will probably want to #define your own WM_ macros for this in one of your headers.
#define WM_MYSOCKETGOTAMESSAGE WM_USER+1
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 4: Closing A Socket
Closing a socket is very easy. You use the closesocket() function, like so:
closesocket(MySock);
Now that wasn't that bad... Was it?
Section 5: Shutting Down Winsock
At the end of any program, you always need to clean up after yourself. When using Winsock, you do this by calling the WSACleanup() function. Put this function call in your cleanup function, where it will only be called once.
WSACleanup();
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 8: Error Handling
Briefly, Error handling is simple. Usually, when an error occurs, the function will return SOCKET_ERROR, and set the error code. This error code is retrieved using the WSAGetLastError() function.
int Error = WSAGetLastError();
if (Error == WSANOTINITIALISED)
...
MSDN online provides a list of error codes, but
here is a list from my copy of MSDN.
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 A: Reference
No better place to find out about each function than MSDN. If you don't subscribe (It is quite expensive), It's available online! Check it out:
http://msdn.microsoft.com/library/default.asp. For more information on the underlying protocols you might check out Wikipedia articles on
TCP/IP Model,
Winsock, and
Berkeley Sockets. Also, for a
quick Winsock error reference, here is my copy of the error section from MSDN.
Section B: 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.
Section C: More Examples
Here I'll give some quick-and-dirty pseudo code for various common scenarios you'll probably encounter in your Winsock programming endeavours.
Chat Server
A chat server is a common project for new programmers to attempt. It's something you can show your friends and have fun with. A chat server will typically accept and maintain a TCP connection from each client, relaying packets from one to one or more others.
Game Server
A game server is a bit more work than a chat server, but builds on the same ideas. A game server will typically relay much more data, probably perform a few calculations of its own, and generate a fair bit more data of its own. Game servers often use UDP ports for the bulk of their data, while sometimes maintaining a TCP connection as well.
Peer-to-peer (P2P) "servent"
Many P2P programs, referred to as peers or "servents" because they perform the roles of both server and client, will maintain many connections to other instances of themselves, building a mesh or fabric through which messages will be relayed. These protocols have a number of complex issues to address, such as routing loops and firewall traversal. Most of them also have some measures to allow for throttling, while most are designed to saturate the link whenever possible. With the proliferation of P2P file transfer in recent years concern has been expressed that these protocols may be detrimental to overall network health, especially if one is malformed.
It's certainly possible that you can write the next big P2P protocol, the BitTorrent killer, but it certainly won't be an easy task. There are probably a lot of really cool algorithms you could implement to get more throughput and achieve really good load balancing and link utilization.
It's also worth noting the legal complications and the negative perception inherent with P2P protocols now. Many legislative bodies and individuals alike believe that P2P is to blame for piracy, that it can serve no legal purpose, that they should be illegalized for the role they play in copyright infringement. These aren't technical problems, and you may feel that they shouldn't matter to you, but if you're trying to sell a product utilizing P2P transport it may invoke negative emotions in your potential customer, so just be aware of it.
Section D: TCP or UDP?
This tutorial has thusfar assumed a certain level of understanding of the TCP/IP protocol stack, but I would be remiss if I didn't explain the differences between TCP and UDP sockets.
The TCP/IP stack can be visualized as a hierarchical tree, with IP as the root. IP provides addressing and routing services for data, but does not guarantee data integrity, delivery, or delivery sequence. IP is also connectionless. Every node on a TCP/IP network has an IP address, which is 32 bits for IPv4 (the standard for the last 20 years) and 128 bits for IPv6 (the forthcoming standard).
Built on top of the IP layer there are several other protocols, but the two most common are TCP and UDP. UDP is a very thin protocol, providing only data integrity. UDP calculates a checksum and sends it along with the data. UDP is still connectionless, and does not guarantee delivery or sequencing. You can send a UDP packet to any address, but you won't know if it arrived or when. Even across direct LAN connections you can see UDP packets arrive out of order or fail to arrive all together. Despite this unreliability, UDP is still very useful because of its lightweight nature. It is commonly used for bulk data transport, such as 3D games and Voice Over Internet Protocol (VOIP).
TCP is a connection-oriented protocol which guarantees data integrity, delivery, and sequencing. Instead of blindly sending packets, with a TCP socket you must first establish a connection to another socket. After the connection is established you can send packets and be certain that they come out of the pipe exactly as you put them in. These wonderful features come at a cost, of course, but TCP is the basis for many internet protocols you're familiar with already (like HTTP, SMTP, POP3, and SSH). Because it is connection-oriented you will also see TCP referred to as "stateful", because there are different states in which a socket can be.
As an application developer you need to be aware of the differences between TCP and UDP, as well as when to use each. Some applications may call for both. Whatever the situation, you will end up building your own protocol on top of it, and whatever you choose to base it on will dictate how much work you have to make your protocol do. You might even decide to use "raw sockets", which are a direct interface to the IP layer. Using raw sockets allows you to forge the IP headers, allowing you to conduct spoofing attacks. For this reason most operating systems restrict use of raw sockets to administrative users (root, in Unix).
There are a number of other protocols at this level as well, but they tend to be more special purpose. Internet Control Message Protocol (ICMP) for instance, which is the protocol used by the common "ping" and "traceroute" commands.
Conclusion
I hope this was helpful for you. If you have any contributions, comments, or corrections please email me at
jonathan@overholt.org. Thanks, and happy coding!