An Intro to Windows Socket (Winsock2) Programming 20

 

 

 

 

 

Testing the UDP Client and select Server Programs in Private Network

 

In this section we try using different hosts in a private network. The receiver IP address is 192.168.1.2 while the sender IP is 192.168.1.1.

 

1.   Firstly, we run the server program and the following screenshot shows a sample when the sender program was already run and completed the connectionless communication.

 

The UDP sender connectionless select Winsock2 and C program: testing the UDP select server in the private network

 

2.   Unblock the Windows firewall if any.

 

The UDP sender connectionless select Winsock2 and C program: unblocking the Windows firewall protection

 

3.      Then we run the sender/client program. The following screenshot shows a sample output.

 

The UDP sender/receiver connectionless select Winsock2 and C program: the UDP sender/client in action in the real private network

 

While in the while loop, the client may also provide 'signal' or message to the server for program termination or to make it more useable, you can provide other user control termination such as using the _fgetchar() function and it is left for you as an exercise. Another improvement that you can implement is providing users with a convenient of supplying the server IP address or domain name and the port number. This simple task can be achieved by manipulating the argc and argv parameters of the main().

 

Message-Based Protocols

 

Just as most connection-oriented communication is also streaming, connectionless communication is almost always message-based. Thus, there are some considerations when you're sending and receiving data. First, because message-based protocols preserve data boundaries, data submitted to a send() function blocks until completed. For non-blocking I/O modes, if a send cannot be completely satisfied, the send function returns with the error WSAEWOULDBLOCK. This means that the underlying system was not able to process that data and you should attempt the send() call again at a later time. The main point to remember is that with message-based protocols, the write can occur as an autonomous action only.

On the flip side, a call to a receive function must supply a sufficiently large buffer. If the supplied buffer is not large enough, the receive call fails with the error WSAEMSGSIZE. If this occurs, the buffer is filled to its capacity, but the remaining data is discarded. The truncated data cannot be retrieved. The only exception is for protocols that do support partial messages, such as the AppleTalk PAP protocol. Prior to returning, the WSARecv(), WSARecvEx(), or WSARecvFrom() functions set the in-out flag parameter to MSG_PARTIAL when it receives only part of a message.

For datagrams based on protocols supporting partial messages, consider using one of the WSARecv* functions because when you make a call to recv()/recvfrom(), there is no notification that the data read is only a partial message. It is up to the programmer to implement a method for the receiver to determine if the entire message has been read. Subsequent calls to recv()/recvfrom() return other pieces of the datagram. Because of this limitation, it can be convenient to use the WSARecvEx() function, which allows the setting and reading of the MSG_PARTIAL flag to indicate if the entire message was read. The Winsock 2 functions WSARecv() and WSARecvFrom() also support this flag. See the descriptions for WSARecv(), WSARecvEx(), and WSARecvFrom() for additional information about this flag.

Finally, let's take a look at one of the more frequently asked questions about sending UDP/IP messages on machines with multiple network interfaces: What happens when a UDP socket is bound explicitly to a local IP interface and datagrams are sent? With UDP sockets, you don't really bind to the network interface; you create an association whereby the IP interface that is bound becomes the source IP address of UDP datagrams sent. The routing table actually determines which physical interface the datagram is transmitted on. If you do not call bind() but instead use either sendto()/WSASendTo() or perform a connect first, the network stack automatically picks the best local IP address based on the routing table. So if you explicitly bind first, the source IP address could be incorrect. That is, the source IP might not be the IP address of the interface on which the datagram was actually sent.

 

Releasing Socket Resources

 

Because there is no connection with connectionless protocols, there is no formal shutdown() or graceful closing of the connection. When the sender or the receiver is finished sending or receiving data, it simply calls the closesocket() function on the socket handle. This releases any associated resources allocated to the socket.

 

Miscellaneous Related Winsock APIs

 

In this section, we'll cover a few Winsock API functions that you might find useful when you put together your own network applications.

 

getpeername()

 

This function is used to obtain the peer's socket address information on a connected socket. The function is defined as:

 

int getpeername(SOCKET s, struct sockaddr FAR* name, int FAR* namelen);

 

The first parameter is the socket for the connection; the last two parameters are a pointer to a SOCKADDR structure of the underlying protocol type and its length. For datagram sockets, this function returns the address passed to a connect() call; however, it will not return the address passed to a sendto() or WSASendTo() call.

 

getsockname()

 

This function is the opposite of getpeername(). It returns the address information for the local interface of a given socket. The function is defined as follows:

 

int getsockname(SOCKET s, struct sockaddr FAR* name, int FAR* namelen);

 

The parameters are the same as the getpeername parameters except that the address information returned for socket s is the local address information. In the case of TCP, the address is the same as the server socket listening on a specific port and IP interface.

 

WSADuplicateSocket()

 

The WSADuplicateSocket() function is used to create a WSAPROTOCOL_INFO structure that can be passed to another process, thus enabling the other process to open a handle to the same underlying socket so that it too can perform operations on that resource. Note that this is necessary only between processes; threads in the same process can freely pass the socket descriptors. This function is defined as:

 

int WSADuplicateSocket(

    SOCKET s,

    DWORD dwProcessId,

    LPWSAPROTOCOL_INFO lpProtocolInfo);

 

The first parameter is the socket handle to duplicate. The second parameter, dwProcessId, is the process ID of the process that intends to use the duplicated socket. Third, the lpProtocolInfo parameter is a pointer to a WSAPROTOCOL_INFO structure that will contain the necessary information for the target process to open a duplicate handle. Some form of interprocess communication must occur so that the current process can pass the WSAPROTOCOL_INFO structure to the target process, which then uses this structure to create a handle to the socket (using the WSASocket() function).

Both socket descriptors can be used independently for I/O. Winsock provides no access control, however, so it is up to the programmer to enforce some kind of synchronization. All of the state information associated with a socket is held in common across all the descriptors because the socket descriptors are duplicated, not the actual socket. For example, any socket option set by the setsockopt() function on one of the descriptors is subsequently visible using the getsockopt() function from any or all descriptors. If a process calls closesocket() on a duplicated socket, it causes the descriptor in that process to become deallocated. The underlying socket, however, will remain open until closesocket is called on the last remaining descriptor.

In addition, be aware of some issues with notification on shared sockets when using WSAAsyncSelect() and WSAEventSelect(). Issuing either of these calls using any of the shared descriptors cancels any previous event registration for the socket regardless of which descriptor was used to make that registration. Thus, for example, a shared socket cannot deliver FD_READ events to process A and FD_WRITE events to process B. If you require event notifications on both descriptors, you should rethink your application design so that it uses threads instead of processes.

 

Windows CE

 

All the information in the preceding sections applies equally to Windows CE. The only exception is that because early Windows CE versions are based on the Winsock 1.1 specification, none of the Winsock 2–specific functions, such as WSA variants of the sending, receiving, connecting, and accepting functions, is available. The only WSA functions available with Windows CE are WSAStartup(), WSACleanup(), WSAGetLastError(), WSASetLastError(), and WSAIoctl(). We have already discussed the first three of these functions; the last will be covered in other chapter.

Windows CE supports the TCP/IP protocol, which means you have access to both TCP and UDP. In addition to TCP/IP, infrared sockets are also supported. The IrDA protocol supports only stream-oriented communication. For both protocols, you make all the usual Winsock 1.1 API calls for creating and transmitting data. The only exception has to do with a bug in UDP datagram sockets in Windows CE 2.0: every call to send() or sendto() causes a kernel memory leak. This bug was fixed in Windows CE 2.1, but because the kernel is distributed in ROM, no software updates can be distributed to fix the problem with Windows CE 2.0. The only solution is to avoid using datagrams in Windows CE 2.0.

Take note that Windows CE does not support console applications and uses UNICODE only. The purpose of our examples is to teach the core concepts of Winsock without having to trudge through code that doesn't relate to Winsock. Unless you're writing a service for Windows CE, a user interface is almost always required. This entails writing many additional functions for window handlers and other user-interface elements, which can obfuscate what we're trying to teach. In addition, there is the dilemma of UNICODE vs. non-UNICODE Winsock functions. It is up to the programmer to decide if the strings passed to the sending and receiving Winsock functions are UNICODE or ANSI strings. Winsock doesn't care what you pass as long as it's a valid buffer. (Of course, you might need to typecast the buffer to silence the compiler warnings.) Don't forget that if you cast a UNICODE string to char*, the length parameter for how many bytes to send should be adjusted accordingly. In Windows CE, if you want to display any data sent or received, you must take into account whether it is UNICODE so that it can be displayed, as all the other Windows system functions do require UNICODE strings. In sum, Windows CE requires a great deal more housekeeping to make a simple Winsock application.

If you do want to run these examples on Windows CE, only a few minor modifications are required for the Winsock code to compile. First, the header file must be WINSOCK.H, as opposed to WINSOCK2.H. WSAStartup() should load version 1.1 because that is the current version of Winsock in Windows CE. Also, Windows CE does not support console applications so you must use WinMain() instead of main(). Note that this does not mean you are required to incorporate a window into your application; it just means you can't use console text I/O functions such as printf().

The Windows CE .NET 4.1 and later implement the Winsock 2.2 and are fully backward compatible with the Winsock 1.1. The Header is Winsock2.h and the Library is Ws2.lib.

 

Conclusion

 

In this chapter, we presented the core Winsock functions that are required for connection-oriented and connectionless communication using the TCP and UDP protocols specifically. For connection-oriented communication, we demonstrated how to accept a client connection and how to establish a client connection to a server. We covered the semantics for session-oriented data-send operations and data-receive operations. For connectionless communication, we also described how to send and receive data. Since this chapter was designed to introduce the core Winsock APIs, we did not address network programming performance considerations. Later, we will address performance issues and introduce the Microsoft Winsock extensions TransmitFile(), TransmitPackets(), AcceptEx(), GetAcceptExSockaddrs(), ConnectEx(), DisconnectEx(), and WSARecvMsg(), which can help you write high performance, scalable Winsock applications. Our discussions so far have demonstrated using Winsock with just the IPv4 protocol. Another complete and advanced Winsock 2 'hands-on' tutorial can be found at: Windows Winsock 2 Network Programming.

 

 

 

 

< Winsock2 19 | Windows Socket 2 (Winssock2) | Win32 Programming | An Intro to Windows Driver Kit (WDK) Programming >