In order to demonstrate how Windows Sockets might really work in a real application, we developed WormHole. WormHole is a peer Windows Sockets application which allows users to establish network "wormholes" between systems and then drag and drop files to one another. The WormHole program is a simple MDI (multiple document interface) application which runs as a 16-bit application on Windows 3.1 systems with a Windows Sockets compliant TCP/IP implementations. WormHole can be conditionally compiled as a 32-bit Windows NT application which will run over Windows NT's 32-bit TCP/IP transport and 32-bit Windows Sockets interface. The makefile explains how to compile this application for Windows NT.
In order to demonstrate as many Windows Sockets concepts as possible, WormHole utilizes both stream and datagram sockets, and is completely event-driven (asynchronous). This allows a WormHole to simultaneously service multiple client connections and act as a WormHole client. "Host windows" are created when a user specifies a destination system (either by IP address or hostname) to connect with. Specifying a remote system does not establish a network connection, it simply creates a host window on the client to provide feedback during file transfers.
"Wormholes" (connections) are established every time a user initiates a file transfer by dragging a file from the file manager into a host window. We refer to the system on which this happens as the WormHole client. The WormHole application implements a very simple protocol which would not be entirely uncommon in a production environment. The wormhole setup takes place over a simple pair of datagram frame transactions, followed by stream connection establishment which facilitates the file transfer. The following diagram illustrates the transactions for connection establishment. Remember that since WormHole is a peer application, every instance of WormHole on the network is capable of acting as both a WormHole client and a WormHole server simultaneously.
The WormHole protocol
The wormhole establishment begins on the client side by sending a FILE frame datagram which specifies to the server the name of the file to transfer, the length of the file, and a unique transaction identifier (xid). The xid allows the server to distinguish between multiple outstanding FILE requests. The server acknowledges the FILE request with either a PORT frame or a NACK frame. A PORT frame informs the client that the server will accept an incoming file, returning a specified stream socket port for the client to connect to as well as the xid specified in the original request. If the server wishes to refuse the FILE request, it may respond with a NACK (negative acknowledgment) frame which contains the xid it is refusing and optionally an error code (for example, "insufficient disk space", "duplicate filename").
Upon receipt of a PORT frame, the client establishes a stream socket connection to the specified port and begins to transfer the file. Since the server is listening on a port it created in association with a particular transaction, and it knows the file name and length, it is not necessary to transfer anything aside from the actual file data. Once the entire file has been received by the server, the stream connection is shut down gracefully.
WormHole Protocol Frame Types and Contents
Datagram Connection Request |
Datagram Connection Acknowledge |
Datagram Connection Refuse |
FILE |
PORT |
NACK |
Transaction ID |
Transaction ID |
Transaction ID |
File length |
Port Number |
Error code (optional) |
File name |
Because the connection request (FILE), acknowledgment (PORT) and refuse (NACK) frames are all transmitted on the network using an unreliable datagram protocol, it is quite possible for these frames to be lost in the shuffle of network activity. This said, the WormHole protocol also implements some retry timers to allow a client to retry a failed connection request. A WormHole client will retry a connection request by retransmitting up to four FILE frames to the server. To keep things simple, the server does not implement a retry timer on possible lost PORT or NACK frames. We instead rely on the client to resend a FILE request in the event that a PORT frame gets lost.
Since the server creates a local window, transaction association, and the like upon receipt of a FILE frame, it does maintain a timer per file transaction to allow for the cleanup of acknowledgment (PORT) frames which go unanswered. Finally, since the file transfer itself takes place over reliable stream sockets, it is not necessary to implement these types of precautions for the actual file transfer. WormHole simply relies on the failure of the send() and recv() APIs in the event of network or connection problems.