< IP Address & Winsock2 APIs | Winsock2 Main | More Winsock2 APIs >


 

 

An Intro to Windows Socket Programming with C

Part 3

 

 

What do we have in this chapter 1 part 3?

  1. Client API Functions

  2. TCP States

  3. connect()

 

 

Client API Functions

 

The client is much simpler and involves fewer steps to set up a successful connection. There are only three steps for a client:

 

  1. Create a socket.

  2. Set up a SOCKADDR address structure with the name of server you are going to connect to (dependent on underlying protocol). For TCP/IP, this is the server's IP address and port number its application is listening on.

  3. Initiate the connection with connect() or WSAConnect().

 

You already know how to create the socket and construct a SOCKADDR structure, so the only remaining step is establishing a connection.

 

TCP States

 

By knowing the actual TCP states you will gain a better understanding of how the Winsock API calls affect change in the underlying protocol. In addition, many programmers run into a common problem when closing sockets: the TCP states surrounding a socket closure are of the most interest.

The start state of every socket is the CLOSED state. When a client initiates a connection, it sends a SYN packet to the server and puts the client socket in the SYN_SENT state. When the server receives the SYN packet, it sends a SYN-ACK packet, which the client responds to with an ACK packet. At this point, the client's socket is in the ESTABLISHED state. If the server never sends a SYN-ACK packet, the client time out and reverts to the CLOSED state. You can refer to the TCP state diagram to get a better description.

When a server's socket is bound and is listening on a local interface and port, the state of the socket is LISTEN. When a client attempts a connection, the server receives a SYN packet and responds with a SYN-ACK packet. The state of the server's socket changes to SYN_RCVD. Finally, the client sends an ACK packet, which causes the state of the server's socket to change to ESTABLISHED.

Once the application is in the ESTABLISHED state, there are two paths for closure. If your application initiates the closure, it is known as an active socket closure; otherwise, the socket closure is passive. If you actively initiate a closure, your application sends a FIN packet. When your application calls closesocket or shutdown (with SD_SEND as its second argument), your application sends a FIN packet to the peer, and the state of your socket changes to FIN_WAIT_1. Normally, the peer responds with an ACK packet, and your socket's state becomes FIN_WAIT_2. If the peer also closes the connection, it sends a FIN packet and your computer responds by sending an ACK packet and placing your socket in the TIME_WAIT state.

The TIME_WAIT state is also called the 2MSL wait state. MSL stands for Maximum Segment Lifetime and represents the amount of time a packet can exist on the network before being discarded. Each IP packet has a time-to-live (TTL) field, which when decremented to 0 causes the packet to be discarded. Each router on the network that handles the packet decrements the TTL by 1 and passes the packet on. Once an application enters the TIME_WAIT state, it remains there for twice the MSL time. This allows TCP to re-send the final ACK in case it's lost, causing the FIN to be retransmitted. After the 2MSL wait state completes, the socket goes to the CLOSED state.

On an active close, two other paths lead to the TIME_WAIT state. In our previous discussion, only one side issues a FIN and receives an ACK response, but the peer is still free to send data until it too closes. This is where the other two paths come into play. In one path, the simultaneous close, a computer and its peer at the other side of a connection issue a close at the same time; the computer sends a FIN packet to the peer and receives a FIN packet from the peer. Then the computer sends an ACK packet in response to the peer's FIN packet and changes its socket to the CLOSING state. Once the computer receives the last ACK packet from the peer, the computer's socket state becomes TIME_WAIT.

The other path for an active closure is just a variation on the simultaneous close: the socket transitions from the FIN_WAIT_1 state directly to the TIME_WAIT state. This occurs when an application sends a FIN packet but shortly thereafter receives a FIN-ACK packet from the peer. In this case, the peer is acknowledging the application's FIN packet and sending its own, to which the application responds with an ACK packet.

The major effect of the TIME_WAIT state is that while a TCP connection is in the 2MSL wait state, the socket pair defining that connection cannot be reused. A socket pair is the combination of local IP–local port and remote IP–remote port. Some TCP implementations do not allow the reuse of any port number in a socket pair in the TIME_WAIT state. Microsoft's implementation does not suffer from this deficiency. However, if a connection is attempted in which the socket pair is already in the TIME_WAIT state, the connection attempt will fail with error WSAEADDRINUSE. One way around this (besides waiting for the socket pair that is using that local port to leave the TIME_WAIT state) is to use the socket option SO_REUSEADDR.

The last point of discussion for socket states is the passive closure. In this scenario, an application receives a FIN packet from the peer and responds with an ACK packet. At this point, the application's socket changes to the CLOSE_WAIT state. Because the peer has closed its end, it can't send any more data, but the application still can until it also closes its end of the connection. To close its end of the connection, the application sends its own FIN, causing the application's TCP socket state to become LAST_ACK. After the application receives an ACK packet from the peer, the application's socket reverts to the CLOSED state. For more information regarding the TCP/IP protocol, consult RFC 793.

 

connect()

 

Connecting a socket is accomplished by calling connect(), WSAConnect(), or ConnectEx(). We'll look at the Winsock 1 version of this function, which is defined as:

 

int connect(

    SOCKET s,

    const struct sockaddr FAR* name,

    int namelen

);

 

s is the valid TCP socket on which to establish the connection.

name is the socket address structure (SOCKADDR_IN) for TCP that describes the server to connect to.

namelen is the length of the name variable.

If the computer you're attempting to connect to does not have a process listening on the given port, the connect() call fails with the WSAECONNREFUSED error. The other error you might encounter is WSAETIMEDOUT, which occurs if the destination you're trying to reach is unavailable (either because of a communication-hardware failure on the route to the host or because the host is not currently on the network).

The following program example demonstrates how to write a simple client that can connect to the server application demonstrated earlier.

 

1.      While in the Visual C++ IDE, click File menu > Project sub menu to create a new project.

2.      Select Win32 for the Project types: and Win32 Console Application for the Templates:. Put the project and solution name. Adjust the project location if needed and click OK.

 

Windows socket and C programming:creating new C Win32 console application

 

3.      Click Next for the Win32 Application Wizard Overview page. We will remove all the unnecessary project items.

4.      In the Application page, select Empty project for the Additional options:. Leave others as given and click Finish.

 

Windows socket and C programming: selecting an empty Win32 console application

 

5.      Next, we need to add new source file. Click Project menu > Add New Item sub menu or select the project folder in the Solution Explorer > Select Add menu > Select New Item sub menu.

6.      Select C++ File (.cpp) for the Templates:. Put the source file name and click Add. Although the extension is .cpp, Visual C++ IDE will recognize that the source code used is C based on the Compile as C Code (/TC) option which will be set in the project property page later.

 

Windows socket and C programming: adding a C source file to the existing project

 

7.      Now, add the source code as given below.

 

#include <winsock2.h>

#include <stdio.h>

 

int main(int argc, char **argv)

{

     WSADATA              wsaData;

     SOCKET               SendingSocket;

     // Server/receiver address

     SOCKADDR_IN          ServerAddr;

     // Server/receiver port to connect to

     unsigned int         Port = 80;

     int  RetCode;

 

     // Initialize Winsock version 2.2

     WSAStartup(MAKEWORD(2,2), &wsaData);

     printf("Client: Winsock DLL status is %s.\n", wsaData.szSystemStatus);

 

     // Create a new socket to make a client connection.

     // AF_INET = 2, The Internet Protocol version 4 (IPv4) address family, TCP protocol

     SendingSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

     if(SendingSocket == INVALID_SOCKET)

     {

          printf("Client: socket() failed! Error code: %ld\n", WSAGetLastError());

          // Do the clean up

          WSACleanup();

          // Exit with error

          return -1;

     }

     else

          printf("Client: socket() is OK!\n");

 

     // Set up a SOCKADDR_IN structure that will be used to connect

     // to a listening server on port 5150. For demonstration

     // purposes, let's assume our server's IP address is 127.0.0.1 or localhost

 

     // IPv4

     ServerAddr.sin_family = AF_INET;

     // Port no.

     ServerAddr.sin_port = htons(Port);

     // The IP address

     ServerAddr.sin_addr.s_addr = inet_addr("209.131.36.158");

 

     // Make a connection to the server with socket SendingSocket.

     RetCode = connect(SendingSocket, (SOCKADDR *) &ServerAddr, sizeof(ServerAddr));

     if(RetCode != 0)

     {

          printf("Client: connect() failed! Error code: %ld\n", WSAGetLastError());

          // Close the socket

          closesocket(SendingSocket);

          // Do the clean up

          WSACleanup();

          // Exit with error

          return -1;

     }

     else

     {

          printf("Client: connect() is OK, got connected...\n");

          printf("Client: Ready for sending and receiving data...\n");

     }

 

     // At this point you can start sending or receiving data on

     // the socket SendingSocket. We will describe sending and receiving data

     // later in the chapter.

 

     // When you are finished sending and receiving data on socket SendingSocket,

     // you should close the socket using the closesocket API. We will

     // describe socket closure later in the chapter.

     if(closesocket(SendingSocket) != 0)

          printf("Client: Cannot close \"SendingSocket\" socket. Error code: %ld\n", WSAGetLastError());

     else

          printf("Client: Closing \"SendingSocket\" socket...\n");

 

     // When your application is finished handling the connection, call WSACleanup.

     if(WSACleanup() != 0)

          printf("Client: WSACleanup() failed!...\n");

     else

          printf("Client: WSACleanup() is OK...\n");

     return 0;

}

 

8.      In the meantime, if you want to enable the line number for the VC++ editor, invoke the project Options page.

 

------------------------------------------------------------------

Windows socket and C programming: invoking the Visual C++ Options project page

 

9.      Expand the Text Editor folder > Select C/C++ link > Check the Line numbers option in the Display checkbox group.

 

Windows socket and C programming: enabling the Visual C++ editor line number

 

10. Then, build the project and make sure there is no error.

 

Windows socket and C programming: building C Winsock2 project

 

11. Run the project.

 

Windows socket and C programming: running the C Winsock2 project without debugging

 

12. If there is no error, the following sample output should be expected.

 

Windows socket and C programming: a sample of Winsock2 client console output with connection refused error code

 

13. The error code 10061 is enumeration for WSAECONNREFUSED which is a Connection refused. It is because there is no listening server on the specified address and port number. The next steps will demonstrate the client and server programs running.

14. Run the previous server program from command prompt (or you can open another Visual C++ IDE instance and run it).

 

Windows socket and C programming: the Winsock server/receiver in action, ready to accept connection from clients

 

15. Then, run the client program.

 

Windows socket and C programming: the Winsock2 server/receiver and client/sender in actions

 

16. Next, let test this client connection to the real server. In this case we change the port to 80 (standard http port) and the server is www.yahoo.com or www.google.com. Then we rebuild the project. You can use the ping tool to get the server address.

 

Windows socket and C programming: running the ping command against domain name to get the IP address

 

17. The following screenshot shows a sample output when connecting to one of the Yahoo.com web server.

 

Windows socket and C programming: testing the client/sender program against the real server

 

Now that you can set up communication for a connection-oriented server and client, you are ready to begin handling data transmission.

 

 

 


< IP Address & Winsock2 APIs | Winsock2 Main | More Winsock2 APIs >