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.
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.