Kevin Gilhooly is with RIS Information Services in Dallas, working on a variety of PC-based client server projects. He can be reached at kjg@dfw.net.
Introduction
Windows Sockets is an open network programming interface for Microsoft Windows. Adapted from Berkely Sockets (which works with UNIX systems), Windows Sockets enables two programs to establish a bidirectional connection. The programs may be on the same or different computers.In this article I present WinJES, a small Microsoft Windows program that lets a user issue a command on a remote Windows machine. WinJES uses the Windows Sockets interface for communications between PCs across a network. The program is both a client and a server, and one copy can exchange messages either with itself or with a copy on a different machine.
I wrote the original WinJES program using Microsoft's Visual C++ 1.1 and NetManage's Chameleon SDK under Windows for Workgroups 3.11. The program uses the Chameleon TCP/IP stack. I've also compiled it under the Windows NT 3.5 Workstation's native TCP/IP, with Visual C++ version 2.0. Any Windows-Sockets-compliant developer's kit and Windows C compiler should be able to compile the source code.
Establishing a Protocol
To communicate with each other, two programs must use a protocol. Windows Sockets programs use either the Transmission Control Protocol (TCP) or User Datagram Protocol (UDP) (see sidebar, "Understanding Sockets and Protocols."). WinJES uses UDP. Unlike TCP, two machines communicating via UDP do not establish a "connection." Rather, they send messages back and forth without acknowledgements. When WinJES starts, it listens for requests from other machines. A user on a remote machine can then submit commands to be executed on the local machine. In many environments, a program can simply wait for input from a remote partner. In UNIX, or on a dedicated DOS machine, a standard UDP server program with no error checking would look like Listing 1.For various reasons, a Windows 3.x server cannot be quite so simple. For one thing, because Windows 3.x is non-preemptive, programs must yield the processor to Windows after a reasonable time, or no other processes will be able to run. Also, Windows is based on a messaging model. When any event occurs, Windows sends a message to a window procedure (code tied to a particular window); the code must process the message as quickly as possible and return to a passive state. Therefore, the above code must be modified for Windows.
WinJES does the real work inside a message loop. Before entering a message loop, the program must register a window class and create a window. The window class ties the window to a window procedure (the code that processes its messages.) Since WinJES uses the Windows Sockets functions, it must initialize Windows Sockets at the start of the program (via function WSAStartup), and terminate Windows Sockets when the program exits (via function WSACleanup). Listing 2 shows the main routine for WinJES, which creates the main window, and enters the message loop.
Processing Windows Messages
In Windows, a window procedure processes Windows messages dispatched from the message loop. To create a window procedure for WinJES, I've added processing for specific messages that I care about. All other messages go to DefWindowProc, the default Windows procedure. The window procedure is shown in Listing 3. A description of the critical messages processed follows:In the WM_CREATE message:
open a socket()
bind() it to an address
WSAAsyncSelect() defines a message and events to filter
In the WM_CLOSE or WM_DESTROY message:
close the socket
In the defined socket message:
switch on wParam (a Windows message parameter) to determine the socket
switch on the low word of lParam (another Windows message parameter) to determine the event
This is a standard model for Windows Sockets servers. WSAAsyncSelect links a list of events on a specific socket to a window and establishes the message to send to that window when one of the events occurs. When the message is posted, the window procedure can look at the low word of lParam to determine the specific event that occurred. The specific socket ID is passed as the wParam. This scheme allows a single message routine to service multiple requests on multiple sockets.
The window procedure for the WinJES program is very simplistic. The user interface is a single-line display, filled from a global variable in response to the WM_PAINT message. I've added the menu item to submit commands to other WinJES servers to the Windows system menu, so the WinJES applicaton will not need a menu of its own.
From a communications standpoint, the most important message is WM_SOCKET, a user-defined message which is defined in the file WINJES.H (on this month's code disk). All private messages must be defined above WM_USER (a constant in WINDOWS.H). The WM_SOCKET message is referenced in WSAAsyncSelect, so the Windows Sockets Dynamic Link Library (DLL) posts it when a socket event occurs.
Because of the Intel architecture, a Windows 3.1 program must deal with a two-part memory address: segment and offset. Handling the address correctly is critical when passing parameters to code outside the program. All the parameters passed from a Windows program to the Windows Sockets DLL routines must either be FAR or cast as a FAR when passed. Copying memory also requires care. In a UNIX or Windows NT program, the usual way to copy from one address to another is memcpy. In Windows 3.1 or Workgroups, programs must use _fmemcpy, which is an address-independent version. It will correctly handle far pointers.
WinJES Server Processing
The server portion of WinJES waits for a message to arrive, then receives and parses it. The server extracts the command parameter from the structure, and passes it (untouched) to the WinExec API for execution. WinExec takes two parameters, a command string and a visibility option. WinJES runs all programs as SW_NORMAL, which means they are visible on the screen and receive focus when they start. The command string may be a Windows or DOS program name, plus any parameters the program needs.
Preparing a socket for input
All socket programs begin by allocating a socket for use. A server continues by binding the socket to a specific port, and monitoring the port for messages. This process occurs in response to a Windows WM_CREATE message. (The code is shown under the WM_CREATE case of the window procedure's outermost switch statement.) A Windows Sockets program listens by asking the Windows Sockets DLL to post a message whenever a defined event occurs. WinJES asks the DLL to post a WM_SOCKET message whenever the socket is ready to read. "Ready to read" means that the program can perform a receive on the socket and the function will not hang. In this UDP program, "ready to read" is the only event a server cares about.
Receiving a message
When the socket receives data, the Windows Sockets DLL posts a WM_SOCKET message, as requested in the WSAAsyncSelect call. (Refer to the WM_SOCKET case clause in the window procedure.) The server can look at the long parameter's low word to determine the event type. In WinJES, the only critical event is FD_READ, which occurs when a request is ready for processing. The parameter will match one of the events defined in the WSAAsyncSelect call. Only those requested events will trigger the message to the window.
WinJES Client Processing
Sending a UDP message is very simple: grab a socket, point it at the remote machine's address and the program's port, and toss it out on the network. A UDP client will not know if the message arrives unless the server program acknowledges it. In WinJES, a UDP message is sent in response to a WM_COMMAND message generated by a menu selection. (Refer to case IDM_SEND under case WM_COMMAND in the main window procedure.) This code fragment will work for many command-line clients, such as a UNIX machine, by moving it and recompiling it almost as is (removing Windows-specific calls). The UNIX WinJES client would receive the host name as a command-line parameter and use gets to read the command to issue, and then runs the (nearly) identical socket code for communications.
Conclusion
The Windows Sockets interface is a powerful addition to basic Windows programming. It extends Windows programs to provide communications over an internet easily, while providing a reasonable porting path for TCP/IP code from other platforms.