Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Programming Windows TCP Sockets in C++ for the Beginner

0.00/5 (No votes)
2 Apr 2007 7  
Helps programmers new to Winsock start programming TCP sockets in C++

Introduction

There really is not a lot of material on this subject (I believe) that explains the use of Windows sockets sufficiently enough for a beginner to understand and begin to program them. I still remember the hassle that I went through trying to find a proper tutorial that didn't leave me hanging with many questions after I started programming with them myself.

That was a long time ago now, and it was quite a challenge for me to program my first application that could communicate with other computers over the Internet � even though my first introduction to sockets was through Visual Basic; a high-level and very user-friendly programming language. Now that I have long since switched to the more powerful C++, I rapidly found that the labor I had expended to code sockets in VB was nothing compared to what awaited!

Thankfully, after many hours searching various web pages on the Internet, I was able to collect all the bits and pieces, and finally compile my first telnet program in C++. My goal is to collect all the necessary data in one place; right here, so the reader doesn't have to recollect all the data over the Internet. Thus, I present this tutorial in hopes that it alone will be sufficient information to begin programming.

Before we begin, you will need to include winsock.h and link libws2_32.a to your project in order to use the API that are necessary for TCP/IP. If this is not possible, use LoadLibrary() to load ws2_32.dll at runtime, or some similar method.

All the code in this article was written and tested using "Bloodshed Dev-C++ 4.9.8.0"; but generally, it should work with any compiler with minimal modifications.

What the Heck are Threads, Ports, and Sockets?

Actually, we can use the word-picture presented to us by the name "socket" in a similar fashion to illustrate what they are and how they work. In an actual mechanical socket, you may recall that it is the female, or "receiving" end of a connection. A "thread" is a symbolic name for a connection between your computer and a remote computer, and a thread is connected to a socket.

In case I've lost you with all that proper terminology, you might think of a thread as an actual, physical, sewing-type thread stretched from one computer to the other, as the common analogy goes. In order for the threads to be attached to each computer, however, there must be a receiving object that attaches to the threads, and these are called sockets.

A socket can be opened on any "port"; which is simply a unique number to distinguish it from other threads, because more than just one connection can be made on the same computer. A few of these ports have been set aside to serve a specific purpose. Beyond these ports, there are quite a large number of other ports that can be used for anything and everything: over 6,000, actually. A few commonly used ports are listed below with their corresponding services:

Port Service
7 Ping
13 Time
15 Netstat
22 SSH
23 Telnet (default)
25 SMTP (Send mail)
43 Whois (Query information)
79 Finger (Query server information)
80 HTTP (Web pages)
110 POP (Receive mail)
119 NNTP
513 CLOGIN (Used for IP spoofing)

There are many more ports used for specific purposes that are not shown here. Typically though, if you wish to use a port that has no specific assigned service, any port from 1,000 to 6,535 should be just fine. Of course, if instead you want to listen in on messages sent to and from service ports, you can do that too.

Are you connected to the Internet now? Let's say you are, and you have Internet Explorer or some other web page service running, as well as AOL or some other chat program. On top of that (as if the connection wasn't slow enough already) you're trying to send and receive email. What ports do you think are opened, sending and receiving data?

  • Internet Explorer (etc.) sends and receives data via port 80
  • AOL and other instant messaging programs usually like to hang out in the higher unassigned ports up in the thousands to be safe from interference. Each chat program varies, as there is no specific "chat" service and multiple messaging programs may run at the same time
  • When you're sending your email, you and the remote mail server are communicating using port 25
  • And, when you receive email, your mail client (such as Microsoft Outlook) uses port 110 to retrieve your mail from the mail server

And onward extends the list.

It's not enough just to know what port number we're using, obviously; we need to know what remote computer/server we're connecting to. Just like we find out the home address of the people we visit before we get in the car, we have to know the "IP address" of the host we are connecting to, if we are connecting and not just listening (a chat program needs to be able to do both).

An IP address is an identification number that is assigned to each computer on the network, and consists of four sets of digits separated by periods. You can view your IP address by running ipconfig.exe at the MSDOS prompt.

For the examples shown throughout this tutorial, we will be using what is called the "loop-back address" to test our chat program without being connected to the Internet. This address is 127.0.0.1. Whenever you try to make a connection to this IP, the computer loops the request back to you computer and attempts to locate a server on the specified port. That way, you can have the server and client running on the same computer. Once you decide to connect to other remote computers, and you've worked the bugs out of your chat program, you will need to get the unique IP address of each to communicate with them over the Internet.

Because we as humans are very capable of forgetting things, and because we couldn't possibly hope to remember a bunch of numbers for every web site we visit, some smart individuals came up the wonderful idea of "domain names". Now, we have neat little names like www.yahoo.com and www.cia.gov that stand for IP addresses that are much easier to remember than clunky sets of digits. When you type one of these names in your browser window, the IP address for that domain name is looked up via a "router", and once it is obtained (or the host is "resolved"), the browser can contact the server residing at that address.

For example, let's say I call an operator because I can't remember my girlfriend's phone number (fat chance). So, I just tell the operator what her name is (and a few other details, but that's not important) and she happily gives me the digits. That's kind of what happens when a request is made for an IP address of any domain name.

We have two API that accomplish this task. It's a good idea to make sure and check to see if whoever uses you program types a domain name instead of an IP address, so your program can look up the correct IP address before continuing. Most people, anyway, won't want to remember any IP addresses, so most likely you'll need to translate domain names into IP addresses before you can establish a connection � which requires that the computer must be connected to the Internet. Then, once you have the address, you're all set to connect.

//Return the IP address of a domain name


DECLARE_STDCALL_P(struct hostent *) gethostbyname(const char*);

//Convert a string address (i.e., "127.0.0.1") to an IP address. Note that  

//this function returns the address into the correct byte order for us so 

//that we do not need to do any conversions (see next section)


unsigned long PASCAL inet_addr(const char*);

Byte Order

Just when you thought all this thread-socket stuff was going to be simple and easy, we have to start discussing byte order. This is because Intel computers and network protocols use reversed byte ordering from each other, and we have to covert each port and IP address to network byte order before we send it; else we'll have a big mix up. Port 25, when not reversed, will not end up being port 25 at all. So, we have to make sure we're speaking the same language as the server when we attempt to communicate with it.

Thankfully, we don't have to code all the conversion functions manually; as Microsoft kindly provides us with a few API to do this as well. The four functions that are used to change the byte order of an IP or port number are as follows:

u_long PASCAL htonl(u_long); //Host to network long

u_long PASCAL ntohl(u_long); //Network to host long


u_short PASCAL htons(u_short); //Host to network short

u_short PASCAL ntohs(u_short); //Network to host short

Remember! The "host" computer is the computer that listens for and invites connections to it, and the "network" computer is the visitor that connects to the host.

So, for example, before we specify which port we are going to listen on or connect to, we'll have to use the htons() function to convert the number to network byte order. Note that after using inet_addr() to convert a string IP address to the required form, we will be returned the address in the correct network order, eliminating the need to evoke htonl(). An easy way to differentiate between htons() and htonl() is to think of the port number as the shorter number, and the IP as the longer number (which is true � an IP address consists of four sets of up to three digits separated by periods, versus a single port number).

Firing Up Winsock

OK, now that we've finally covered the basics, hopefully you are starting to see light at the end of the tunnel and we can move on. Don't worry if you don't understand every aspect of the procedure, for many supplementary facts will be brought to light as we progress.

The first step to programming with windows sockets (A.K.A "Winsock") is starting up the Winsock API. There are two versions of Winsock; version one is the older, limited version; and version 2 is the latest edition and is therefore the version we prefer to specify.

#define SCK_VERSION1            0x0101
#define SCK_VERSION2            0x0202

int PASCAL WSAStartup(WORD,LPWSADATA);
int PASCAL WSACleanup(void);

//This typedef will be filled out when the function returns

//with information about the Winsock version


typedef struct WSAData
{
    WORD      wVersion;
    WORD      wHighVersion;
    char      szDescription[WSADESCRIPTION_LEN+1];
    char      szSystemStatus[WSASYS_STATUS_LEN+1];
    unsigned short      iMaxSockets;
    unsigned short      iMaxUdpDg;
    char *       lpVendorInfo;
} 
WSADATA;

typedef WSADATA *LPWSADATA;

You should only need to call these functions once each, the former when you initialize Winsock, and the latter when you are finished. Don't close down Winsock until you are finished, though, as doing so would cancel any connections that your program has initiated or any ports that you are listening on.

Initializing a Socket

We understand how sockets work now, hopefully, but up until now we had no idea how to initialize them. The correct parameters must be filled out and passed to a handy API call that begins the socket (hopefully). In this case, we are returned the handle to the socket that we have created. This handle is very "handy" and we must keep it on hand to manipulate the socket's activity.

When you are all finished doing your dirty work, it is considered proper programming practice to shut down any sockets that you have opened before your program exits. Of course, when it does, all the ties and connections it has will be forcibly shut down, including any sockets, but it's better to shut them down the graceful way with closesocket(). You will need to pass the socket's handle to this API when you call it.

//There are many more options than the ones defined here, to see them

//browse the winsock2.h header file


#define SOCK_STREAM      1
#define SOCK_DGRAM      2
#define SOCK_RAW      3

#define AF_INET      2 

#define IPPROTO_TCP      6

SOCKET PASCAL socket(int,int,int);
int PASCAL closesocket(SOCKET);

When creating a socket, you will need to pass the "address family", socket "type", and the "protocol type". Unless you're doing some special (or odd) work, which is beyond the scope of this report, you should typically just pass AF_INET as the default address family. This parameter specifies how the computer addresses will be interpreted.

There is more than just one type of socket; actually, there are many more. Three of the most common ones include: Raw Sockets, Stream Sockets, and Datagram Sockets. Stream sockets, however, are what we are using in this tutorial, since we are dealing with TCP protocols, so we will specify SOCK_STREAM as the second parameter to socket().

We're close, so close! We've got the "nitty gritty" stuff done and over with, so let's move on the more exiting parts of Winsock programming.

Connecting to a Remote Host (Acting as the Client)

Let's try out what we've gone over with a simple program that can connect to a remote computer. Doing this will help you to understand much better how everything works, and helps to prevent information overload!

You'll need to fill out information about the remote host that you are connecting to, and then pass a pointer to this structure to the magic function, connect(). This structure and the API are listed below. Note that the sin_zero parameter is unneeded and is thus left blank.

struct sockaddr_in 
{
      short      sin_family;
      u_short      sin_port;
      struct      in_addr sin_addr;
      char      sin_zero[8];
};
 
int PASCAL connect(SOCKET,const struct sockaddr*,int);

I highly recommend that you type in all of the examples in this report by hand, instead of copying and pasting it into your compiler. While I know that doing so will slow you up, I am confident and know from experience that you will learn the process much better that way than if you copy and paste the code.

//CONNECT TO REMOTE HOST (CLIENT APPLICATION)

//Include the needed header files.

//Don't forget to link libws2_32.a to your program as well

#include <winsock.h>


SOCKET s; //Socket handle


//CONNECTTOHOST � Connects to a remote host

bool ConnectToHost(int PortNo, char* IPAddress)
{
    //Start up Winsock�

    WSADATA wsadata;

    int error = WSAStartup(0x0202, &wsadata);

    //Did something happen?

    if (error)
        return false;

    //Did we get the right Winsock version?

    If (wssadata.wVersion != 0x0202)
    {
        WSACleanup(); //Clean up Winsock

        return false;
    }

    //Fill out the information needed to initialize a socket�

    SOCKADDR_IN target; //Socket address information


    target.sin_family = AF_INET; // address family Internet

    target.sin_port = htons (PortNo); //Port to connect on

    target.sin_addr.s_addr = inet_addr (IPAddress); //Target IP


    s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); //Create socket

    if (s == INVALID_SOCKET)
    {
        return false; //Couldn't create the socket

    }  

    //Try connecting...


    if (connect(s, (SOCKADDR *)&target, sizeof(target)) == SOCKET_ERROR)
    {
        return false; //Couldn't connect

    }
    else
        return true; //Success

}

//CLOSECONNECTION � shuts down the socket and closes any connection on it

void CloseConnection ()
{
    //Close the socket if it exists

    if (s)
        closesocket(s);

    WSACleanup(); //Clean up Winsock

}

Before you move on, type this code up and give it a try.

Receiving Connections � Acting as a Server

Now that you've had a feel for what it's like to connect to a remote computer, it's time to play the "server" role; so remote computers can connect to you. To do this, we can "listen" on any port and await an incoming connection. As always, we use a few handy API calls:

int PASCAL bind(SOCKET,const struct sockaddr*,int); //bind to a socket

int PASCAL listen(SOCKET,int); //Listen for an incoming connection


//Accept a connection request

SOCKET PASCAL accept(SOCKET,struct sockaddr*,int*);

When you act as the server, you can receive requests for a connection on the port you are listening on: say, for example, a remote computer wants to chat with your computer, it will first ask your server whether or not it wants to establish a connection. In order for a connection to be made, your server must accept() the connection request. Note that the "server" decides whether or not to establish the connection. Finally, both computers are connected and can exchange data.

Although the listen() function is the easiest way to listen on a port and act as the server, it is not the most desirable. You will quickly find out when you attempt it that your program will freeze until an incoming connection is made, because listen() is a "blocking" function � it can only perform one task at a time, and will not return until a connection is pending.

This is definitely a problem, but there are a few solutions for it. First, if you are familiar with multi-threaded applications (note that we are not talking about TCP threads here), then you can place the server code on a separate thread that, when started, will not freeze the entire program and the efficiency of the parent program will thus not be impeded. This is really more of a pain that it needs to be; as you could just replace the listen() function with "asynchronous" sockets. If I've caught your attention with that important-sounding name, you can skip ahead to the next section if you like, but I recommend that you stick with me here and learn the fundamentals. We'll spiff up our code later; but for now, let's focus on the bare essentials.

Before you can even think about listening on a port, you must:

  1. Initialize Winsock (we discussed this before, remember)
  2. Start up a socket and make sure it returns a nonzero value, which signifies success and is the handle to the socket
  3. Fill out the SOCKADDR_IN structure with the necessary data, including the address family, port, and IP address.
  4. Use bind() to bind the socket to a specific IP address (if you specified inet_addr("0.0.0.0") or htonl(INADDR_ANY) as the sin_addr section of SOCKADDR_IN, you can bind to any IP address)

At this point, if all has gone according to plan, you're all set to call listen() and spy to your heart's content.

The first parameter of listen() must be the handle to a socket that you have previously initialized. Of course, whatever port this socket is attached to is the port that you will be listening on. You can then specify, with the next and final parameter, how many remote computers can communicate with your server at the same time. Generally, however, unless you want to exclude all but one or a few connections, we just pass SOMAXCONN (SOcket MAX CONNection) as the final parameter to listen(). If the socket is up and working fine, all should go well, and when a connection request received, listen() will return. This is your clue to call accept(), if you wish to establish a connection.

#include <windows.h>

#include <winsock.h>


SOCKET s;
WSADATA w;

//LISTENONPORT � Listens on a specified port for incoming connections 

//or data

int ListenOnPort(int portno)
{
    int error = WSAStartup (0x0202, &w);   // Fill in WSA info


    if (error)
    {
        return false; //For some reason we couldn't start Winsock

    }

    if (w.wVersion != 0x0202) //Wrong Winsock version?

    {
        WSACleanup ();
        return false;
    }

    SOCKADDR_IN addr; // The address structure for a TCP socket


    addr.sin_family = AF_INET;      // Address family

    addr.sin_port = htons (portno);   // Assign port to this socket


    //Accept a connection from any IP using INADDR_ANY

    //You could pass inet_addr("0.0.0.0") instead to accomplish the 

    //same thing. If you want only to watch for a connection from a 

    //specific IP, specify that //instead.

    addr.sin_addr.s_addr = htonl (INADDR_ANY);  

    s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); // Create socket


    if (s == INVALID_SOCKET)
    {
        return false; //Don't continue if we couldn't create a //socket!!

    }

    if (bind(s, (LPSOCKADDR)&addr, sizeof(addr)) == SOCKET_ERROR)
    {
       //We couldn't bind (this will happen if you try to bind to the same  

       //socket more than once)

        return false;
    }

    //Now we can start listening (allowing as many connections as possible to  

    //be made at the same time using SOMAXCONN). You could specify any 

    //integer value equal to or lesser than SOMAXCONN instead for custom 

    //purposes). The function will not //return until a connection request is 

    //made

    listen(s, SOMAXCONN);

    //Don't forget to clean up with CloseConnection()!

}

If you compile and run this code, as mentioned before, your program will freeze until a connection request is made. You could cause this connection request by, for example, trying a "telnet" connection. The connection will inevitably fail, or course, because the connection will not be accepted, but you will cause listen() to return and your program will resurrect from the land of the dead. You can try this by typing telnet 127.0.0.1 "port_number" at the MSDOS command prompt (replace "port_number" with the port that your server is listening on).

Asynchronous Sockets

Because using blocking functions such as listen() is so impractical and such a pain, let's go ahead and before we move on discuss "asynchronous" "sockets". I mentioned these earlier on, and promised you I'd show you how they work.

C++ gives us an advantage here that most high-level programming languages do not; namely, because we don't have to go to the extra length of "sub-classing" the parent window before we can use asynchronous sockets. It's already done for us, so all we really have to do is add the handling code into the message handler. This is because asynchronous sockets, as you will see, depend on being able to send your program messages when a connection request is made, data is being received, etc. This enables it to wait silently in the background without disturbing your parent program or impeding productivity, as it only communicates when necessary. There is a relatively small price to pay, too, for it really doesn't take much additional coding. Understanding how it all works might take a little while, but you'll definitely be pleased that you took the time to understand asynchronous sockets. It'll save you a lot of trouble in the long run.

Instead of having to rework and modify all the code that we have written up to this point, making a socket asynchronous simply requires an additional line of code after the listen() function. Of course, your message handler needs to be ready to accept the following messages:

  • FD_ACCEPT: If your application is acting as the client (i.e., you are attempting to connect to a remote host using connect()), you will receive this message when a connection request is being made. Should you choose to do so, the following message will be sent:
  • FD_CONNECT: Signifies that a connection has been successfully made
  • FD_READ: We've got incoming data from the remote computer. We'll learn how to deal with this later on.
  • FD_CLOSE: The remote host disconnected, so we lost the connection.

These values will be sent in the lParam parameter of your message handler. I'll show you exactly where to put them in a minute; but first, we need to understand the parameters of the API call we'll be using to set our socket to asynchronous mode:

//Switch the socket to a non-blocking asynchronous one

int PASCAL WSAAsyncSelect(SOCKET,HWND,u_int,long);

The first parameter, obviously, asks for a handle to our socket, and the second requires the handle to our parent window. This is necessary so that it send the messages to the correct window! The third parameter, as you can see, accepts an integer value, for which you will specify a unique notification number. When any message is sent to your program's message handler, whatever number you specify here will also be sent. Thus, you would code your message handler to wait for the identification number, and then determine what type of notification has been sent. I know this is confusing, so hopefully a glance at the following source code will shed a little light on the subject:

#define MY_MESSAGE_NOTIFICATION      1048 //Custom notification message


//This is our message handler/window procedure

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message) //handle the messages

    {
    case MY_MESSAGE_NOTIFICATION: //Is a message being sent?

        {
            switch (lParam) //If so, which one is it?

            {
            case FD_ACCEPT:
                //Connection request was made

                break;

            case FD_CONNECT:
                //Connection was made successfully

                break;

            case FD_READ:
                //Incoming data; get ready to receive

                break;

            case FD_CLOSE:
                //Lost the connection

                break;
            }
        }
        break;

        //Other normal window messages here�


    default: //The message doesn't concern us

        return DefWindowProc(hwnd, message, wParam, lParam);
    }
    break;
}

That's not too bad, is it? Now that our handler is all set, we should append the following line of code to function ListenOnPort(), after listen():

//The socket has been created


//IP address has been bound


//Function listen() has just been called� 


//Set the socket to non-blocking asynchronous mode

//�hwnd is a valid handle to the program's parent window

//Make sure you OR all the needed flags

WSAAsyncSelect (s, hwnd, MY_MESSAGE_NOTIFICATION, (FD_ACCEPT | FD_CONNECT |
     FD_READ | FD_CLOSE);
 
//And so forth�

C:\Documents and Settings\Cam>netstat -an

Active Connections

Proto Local Address Foreign Address State
TCP 0.0.0.0:135 0.0.0.0:0 LISTENING
TCP 0.0.0.0:445 0.0.0.0:0 LISTENING
TCP 0.0.0.0:5225 0.0.0.0:0 LISTENING
TCP 0.0.0.0:5226 0.0.0.0:0 LISTENING
TCP 0.0.0.0:8008 0.0.0.0:0 LISTENING
TCP 127.0.0.1:1025 0.0.0.0:0 LISTENING
TCP 127.0.0.1:1035 127.0.0.1:5226 ESTABLISHED
TCP 127.0.0.1:5226 127.0.0.1:1035 ESTABLISHED
TCP 127.0.0.1:8005 0.0.0.0:0 LISTENING
UDP 0.0.0.0:445 *:*
UDP 0.0.0.0:500 *:*
UDP 0.0.0.0:4500 *:*
UDP 127.0.0.1:123 *:*
UDP 127.0.0.1:1031 *:*
UDP 127.0.0.1:1032 *:*
UDP 127.0.0.1:1900 *:*

C:\Documents and Settings\Cam>

If your server is working correctly, you should see under "Local Address" something like, "0.0.0.0:Port#," where Port# is the port that you are listening on, in a LISTENING state. Incidentally, if you forget to use htons() to convert the port number, you might find a new port has been opened, but it will be on a completely different port than what you expected.

Don't worry if it takes you a couple of tries to get everything working right; it happens to all of us. You'll get it with a couple of tries. (Of course, if you try without avail for a couple weeks, burn this report and forget who wrote it!)

Sending and Receiving Data

Up to this section, all you've got for a server is a deaf mute! Which, not surprisingly, does not do you a lot of good in the real world. So, let's take a look at how we can communicate properly and effectively with any computer that decided to chat with us. As always, a few API calls come to the rescue when we're stumped:

//Send text data to a remote computer

int PASCAL send(SOCKET,const char*,int,int);

//Receive incoming text from a remote computer

int PASCAL recv(SOCKET,char*,int,int); 

//Advanced functions that allow you to communicate exclusively with a 

//certain computer when multiple computers are connected to the same server

int PASCAL sendto(SOCKET,const char*,int,int,const struct   sockaddr*,int);
int PASCAL recvfrom(SOCKET,char*,int,int,struct sockaddr*,int*);

If you're not using an asynchronous server, then you'll have to put the recv() function in a timer function, that constantly checks for incoming data � not so elegant of a solution, to say the least. If, on the other hand, you've done the smart thing and set up an asynchronous server, then all you have to do is put your recv() code inside FD_READ in your message handler. When there's incoming data, you'll be notified. Can't get any easier than that!

When we do detect activity, a buffer must be created to hold it, and then a pointer to the buffer passed to recv(). After the function returns, the text should have been dutifully placed in our buffer just itching to be displayed. Check out the source code:

case FD_READ:
    {
        char buffer[80];
        memset(buffer, 0, sizeof(buffer)); //Clear the buffer


        //Put the incoming text into our buffer

        recv (s, buffer, sizeof(buffer)-1, 0); 

        //Do something smart with the text in buffer

        //You could display it in a textbox, or use:


        //MessageBox(hwnd, Buffer, "Captured Text�", MB_OK);

    }
    break;

Now that you can receive incoming text from the remote computer or server, all that our server lacks is the ability to reply, or "send" data to the remote computer. This is probably the most simple and self-evident process in Winsock programming, but if you're like me and like to have every step spelled out for you, here's how to use send() correctly:

char *szpText;

//Allocate memory for the text in your Text Edit, retrieve the text,

//(see the source code for this) and then pass a pointer to it�

 
send(s, szpText, len_of_text, 0);

For brevity's sake, the above snippet of code is just a skeleton to give you a general idea of how send() is used. To see the entire code, please download the example source code that comes along with this tutorial.

On a more advanced note, sometimes the simple send() and receive() functions just aren't enough to do what you want. This happens when you have multiple connections at the same time from different sources (remember when we called listen(), we passed SOMAXCONN to allow the maximum number of connections possible), and you need to send data to one particular computer, and not all of them. If you're uncommonly sharp, you may have noticed two extra API below send() and receive() (extra credit if you did!); sendto() and receivefrom().

These two API allow you to communicate with any one remote computer without tipping your hand to everyone else that is connected. There is an extra parameter that accepts a pointer to a sockaddr_in structure in these advanced functions, which you can use to specify the IP address of any remote computer that you want to communicate with exclusively. This is an important skill to know if you are building a full-fledged chat program, or something similar, but beyond giving you the basic idea of how these functions work, I'll let you figure them out on your own. (Don't you hate it when authors say that? Usually it's because we don't have the slightest clue ourselves � but really, it shouldn't take much to implement them if you decide that you need to.)

Some Final Notes

Well, by now you should have a decent understanding of Windows sockets � or a profound hatred of them � but at any rate, if you're looking for a much better explanation than I can give you here, please take a look at the example source code provided with this article. Practice will do much more for you than reading any article.

Additionally, I have found that if you try to copy and paste code, or just compiling someone else's code you found on the Internet, you won't come close to the level of understanding you will gain if you type in all the examples by hand yourself. A big pain, I know! But if you take the time to do it, you'll save yourself a lot of trouble in the long run.

Have fun, and let me know what you think of this article by posting feedback.

This article (not including the accompanying source code) is copyrighted � 2006 by the author, and cannot be modified, sold, and redistributed for personal gain without prior explicit permission from him. It is provided free of charge for the benefit of the public. You are allowed, however, to make and distribute as many copies of it as you like, provided that you do not modify the original content in any way. Thanks!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here