< Client APIs & TCP States | Winsock2 Main | TCP Client-server Examples >
|
Data Transmission
Sending and receiving data is what network programming is all about. For sending data on a connected socket, there are two API functions:
The second function is specific to Winsock 2. Likewise, two functions are for receiving data on a connected socket:
The latter is also a Winsock 2 call. An important thing to keep in mind is that all buffers associated with sending and receiving data are of the simple char type which is just simple byte-oriented data. In reality, it can be a buffer with any raw data in it, whether it's binary or string data doesn't matter. In addition, the error code returned by all send and receive functions is SOCKET_ERROR. Once an error is returned, call WSAGetLastError() to obtain extended error information. The two most common errors encountered are WSAECONNABORTED and WSAECONNRESET. Both of these deal with the connection being closed, either through a timeout or through the peer closing the connection. Another common error is WSAEWOULDBLOCK, which is normally encountered when either nonblocking or asynchronous sockets are used. This error basically means that the specified function cannot be completed at this time. In another chapter, we will describe various Winsock I/O methods that can help you avoid some of these errors.
send() and WSASend()
The first API function to send data on a connected socket is send(), which is prototyped as:
int send( SOCKET s, const char FAR * buf, int len, int flags ); |
The SOCKET parameter is the connected socket to send the data on.
The second parameter, buf, is a pointer to the character buffer that contains the data to be sent.
The third parameter, len, specifies the number of characters in the buffer to send.
Finally, the flags parameter can be either 0, MSG_DONTROUTE, or MSG_OOB. Alternatively, the flags parameter can be a bitwise OR any of those flags. The MSG_DONTROUTE flag tells the transport not to route the packets it sends. It is up to the underlying transport to honor this request (for example, if the transport protocol doesn't support this option, it will be ignored). The MSG_OOB flag signifies that the data should be sent out of band.
On a good return, send returns the number of bytes sent; otherwise, if an error occurs, SOCKET_ERROR will be returned. A common error is WSAECO-NNABORTED, which occurs when the virtual circuit terminates because of a timeout failure or a protocol error. When this occurs, the socket should be closed, as it is no longer usable. The error WSAECONNRESET occurs when the application on the remote host resets the virtual circuit by executing a hard close or terminating unexpectedly, or when the remote host is rebooted. Again, the socket should be closed after this error occurs. The last common error is WSAETIMEDOUT, which occurs when the connection is dropped because of a network failure or the remote connected system going down without notice.
The Winsock 2 version of the send() API function, WSASend(), is defined as:
int WSASend(
SOCKET s,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesSent,
DWORD dwFlags,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
The socket is a valid handle to a connection session.
The second parameter is a pointer to one or more WSABUF structures. This can be either a single structure or an array of such structures.
The third parameter indicates the number of WSABUF structures being passed. Remember that each WSABUF structure is a character buffer and the length of that buffer. You might wonder why you would want to send more than one buffer at a time. This is called scatter-gather I/O and will be discussed later in this chapter; however, in the case of data sent using multiple buffers on a connected socket, each buffer is sent from the first to the last WSABUF structure in the array.
The lpNumberOfBytesSent is a pointer to a DWORD that on return from the WSASend() call contains the total number of bytes sent.
The dwFlags parameter is equivalent to its counterpart in send.
The last two parameters, lpOverlapped and lpCompletionRoutine, are used for overlapped I/O. Overlapped I/O is one of the asynchronous I/O models that Winsock supports and is discussed in detail in other chapter.
The WSASend() function sets lpNumberOfBytesSent to the number of bytes written. The function returns 0 on success and SOCKET_ERROR on any error, and generally encounters the same errors as the send function. There is one final send function you should be aware of: WSASendDisconnect().
This function is rather specialized and not generally used. The function prototype is:
int WSASendDisconnect(SOCKET s, LPWSABUF lpOutboundDisconnectData);
When an application on a connected stream socket needs to send data that is more important than regular data on the stream, it can mark the important data as out-of-band (OOB) data. The application on the other end of a connection can receive and process OOB data through a separate logical channel that is conceptually independent of the data stream.
In TCP, OOB data is implemented via an urgent 1-bit marker (called URG) and a 16-bit pointer in the TCP segment header that identify a specific downstream byte as urgent data. Two specific ways of implementing urgent data currently exist for TCP. RFC 793, which describes TCP and introduces the concept of urgent data, indicates that the urgent pointer in the TCP header is a positive offset to the byte that follows the urgent data byte. However, RFC 1122 describes the urgent offset as pointing to the urgent byte itself.
The Winsock specification uses the term OOB to refer to both protocol-independent OOB data and TCP's implementation of OOB data (urgent data). To check whether pending data contains urgent data, you must call the ioctlsocket function with the SIOCATMARK option.
Winsock provides several methods for obtaining the urgent data. Either the urgent data is inlined so that it appears in the normal data stream, or inlining can be turned off so that a discrete call to a receive function returns only the urgent data. The socket option SO_OOBINLINE controls the behavior of OOB data.
Telnet and Rlogin use urgent data for several reasons. However, unless you plan to write your own Telnet or Rlogin, you should stay away from urgent data. It's not well defined and might be implemented differently on platforms other than Windows. If you require a method of signaling the peer for urgent reasons, implement a separate control socket for this urgent data and reserve the main socket connection for normal data transfers.
The function initiates a shutdown of the socket and sends disconnect data. Of course, this function is available only to those transport protocols that support graceful close and disconnect data. The WSASendDisconnect() function behaves like a call to the shutdown() function (which is described later) with an SD_SEND argument, but it also sends the data contained in its lpOutboundDisconnectData parameter. Subsequent sends are not allowed on the socket. Upon failure, WSASendDisconnect() returns SOCKET_ERROR. This function can encounter some of the same errors as the send function.
The recv() function is the most basic way to accept incoming data on a connected socket. This function is defined as:
int recv(
SOCKET s,
char FAR* buf,
int len,
int flags
);
The first parameter, s, is the socket on which data will be received.
The second parameter, buf, is the character buffer that will receive the data,
The len is either the number of bytes you want to receive or the size of the buffer, buf.
Finally, the possible value of flags parameter is constructed by using the bitwise OR operator with any of the following values.
Value |
Meaning |
MSG_PEEK |
Peeks at the incoming data. The data is copied into the buffer, but is not removed from the input queue. The function subsequently returns the amount of data that can be read in a single call to the recv (or recvfrom) function, which may not be the same as the total amount of data queued on the socket. The amount of data that can actually be read in a single call to the recv (or recvfrom) function is limited to the data size written in the send or sendto function call. |
MSG_OOB |
Processes Out Of Band (OOB) data. |
MSG_WAITALL |
The receive request will complete only when one of the following events occurs:
Note that if the underlying transport does not support MSG_WAITALL, or if the socket is in a non-blocking mode, then this call will fail with WSAEOPNOTSUPP. Also, if MSG_WAITALL is specified along with MSG_OOB, MSG_PEEK, or MSG_PARTIAL, then this call will fail with WSAEOPNOTSUPP. This flag is not supported on datagram sockets or message-oriented CO sockets. |
Of course, 0 specifies no special actions. MSG_PEEK causes the data that is available to be copied into the supplied receive buffer, but this data is not removed from the system's buffer. The number of bytes pending is also returned.
Message peeking is bad. Not only does it degrade performance, as you now need to make two system calls (one to peek and one without the MSG_PEEK flag to actually remove the data), but it is also unreliable under certain circumstances. The data returned might not reflect the entire amount available. Also, by leaving data in the system buffers, the system has less space to contain incoming data. As a result, the system reduces the TCP window size for all senders. This prevents your application from achieving the maximum possible throughput. The best thing to do is to copy all the data you can into your own buffer and manipulate it there.
There are some considerations when using recv() on a message- or datagram-based socket such as UDP, which we will describe later. If the data pending is larger than the supplied buffer, the buffer is filled with as much data as it will contain. In this event, the recv() call generates the error WSAEMSGSIZE. Note that the message-size error occurs with message-oriented protocols. Stream protocols such as TCP buffer incoming data and will return as much data as the application requests, even if the amount of pending data is greater. Thus, for streaming protocols you will not encounter the WSAEMSGSIZE error.
The WSARecv() function adds some new capabilities over recv(), such as overlapped I/O and partial datagram notifications. The definition of WSARecv() is:
int WSARecv(
SOCKET s,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesRecvd,
LPDWORD lpFlags,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
Parameter s is the connected socket.
The second and third parameters are the buffers to receive the data. The lpBuffers parameter is an array of WSABUF structures, and dwBufferCount indicates the number of WSABUF structures in the array.
The lpNumberOfBytesReceived parameter points to the number of bytes received by this call if the receive operation completes immediately.
The lpFlags parameter can be one of the values MSG_PEEK, MSG_OOB, or MSG_PARTIAL, or a bitwise OR combination of those values. The MSG_PARTIAL flag has several different meanings depending on where it is used or encountered. For message-oriented protocols that support partial messaging (like AppleTalk), this flag is set upon return from WSARecv() (if the entire message could not be returned in this call because of insufficient buffer space). In this case, subsequent WSARecv() calls set this flag until the entire message is returned, when the MSG_PARTIAL flag is cleared. If this flag is passed as an input parameter, the receive operation should complete as soon as data is available, even if it is only a portion of the entire message. The MSG_PARTIAL flag is used only with message-oriented protocols, not with streaming ones. In addition, not all protocols support partial messages. The protocol entry for each protocol contains a flag indicating whether it supports this feature.
The lpOverlapped and lpCompletionRoutine parameters are used in overlapped I/O operations, discussed in other chapter. There is one other specialized receive function you should be aware of: WSARecvDisconnect().
This function is the opposite of WSASendDisconnect() and is defined as follows:
int WSARecvDisconnect(SOCKET s, LPWSABUF lpInboundDisconnectData);
Like its sending counterpart, the parameters of WSASendDisconnect() are the connected socket handle and a valid WSABUF structure with the data to be received. The data received can be only disconnecting data that is sent by a WSASendDisconnect() on the other side; it cannot be used to receive normal data. In addition, once the data is received, this function disables reception from the remote party, which is equivalent to calling the shutdown() function (which is described later) with SD_RECEIVE.
Because most connection-oriented communication, such as TCP, is streaming protocols, we'll briefly describe them here. A streaming protocol is one that the sender and receiver may break up or coalesce data into smaller or larger groups. The following Figure describes briefly the flow of TCP packet between client and server sides.
The main thing to be aware of with any function that sends or receives data on a stream socket is that you are not guaranteed to read or write the amount of data you request. Let's say you have a character buffer with 2048 bytes of data you want to send with the send function. The code to send this is:
char sendbuff[2048];
int nBytes = 2048;
// Fill sendbuff with 2048 bytes of data
// Assume s is a valid, connected stream socket
ret = send(s, sendbuff, nBytes, 0);
It is possible for send to return having sent less than 2048 bytes. The ret variable will be set to the number of bytes sent because the system allocates a certain amount of buffer space for each socket to send and receive data. In the case of sending data, the internal buffers hold data to be sent until such time as the data can be placed on the wire. Several common situations can cause this. For example, simply transmitting a huge amount of data will cause these buffers to become filled quickly. Also, for TCP/IP, there is what is known as the window size sliding window demo (http://www2.rad.com/networks/2004/sliding_window/). The receiving end will adjust this window size to indicate how much data it can receive. If the receiver is being flooded with data, it might set the window size to 0 to catch up with the pending data. This will force the sender to stop until it receives a new window size greater than 0. In the case of our send call, there might be buffer space to hold only 1024 bytes, in which case you would have to resubmit the remaining 1024 bytes. The following code ensures that all your bytes are sent:
char sendbuff[2048];
int nBytes = 2048, nLeft, idx;
// Fill sendbuff with 2048 bytes of data
nLeft = nBytes;
idx = 0;
while (nLeft > 0)
{
// Assume s is a valid, connected stream socket
ret = send(s, &sendbuff[idx], nLeft, 0);
if (ret == SOCKET_ERROR)
{
// Error handler
}
nLeft -= ret;
idx += ret;
}
The same principle holds true for receiving data on a stream socket but is less significant. Because stream sockets are a continuous stream of data, when an application reads, it isn't generally concerned with how much data it should read. If your application requires discrete messages over a stream protocol, you might have to do a little work. If all the messages are the same size, life is pretty simple, and the code for reading, say, 512-byte messages would look like this:
char recvbuff[1024];
int ret, nLeft, idx;
nLeft = 512;
idx = 0;
while (nLeft > 0)
{
ret = recv(s, &recvbuff[idx], nLeft, 0);
if (ret == SOCKET_ERROR)
{
// Error handler
}
idx += ret;
nLeft -= ret;
}
Things get a little complicated if your message sizes vary. It is necessary to impose your own protocol to let the receiver know how big the forthcoming message will be. For example, the first four bytes written to the receiver will always be the integer size in bytes of the forthcoming message. The receiver will start every read by looking at the first four bytes, converting them to an integer, and determining how many additional bytes that message comprises.
Scatter-gather support is a concept originally introduced in Berkeley Sockets with the functions recv and writev. This feature is available with the Winsock 2 functions WSARecv(), WSARecvFrom(), WSASend(), and WSASendTo(). It is most useful for applications that send and receive data that is formatted in a very specific way. For example, messages from a client to a server might always be composed of a fixed 32-byte header specifying some operation, followed by a 64-byte data block and terminated with a 16-byte trailer. In this example, WSASend() can be called with an array of three WSABUF structures, each corresponding to the three message types. On the receiving end, WSARecv() is called with three WSABUF structures, each containing data buffers of 32 bytes, 64 bytes, and 16 bytes.
When using stream-based sockets, scatter-gather operations simply treat the supplied data buffers in the WSABUF structures as one contiguous buffer. Also, the receive call might return before all buffers are full. On message-based sockets, each call to a receive operation receives a single message up to the buffer size supplied. If the buffer space is insufficient, the call fails with WSAEMSGSIZE and the data is truncated to fit the available space. Of course, with protocols that support partial messages, the MSG_PARTIAL flag can be used to prevent data loss.
Once you are finished with a socket connection, you must close it and release any resources associated with that socket handle. To actually release the resources associated with an open socket handle, use the closesocket() call. Be aware, however, that closesocket() can have some adverse effects, depending on how it is called, that can lead to data loss. For this reason, a connection should be gracefully terminated with the shutdown() function before a call to the closesocket() function. These two API functions are discussed next.
To ensure that all data an application sends is received by the peer, a well-written application should notify the receiver that no more data is to be sent. Likewise, the peer should do the same. This is known as a graceful close and is performed by the shutdown() function, defined as:
int shutdown(SOCKET s, int how);
The how parameter can be SD_RECEIVE, SD_SEND, or SD_BOTH. For SD_RECEIVE, subsequent calls to any receive function on the socket are disallowed. This has no effect on the lower protocol layers. And for TCP sockets, if data is queued for receive or if data subsequently arrives, the connection is reset. However, on UDP sockets incoming data is still accepted and queued (because shutdown() has no meaning for connectionless protocols). For SD_SEND, subsequent calls to any send function are disallowed. For TCP sockets, this causes a FIN packet to be generated after all data is sent and acknowledged by the receiver. Finally, specifying SD_BOTH disables both sends and receives.
Note that not all connection-oriented protocols support graceful closure, which is what the shutdown() API performs. For these protocols (such as ATM), only closesocket() needs to be called to terminate the session. A flag that describes what types of operation is summarized in the following Table. Possible values for this flag are listed in the Winsock2.h header file.
Value |
Meaning |
SD_SEND (0) |
Shutdown send operations. |
SD_RECEIVE (1) |
Shutdown receive operations. |
SD_BOTH (2) |
Shutdown both send and receive operations. |
If no error occurs, shutdown returns zero. Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError().
Error code |
Meaning |
WSANOTINITIALISED |
A successful WSAStartup() call must occur before using this function. |
WSAENETDOWN |
The network subsystem has failed. |
WSAEINVAL |
The how parameter is not valid, or is not consistent with the socket type. For example, SD_SEND is used with a UNI_RECV socket type. |
WSAEINPROGRESS |
A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function. |
WSAENOTCONN |
The socket is not connected (connection-oriented sockets only). |
WSAENOTSOCK |
The descriptor is not a socket. |
Once the shutdown() function is called to disable send, receive, or both, there is no method to re-enable send or receive for the existing socket connection. An application should not rely on being able to reuse a socket after it has been shut down. In particular, a Windows Sockets provider is not required to support the use of connect() on a socket that has been shut down.
If an application wants to reuse a socket, then the DisconnectEx() function should be called with the dwFlags parameter set to TF_REUSE_SOCKET to close a connection on a socket and prepare the socket handle to be reused. When the DisconnectEx() request completes, the socket handle can be passed to the AcceptEx() or ConnectEx() function.
If an application wants to reuse a socket, the TransmitFile() or TransmitPackets() functions can be called with the dwFlags parameter set with TF_DISCONNECT and TF_REUSE_SOCKET to disconnect after all the data has been queued for transmission and prepare the socket handle to be reused. When the TransmitFile() request completes, the socket handle can be passed to the function call previously used to establish the connection, such as AcceptEx() or ConnectEx(). When the TransmitPackets() function completes, the socket handle can be passed to the AcceptEx() function.
Take note that the socket level disconnect is subject to the behavior of the underlying transport. For example, a TCP socket may be subject to the TCP TIME_WAIT state, causing the DisconnectEx(), TransmitFile(), or TransmitPackets() call to be delayed.
The closesocket() function closes a socket and is defined as:
int closesocket (SOCKET s);
If no error occurs, closesocket() returns zero. Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError().
Error code |
Meaning |
WSANOTINITIALISED |
A successful WSAStartup() call must occur before using this function. |
WSAENETDOWN |
The network subsystem has failed. |
WSAENOTSOCK |
The descriptor is not a socket. |
WSAEINPROGRESS |
A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function. |
WSAEINTR |
The (blocking) Windows Socket 1.1 call was canceled through WSACancelBlockingCall(). |
WSAEWOULDBLOCK |
The socket is marked as nonblocking, but the l_onoff member of the linger structure is set to non-zero and the l_linger member of the linger structure is set to a nonzero timeout value. |
Calling closesocket() releases the socket descriptor and any further calls using the socket fail with WSAENOTSOCK. If there are no other references to this socket, all resources associated with the descriptor are released. This includes discarding any queued data.
Pending synchronous calls issued by any thread in this process are canceled without posting any notification messages. Pending overlapped operations are also canceled. Any event, completion routine, or completion port that is associated with the overlapped operation is performed but will fail with the error WSA_OPERATION_ABORTED. In addition, one other factor influences the behavior of closesocket(): whether the socket option SO_LINGER has been set. An application should always have a matching call to closesocket() for each successful call to socket to return any socket resources to the system.
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.
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.
---------------------------------------------------------
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.
7. Now, add the source code as given below.
#include <winsock2.h>
#include <stdio.h>
// A sample of the select() return value
int recvTimeOutTCP(SOCKET socket, long sec, long usec)
{
// Setup timeval variable
struct timeval timeout;
struct fd_set fds;
// assign the second and microsecond variables
timeout.tv_sec = sec;
timeout.tv_usec = usec;
// Setup fd_set structure
FD_ZERO(&fds);
FD_SET(socket, &fds);
// Possible return values:
// -1: error occurred
// 0: timed out
// > 0: data ready to be read
return select(0, &fds, 0, 0, &timeout);
}
int main(int argc, char **argv)
{
WSADATA wsaData;
SOCKET ListeningSocket, NewConnection;
SOCKADDR_IN ServerAddr, SenderInfo;
int Port = 7171;
// Receiving part
char recvbuff[1024];
int ByteReceived, i, nlen, SelectTiming;
// Initialize Winsock version 2.2
if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
{
// The WSAGetLastError() function is one of the only functions
// in the Winsock 2.2 DLL that can be called in the case of a WSAStartup failure
printf(Server: WSAStartup failed with error %ld.\n, WSAGetLastError());
// Exit with error
return 1;
}
else
{
printf(Server: The Winsock DLL found!\n);
printf(Server: The current status is %s.\n, wsaData.szSystemStatus);
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2 )
{
//Tell the user that we could not find a usable WinSock DLL
printf(Server: The dll do not support the Winsock version %u.%u!\n,
LOBYTE(wsaData.wVersion),HIBYTE(wsaData.wVersion));
// Do the clean up
WSACleanup();
// and exit with error
return 1;
}
else
{
printf(Server: The dll supports the Winsock version %u.%u!\n, LOBYTE(wsaData.wVersion),
HIBYTE(wsaData.wVersion));
printf(Server: The highest version this dll can support is %u.%u\n,
LOBYTE(wsaData.wHighVersion), HIBYTE(wsaData.wHighVersion));
}
// Create a new socket to listen for client connections.
ListeningSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// Check for errors to ensure that the socket is a valid socket.
if (ListeningSocket == INVALID_SOCKET)
{
printf(Server: Error at socket(), error code: %ld.\n, WSAGetLastError());
// Clean up
WSACleanup();
// and exit with error
return 1;
}
else
printf(Server: socket() is OK!\n);
// Set up a SOCKADDR_IN structure that will tell bind that we
// want to listen for connections on all interfaces using port 7171.
// The IPv4 family
ServerAddr.sin_family = AF_INET;
// host-to-network byte order
ServerAddr.sin_port = htons(Port);
// Listen on all interface, host-to-network byte order
ServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);
// Associate the address information with the socket using bind.
// Call the bind function, passing the created socket and the sockaddr_in
// structure as parameters. Check for general errors.
if (bind(ListeningSocket, (SOCKADDR *)&ServerAddr, sizeof(ServerAddr)) == SOCKET_ERROR)
{
printf(Server: bind() failed! Error code: %ld.\n, WSAGetLastError());
// Close the socket
closesocket(ListeningSocket);
// Do the clean up
WSACleanup();
// and exit with error
return 1;
}
else
printf(Server: bind() is OK!\n);
// Listen for client connections with a backlog of 5
if (listen(ListeningSocket, 5) == SOCKET_ERROR)
{
printf(Server: listen(): Error listening on socket %ld.\n, WSAGetLastError());
// Close the socket
closesocket(ListeningSocket);
// Do the clean up
WSACleanup();
// Exit with error
return 1;
}
else
printf(Server: listen() is OK, I'm listening for connections...\n);
// Set 10 seconds 10 useconds timeout
SelectTiming = recvTimeOutTCP(ListeningSocket, 10, 10);
switch (SelectTiming)
{
case 0:
// Timed out, do whatever you want to handle this situation
printf(\nServer: Timeout lor while waiting you retard client!...\n);
break;
case -1:
// Error occurred, more tweaking here and the recvTimeOutTCP()...
printf(\nServer: Some error encountered with code number: %ld\n, WSAGetLastError());
break;
default:
{
// Accept a new connection when available. 'while' always true
while(1)
{
// Reset the NewConnection socket to SOCKET_ERROR
// Take note that the NewConnection socket in not listening
NewConnection = SOCKET_ERROR;
// While the NewConnection socket equal to SOCKET_ERROR
// which is always true in this case...
while(NewConnection == SOCKET_ERROR)
{
// Accept connection on the ListeningSocket socket and assign
// it to the NewConnection socket, let the ListeningSocket
// do the listening for more connection
NewConnection = accept(ListeningSocket, NULL, NULL);
printf(\nServer: accept() is OK...\n);
printf(Server: New client got connected, ready to
receive and send data...\n);
// At this point you can do two things with these sockets
// 1. Wait for more connections by calling accept again
// on ListeningSocket (loop)
// 2. Start sending or receiving data on NewConnection.
ByteReceived = recv(NewConnection, recvbuff, sizeof(recvbuff), 0);
// When there is data
if ( ByteReceived > 0 )
{
printf(Server: recv() looks fine....\n);
// Some info on the receiver side...
getsockname(ListeningSocket, (SOCKADDR *)&ServerAddr,
(int *)sizeof(ServerAddr));
printf(Server: Receiving IP(s) used: %s\n,
inet_ntoa(ServerAddr.sin_addr));
printf(Server: Receiving port used: %d\n, htons(ServerAddr.sin_port));
// Some info on the sender side
// Allocate the required resources
memset(&SenderInfo, 0, sizeof(SenderInfo));
nlen = sizeof(SenderInfo);
getpeername(NewConnection, (SOCKADDR *)&SenderInfo, &nlen);
printf(Server: Sending IP used: %s\n, inet_ntoa(SenderInfo.sin_addr));
printf(Server: Sending port used: %d\n, htons(SenderInfo.sin_port));
// Print the received bytes. Take note that this is the total
// byte received, it is not the size of the declared buffer
printf(Server: Bytes received: %d\n, ByteReceived);
// Print what those bytes represent
printf(Server: Those bytes are: \);
// Print the string only, discard other
// remaining 'rubbish' in the 1024 buffer size
for(i=0;i < ByteReceived;i++)
printf(%c, recvbuff[i]);
printf(\);
}
// No data
else if ( ByteReceived == 0 )
printf(Server: Connection closed!\n);
// Others
else
printf(Server: recv() failed with error code: %d\n, WSAGetLastError());
}
// Clean up all the send/recv communication, get ready for new one
if( shutdown(NewConnection, SD_SEND) != 0)
printf(\nServer: Well, there is something wrong with the
shutdown(). The error code: %ld\n, WSAGetLastError());
else
printf(\nServer: shutdown() looks OK...\n);
// Well, if there is no more connection in 15 seconds,
// just exit this listening loop...
if( recvTimeOutTCP(ListeningSocket, 15, 0) == 0)
break;
}
}
}
printf(\nServer: The listening socket is timeout...\n);
// When all the data communication and listening finished, close the socket
if(closesocket(ListeningSocket) != 0)
printf(Server: Cannot close \ListeningSocket\ socket. Error code: %ld\n, WSAGetLastError());
else
printf(Server: Closing \ListeningSocket\ socket...\n);
// Finally and optionally, clean up all those WSA setup
if(WSACleanup() != 0)
printf(Server: WSACleanup() failed! Error code: %ld\n, WSAGetLastError());
else
printf(Server: WSACleanup() is OK...\n);
return 0;
}
8. Before we can build this Winsock C Win32 console application project, we need to set the project to be compiled as C code and link to ws2_32.lib, the Winsock2 library. Invoke the project property page.
9. Expand the Configuration folder > Expand the C/C++ sub folder. Select the Advanced link and for the Compile As option, select Compile as C Code (/TC).
10. Next, expand the Linker folder and select the Input link. For the Additional Dependencies option, click the ellipses at the end of the empty field on the right side.
11. Manually, type the library name, ws2_32.lib and click OK Or you can just directly type the library name in the empty field on the right of the Additional Dependencies. Click OK.
12. Build and run the project. If there is no error during the build and run processes, the following shows a sample output that should be expected. Without any connection in 10 seconds and 10 microseconds, the program terminates. We will test this receiver/server program with the sender/client which will be created in the next exercise.
In the meantime, you may want to run the netstat tool, when you run the server program to view the TCP status. In this case we run the following netstat tool which displays all the traffic info for every 5 seconds. You may also redirect the output to a text file using: netstat –a 5 > text_file_name.txt.
Then we run the server program. The status should be in the listening mode on the specified port. You can have more info on the TCP/IP states diagram at Debugging the TCP.
< Client APIs & TCP States | Winsock2 Main | TCP Client-server Examples >