Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

.NET

Untangling the Windows Sockets API


FEB93: UNTANGLING THE WINDOWS SOCKETS API

UNTANGLING THE WINDOWS SOCKETS API

A standardized interface for network development

Mike Calbaum, Frank Porcaro, Mark Ruegsegger, Bruce Backman

The authors are engineers at Frontier Technologies Corp. and be contacted at 10201 North Port Washington Road, Mequon WI 53092 or at [email protected].


The Windows Sockets API is an open, standard programming interface for developing TCP/IP network applications for Microsoft Windows. As a standardized programming interface, the API allows you to develop one application that will run unmodified over any TCP/IP network stack with a Windows Sockets-compliant API. Before the standard, you needed to develop a version of your application for every implementation of TCP/IP on which you wished to run the application. The Windows Sockets API is implemented as a Windows dynamic link library (DLL) with standardized function interfaces.

The Windows Sockets API idea was conceived at a Birds Of A Feather session held at Interop '91. After numerous drafts and revisions, the final specification was released for implementation by TCP/IP vendors. The API has its origins in the socket interface first distributed with 4.1cBSD UNIX for the VAX in 1982 and updated with the 4.3BSD VAX release in 1986. The original BSD sockets interface supported UNIX domain sockets for interprocess communication on a single host, TCP/IP domain sockets, and Xerox XNS domain sockets. The Windows Sockets API currently only supports TCP/IP networks.

The sockets interface removes from you the responsibility of knowing the details of the underlying network and bundles the details of network programming into a handy abstraction called a socket. A socket is one endpoint in a network-communication path. Sockets are differentiated from each other by names that consists of the local-host network address and a port number. You can either select the port number or let the sockets interface select one. The available port numbers are: 1-1023, which are reserved for standard TCP/IP server applications (FTP, rwho, finger, and the like); 1024-5000, which can be assigned to a socket by the system; and 5001, and above, which are available for you to assign to your sockets.

The sockets interface provides a means for both reliable, connection-oriented stream communications and unreliable, connectionless datagram-oriented communications. Reliable, connection-oriented communication is provided by SOCK_STREAM-type sockets. Applications communicating through stream sockets have a socket in each application that is "connected" to the socket in the other application. When a socket is connected to another socket, it can only send data to the socket on the other endpoint of the connection and can only receive data from that socket. Stream-type sockets use a reliable data-transmission protocol (usually TCP) to provide reliable communications with a peer application. A reliable transmission protocol guarantees that data will arrive at its destination uncorrupted and in the same order it was sent. If data is lost or corrupted, it will be retransmitted until a successful reception of the data is acknowledged by the receiving host. A stream socket provides bidirectional communication with its peer socket, which means that data can be sent and received on the same socket. The data in a stream socket is sent and received as a continuous stream of bytes with no record delimiters. If an application sends different types of data through a single socket, it must provide record delimiters or closely coordinate the order of the data transmitted between the two applications.

Most distributed applications use a client/server model, in which one application acts as a server, accepting requests for services from client applications, processing the requests, and then returning the results. This model is particularly well suited to stream sockets, where one application must be listening for incoming connections and the other must attempt to connect. A typical client/server interaction using stream sockets follows the line of execution shown in Figure 1.

The sockets interface can also provide connectionless datagram-oriented communications through SOCK_DGRAM-type sockets. Datagram sockets do not need to be connected to another socket and can therefore send data to and receive data from multiple sockets. Datagram sockets send and receive their data as self-contained packages with one package of data sent or received at a time, rather than as a continuous stream of data bytes. Datagram sockets use an unreliable transmission protocol (usually UDP) to transmit their data. Since the protocol is unreliable, lost data will not be retransmitted, and data can arrive in a different order than it was transmitted. Datagram sockets can broadcast messages across the local network to be picked up by any application receiving datagrams on the port to which it was broadcast.

The sockets interface also provides functions to associate user-readable host names with network addresses, network addresses with host names, server names with port numbers, and the like. These functions--gethostbyname(), gethostbyaddress(), getservbyport(), and so on--are collectively known as the getXbyY functions and may be implemented such that they search a local database file for the information, request it from a server on the same network, or both.

The Berkeley sockets interface has some major shortcomings when implemented for Windows. Potentially time-consuming operations (like waiting for an incoming connection or doing a host-name lookup) will block the execution of the application until they're finished. In Windows, this will cause the application's user interface to be unresponsive until the operation is completed, preventing the user from aborting any pending operations. Also, the select() function, which can be used to test a socket for outstanding, incoming, connections, readability, writability, and the like must be called repeatedly in a polling fashion until the desired condition occurs.

To overcome these shortcomings, the authors of the Windows Sockets Specification extended the Berkeley definitions to include asynchronous versions of the select() and getXbyY functions and a function to abort any blocking operation. Rather than block execution until finished, the asynchronous functions return immediately and post messages to an application window when the select function detects the desired condition or when the getXbyY request has been resolved.

Developing a Windows Sockets Application

To illustrate the use of the Windows Sockets API to develop a network application, we'll build a program that implements some of the standard UNIX network utilities. We'll develop an application that implements the finger utility as an example of a stream-oriented client application, and the fingerd server as an example of a stream-oriented server application. Listing One (page 96) lists sock.c, Listing Two (page 99) is dlg.h, and Listing Three (page 99) is socket.rc. All Windows Sockets applications must include the file winsock.h. All the related data structures are defined in this file, and all the Windows Sockets functions have prototypes in this file.

After doing the standard Windows startup tasks, registering our window class, creating the window, and so on, the application must first call the Windows Sockets function WSAStartup() and pass it two parameters: 1. A WORD whose low-order byte is equal to the major version number and whose high-order byte is the minor revision number of the Windows Sockets version needed to support this application; and 2. a pointer to a WSADATA structure. The WSAStartup() function will return 0 if the Windows Sockets DLL can support the version of the specification that the application requires and initialization succeeded; otherwise it will return an error code. If a 0 is returned, the WSADATA structure is filled with information about the Windows Sockets DLL. If WSAStartup() succeeds, we start executing the application message loop.

When we exit the application message loop, we know that the application is shutting down so we close any open sockets and call the WSACleanup() function to advise the Windows Sockets DLL that we are going away and it can clean up any memory allocations that support the application.

The WinMain() function is fairly straightforward: We do our standard Windows initializations, call WSAStartup() to register our application with the Windows Sockets DLL, perform the application message loop, close any open sockets, and call WSACleanup() before exiting.

The main window procedure does all of the standard window-procedure processing: handling keyboard input, creating windows, updating and responding to menu selections, and responding to close messages.

The application window has one menu, the Sockets menu, which has five menu items: Finger Client, Finger Server, Cancel Operation, About, and Exit. About and Exit are standard items for a Windows application and are handled in the standard way. About displays a message box containing information about the application; and Exit posts a close message to the window to end execution of the application. The remaining three menu items use the Windows Sockets API to implement the UNIX network utilities finger and fingerd.

A Sample Client Program

The finger client utility connects to a remote host and sends it the user name of a person on that system; the finger server accepts the connection, reads the user name from the socket, looks up information about that user, and sends the information back to the client.

The first step is to develop the client finger application. If you have a network that contains a UNIX host, you'll be able to use the client application to get information about users on that host; otherwise, you'll need to develop the finger-server portion of the application and run it on another machine to test your application.

When the user selects the Finger Client menu item, the program prompts for the name of the remote host to query. If the user enters a host name and presses the OK button, the Windows Sockets function gethostbyname() is called, passing the host name as a parameter. (See lines 184-194 in Listing One.) If gethostbyname() successfully locates information about a host with this name, it will return a pointer to a hostent structure containing the information. If it cannot find information about this host, it will return NULL. If gethostbyname() returns a non-NULL pointer, we copy the hosts network address from the h_addr field of the hostent structure into the sin_addr.s_addr field of a sockaddr_in structure. For TCP/IP, the address will always be four bytes long, but to be safe, convention dictates that we examine the h_length field of the hostent structure and copy that many bytes into the sockaddr_in structure. The pointers returned by the getXbyY functions point to static memory areas that may be reused by the Windows Sockets DLL for other information; therefore, all the information we need from the structure should be copied before any subsequent Windows Sockets functions are called. If gethostbyname() returns NULL, we display a message box for the user telling them we do not know this host.

If the network address of the remote host was located successfully, the program prompts the user for the user name for which they would like information. Entering a blank user name is not an error; the finger server considers a blank user name to be a request for information on all users currently logged in to the remote host, as in lines 195-197 of Listing One.

If we have not previously looked up the port number used by the finger server, we now obtain it using getservbyname() (see Listing One, lines 198-209) which takes two strings as parameters, the server name and the protocol name. We pass in "finger" and "tcp" as the parameters. getservbyname() returns a pointer to a servent structure if successful and NULL if unsuccessful. If we receive a non-NULL pointer, we copy the port number from the servent structure into a global variable. TCP/IP networks use all numbers in a byte order opposite that used by the Intel and DEC VAX processors. When providing numbers to the sockets interface or getting them from the sockets interface, we need to convert the byte order. Sockets provides utility functions to convert the byte order of the numbers. ntohs is used to convert a short from network to host byte order; htons converts a short from host to network byte order. ntohl and htonl serve the same purpose for longs. Even if the host for which your application is targeted uses the same byte ordering as the network, you should use these functions for the sake of portability. If we receive a NULL pointer from getservbyname(), we alert the user with a message box and exit the switch statement.

If everything has been successful so far, we next create a socket by calling socket(), as in lines 210-228 of Listing One. If the socket function fails to allocate a socket for us, it will return INVALID_SOCKET; otherwise it will return a socket descriptor. If we get a valid socket descriptor, we finish filling in the sockaddr_in structure with the address of the socket to which we wish to connect. We have already filled in the host address, so we now fill in the address family, which is always PF_INET for TCP_IP and the port number in network byte order. We then call the connect function to connect to the finger-server socket. The connect function will return O on success and nonzero on failure. The return value of all Windows Sockets functions should be checked to detect any failures. If any Windows Sockets call fails, we can call the WSAGetLastError() function to determine a specific error code. The sockaddr_in structure holding the servers address is cast to type struct sockaddr when passed to most sockets calls. This is a holdover from the Berkeley interface that supported both UNIX domain sockets and TCP/IP sockets.

If we successfully connect to the finger server, we concatenate a carriage return/line feed onto the user name (refer to lines 231-235) and send the whole string to the finger server using the send function. The carriage return/line feed has no special meaning for the sockets DLL, but is used by the server to detect the end of the user name.

When the finger server receives the user-name string, it looks up information on the specified user and sends it to the finger client on the same socket. We read the data from the socket into a global buffer and invalidate the window rectangle so the application will receive a WM_PAINT message and update the window with the finger data; see lines 237-250 in Listing One.

We then close the socket with the closesocket() function and return to the application message loop.

A Sample Server Program

The finger server creates a socket and binds it to a well-known port number. It then waits for clients to connect to the socket, and when a connection is made, it reads a carriage return/line feed terminated string from the socket. The string is the name of a user about whom the client would like information. The server looks up information about the user and sends it to the client on the socket created when the connection was accepted.

The Finger Server menu selection is implemented as a server that can be toggled off and on. When the server is active and accepting connections, a check mark is placed next to the menu item.

To create a server using sockets, we first create a socket with socket() and then bind a name to it. We bind a name to the socket by filling in a sockaddr_in structure with the desired name and calling the bind() function; see lines 272-295 in Listing One. The sockaddr_in structure is filled in the same manner as it was for the finger client with one notable exception: We do not need to supply the network address of the server's host. If we fill the sin_addr.s_addr field of the socket address with the constant INADDR_ANY, the sockets DLL will determine and fill in the local-host network address for us. For the finger server, we are binding the socket to a port number found using getservbyname(). If we did not care what port number was assigned to this socket we could use 0 as the port number and have the sockets DLL supply us with an available port number. If a port number is specified and another socket is using this port number, bind() will fail.

The next step to creating a server is to call the listen() function, as in lines 296-308 of Listing One. This function puts the socket into a state in which it will accept incoming connections from clients. The listen() function takes as parameters the socket descriptor and an integer indicating the number of backlogged connections the sockets DLL should allow to accumulate. The sockets DLL will allow at most a backlog of five connections. Even if you request a larger backlog, you will only get five. If the listen call succeeds, we call WSAAsyncSelect() to notify the sockets DLL that we wish to receive a window message whenever a connection is ready to be accepted for this socket. The WSAAsyncSelect() function takes as parameters a socket descriptor, a handle to a window to which the messages should be posted, the message to post, and the conditions in which the message should be posted. If WSAAsyncSelect() succeeds, we place a check mark next to the Finger Server menu item to indicate that the server is functioning and then return to the application message loop and wait for a connection message.

Once we have told the sockets DLL that we want to be notified of incoming connections for our server, we wait and process window messages until a message is received notifying us that a connection has been established and can be accepted by our application. The message we'll receive is the message that we passed as a parameter to WSAAsyncSelect(). In this case we have defined a constant named CONN_MSG and asked for this message to be posted to our application window when a connection is ready. When we receive a CONN_MSG message, we use the WSAGETSELECTERROR() macro (lines 331-340 in Listing One) to extract any error code from lParam. If there was no error, we call accept() to accept the connection. If accept() succeeds, it will return a new socket descriptor, which we will use to communicate with the client. A new socket descriptor is returned so that the original socket which we are using to listen for connections can remain in a listening state, accepting connections from clients. If the accept function fails, it will return the constant INVALID_SOCKET.

If we receive a valid socket descriptor from the accept function, we first call recv() (Listing One, lines 343-354) to read the user name string from the client. In our server we ignore the string, but we do check the return value from recv() to detect any errors. If there was no error, we call send() to send the client the string entered by the user as their real name. We then close the received socket and return to the application message loop to wait for more connections.

If we already have a finger server listening for clients when the user selects the Finger Server menu item, we close the finger-server socket and remove the check mark from the menu.

If we receive a valid socket descriptor from the accept function, we call WSAAsyncSelect on our new socket to notify the socket's DLL that we wish to be informed when data arrives for this socket. We then wait and process window messages until a message is received notifying us that data has arrived. We then call recv() (Listing One, lines 346-353) to read the user-name string from the client.

Summary

Together, the finger client and finger server implement the UNIX finger utility and provide a fairly complete example of network application development using the Windows Sockets API. Developers new to the sockets interface should read the appropriate chapters in UNIX Network Programming, by W. Richard Stevens (Prentice-Hall, 1990). Anyone using the Windows Sockets API should get the Windows Sockets Specification by Martin Hall, Mark Towfiq, Geoff Arnold, David Treadwell, and Henry Sanders available through anonymous ftp from vax.ftp.com and ftp.uu.net or downloaded from CompuServe from the Microsoft Software Libraries (GO MSL). Any comments or questions about this article can be sent to tcp@ frontiertech.com.


_UNTANGLING THE WINDOWS SOCKETS API_
by Mike Calbaum, Frank Porcaro, Mark Ruegsegger, and Bruce Backman



[LISTING ONE]
<a name="008e_000c">

/*
 * Sock.c -- Windows sockets sample application
 *
 * Implements psuedo finger client and server.
 * Should work with any UNIX finger daemon
 *
 */



#include "windows.h"
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <winsock.h>
#include "dlg.h"


#define PROMPT_STRING       130
#define PROMPT_TEXT         131

#define CONN_MSG        (WM_USER+25)
#define   FNGR_READ      (WM_USER+26)

extern void errormsg(LPSTR cp);
extern int PASCAL WinMain(HANDLE, HANDLE, LPSTR, int);
extern BOOL InitApplication(HANDLE);
extern BOOL InitInstance(HANDLE, int);
extern long FAR PASCAL MainWndProc(HWND, unsigned, WORD, LONG);
extern BOOL FAR PASCAL About(HWND, unsigned, WORD, LONG);
extern long FAR PASCAL SockWndProc(HWND hWnd, unsigned message, WORD wParam, LONG lParam);
extern char *prompt(HWND hWnd, HANDLE hInst, char *message, char *buf);


/*  Global buffers  */
char buff[4096];
char msg[80], name[125];

HANDLE hInst;
HWND hWnd;
struct sockaddr_in anaddr;
int whoport, fngrport;      /*  Well known port numbers  */
int app_closing;

/*  Sockets  */
SOCKET fclient = INVALID_SOCKET;
SOCKET fserver = INVALID_SOCKET;
SOCKET fconnect = INVALID_SOCKET;


/*
 * standard windows initialization routines
 */
BOOL InitApplication(HANDLE hInstance)
    {
    WNDCLASS  wc;

      buff[0] = '\0';

    wc.style = NULL;
    wc.lpfnWndProc = SockWndProc;

    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName =  "SockMenu";
    wc.lpszClassName = "SockWClass";

    return(RegisterClass(&wc));
    }

BOOL InitInstance(HANDLE hInstance, int nCmdShow)
    {

    hInst = hInstance;

    /* Create a main window for this application instance.  */

    hWnd = CreateWindow("SockWClass", "Sockets Sample Application",
                      WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
                      CW_USEDEFAULT, CW_USEDEFAULT,
                      CW_USEDEFAULT, NULL, NULL, hInstance, NULL);

    if (!hWnd)
       return (FALSE);

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    return (TRUE);
    }


int PASCAL
WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    {
    MSG msg;
    FARPROC lpfn;
    WSADATA wskData;

    if (!hPrevInstance)
      if (!InitApplication(hInstance))
         return(FALSE);

    if (!InitInstance(hInstance, nCmdShow))
      return(FALSE);

    /*
     * register application with windows sockets dll
     */

   if(!WSAStartup(1, &wskData) && (wskData.wVersion == 1))
      {

       while (GetMessage(&msg, NULL, NULL, NULL))
         {
         TranslateMessage(&msg);
         DispatchMessage(&msg);
         if (app_closing)
            {
            /* The application has been requested to shut down. Continue processing
            * the close request by calling DestroyWindow  */
            DestroyWindow(hWnd);
            }
         }

      /* Close any open sockets */
      if(fclient != INVALID_SOCKET)
         closesocket(fclient);
      if(fserver != INVALID_SOCKET)
         closesocket(fserver);
      if(fconnect != INVALID_SOCKET)
         closesocket(fconnect);

      /*  Let windows sockets know we're done */
       WSACleanup();
      return(msg.wParam);
       }

    return(0);
    }


BOOL FAR PASCAL About(HWND hDlg, unsigned message, WORD wParam, LONG lParam)
    {
    switch (message)
       {
          case WM_INITDIALOG:
            return (TRUE);

          case WM_COMMAND:
            if (wParam == IDOK || wParam == IDCANCEL)
               {
               EndDialog(hDlg, TRUE);
               return (TRUE);
               }
            break;
       }
    return (FALSE);
    }


long FAR PASCAL
SockWndProc(HWND hWnd, unsigned message, WORD wParam, LONG lParam)
    {
    int len, cnt;
    RECT wRect;
    PAINTSTRUCT ps;
    HDC hdc;
    struct hostent FAR *hp;
    static HANDLE arrow, hrGlass, hMenu;

    switch (message)
      {
      case WM_CREATE:
         hMenu = GetMenu(hWnd);
         arrow = LoadCursor(NULL, IDC_ARROW);
         hrGlass = LoadCursor(NULL, IDC_WAIT);
         SetCursor(arrow);
         break;

       /*
        * If user presses control-c, interpret as an interrupt request.
        * Could also provide a menu item or a dialogbox with a "cancel"
        * button.
        *
        */
       case WM_CHAR:
         if (wParam == 3/* ctrl-c */)
            {
            WSACancelBlockingCall();
            strcpy(buff, "Call cancelled!!");
            InvalidateRect(hWnd, NULL, 1);
            }
         else
            return(DefWindowProc(hWnd, message, wParam, lParam));
         break;

        /* Update the window */
        case WM_PAINT:
           hdc = BeginPaint(hWnd, &ps);

           /* Use fixed fontand expand tabs so tabs align */
           SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
           GetClientRect(hWnd, &wRect);
           DrawText(hdc, buff, -1, &wRect, DT_LEFT | DT_EXPANDTABS);
           EndPaint(hWnd, &ps);
           break;

        /* Menu item selected, wParam == menu item ID  */
       case WM_COMMAND:
         switch (wParam)
            {
            /* Display About Box */
            case IDM_ABOUT:
                {
                FARPROC lpfn;

                lpfn = MakeProcInstance(About, hInst);
                DialogBox(hInst, "AboutBox", hWnd, lpfn);
                FreeProcInstance(lpfn);
                break;
                }

                /* Finger client utility */
            case FINGER:
                   /*  Get user name  */
                   if(prompt(hWnd, hInst, "Hostname:", msg) == NULL)
                      break;

                    /*  Look up the host address  */
                    if((hp = gethostbyname(msg)) == NULL)
                       {
                       errormsg("Unknown host");
                       break;
                       }
               _fmemcpy((char FAR *) &anaddr.sin_addr.s_addr, hp->h_addr, hp->h_length);

                    /*  Get username to finger */
                    if(!prompt(hWnd, hInst, "Finger User:", msg))
                       strcpy(msg, "");

               /*  Look up port number */
               if(fngrport == 0)
                  {
                  struct servent FAR *sp;

                  if((sp = getservbyname("finger", "tcp")) == NULL)
                     {
                     errormsg("Cannot determine port number for finger daemon.");
                     break;
                     }
                  fngrport = htons(sp->s_port);
                  }

                    /* create socket */
               if((fclient = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
                  {
                  errormsg("Unable to create a socket");
                  }
               else
                  {
                  /*  Fill in address to which we will connect */
                  anaddr.sin_family = PF_INET;
                  anaddr.sin_port = htons(fngrport);

                  /*  try to connect */
                  if(connect(fclient, (struct sockaddr FAR *) &anaddr, sizeof(struct sockaddr_in)))
                     {
                     errormsg("Unable to connect to finger daemon.");
                     closesocket(fclient);
                     fclient = INVALID_SOCKET;
                     }
                  else
                     {
                     strcat(msg, "\r\n");
                     if(send(fclient, msg, strlen(msg), 0) < (int) strlen(msg))
                        {
                        errormsg("Error sending string to finger daemon");
                        }
                     else
                        if((cnt = recv(fclient, buff, sizeof(buff), 0)) == SOCKET_ERROR)
                           {
                           errormsg("Error reading data from daemon");
                           }
                        else
                           if(cnt == 0)
                              errormsg("No data received from finger daemon");
                                   else
                                      {
                              buff[cnt] = '\0';
                              InvalidateRect(hWnd, NULL, 1);
                              }
                     }
                  closesocket(fclient);
                  fclient = INVALID_SOCKET;
                  }
               break;

            case FINGER_SRV:
               /*  Look up port number */
               if(fngrport == 0)
                  {
                  struct servent FAR *sp;

                  if((sp = getservbyname("finger", "tcp")) == NULL)
                     {
                     errormsg("Cannot determine port number for finger daemon.");
                     break;
                     }
                  fngrport = htons(sp->s_port);
                  }

                    /* if not acting as server, start */
               if(fserver == INVALID_SOCKET)
                  {
                      /*  Get user name  */
                      if(prompt(hWnd, hInst, "Real Name:", msg) == NULL)
                         break;
                      else
                         wsprintf(name, "In Real Life: %s\r\n", (LPSTR) msg);

                  /*  allocate socket  */
                  if((fserver = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
                     {
                     errormsg("Cannot allocate socket for finger server");
                     break;
                     }

                  /* Bind our socket to the finger port number */
                  anaddr.sin_port = htons(fngrport);
                  anaddr.sin_addr.s_addr = INADDR_ANY;
                  anaddr.sin_family = PF_INET;
                  if(bind(fserver, (struct sockaddr FAR *) &anaddr, sizeof(anaddr)))
                     {
                     errormsg("Error binding to finger daemon port");
                     closesocket(fserver);
                     fserver = INVALID_SOCKET;
                     break;
                     }

                   /*  listen for connections and ask for a message when they come in */
                  if(listen(fserver, 5) || WSAAsyncSelect(fserver, hWnd, CONN_MSG, FD_ACCEPT))
                     {
                     errormsg("Error trying to listen on finger server socket");
                     closesocket(fserver);
                     fserver = INVALID_SOCKET;
                     }
                  else
                     CheckMenuItem(hMenu, FINGER_SRV, MF_BYCOMMAND | MF_CHECKED);
                  }
               else    /*  Already acting as server, stop  */
                  {
                  closesocket(fserver);
                  fserver = INVALID_SOCKET;
                  CheckMenuItem(hMenu, FINGER_SRV, MF_BYCOMMAND | MF_UNCHECKED);
                  }
               break;

                case CANCEL:   /* Cancel any outstanding blocking call (send or recv) */
                   WSACancelBlockingCall();
               strcpy(buff, "Call cancelled!!");
               InvalidateRect(hWnd, NULL, 1);
                   break;

            case QUIT:
               SendMessage(hWnd, WM_CLOSE, 0, 0L);
               break;

            default:
                return (DefWindowProc(hWnd, message, wParam, lParam));
            }
      break;

        case CONN_MSG:      /* Connection on finger server  */
           if(WSAGETSELECTERROR(lParam))
              errormsg("Error listening for finger connections");
           else
              {
              /* accept connection */
              len = sizeof(struct sockaddr_in);
              fconnect = accept(fserver, (struct sockaddr FAR *) &anaddr, &len);
              if(fconnect == INVALID_SOCKET)
                 errormsg("Error accepting connection to finger server");
              else
                 WSAAsyncSelect(fconnect, hWnd, FNGR_READ, FD_READ);
              }
           break;

        case FNGR_READ:   /*  Data available for reading  */
          {
          /* The finger client will send us a string but we will ignore it */
          cnt = recv(fconnect, buff, sizeof(buff), 0);
          buff[0] = '\0';
          if(cnt == SOCKET_ERROR)
             errormsg("Error reading from finger client");
          else
             if(send(fconnect, name, strlen(name), 0) < (int) strlen(name))
                errormsg("Error sending data to finger client");

          /* Close up the accepted connection */
          closesocket(fconnect);
          fconnect = INVALID_SOCKET;
          }
           break;

       case WM_CLOSE:
         /* Application's main window is closing. Set flag to notify main loop
          * The main function is responsible for handling the close
          */
         app_closing = 1;
         break;

       case WM_DESTROY:
         PostQuitMessage(0);
         break;

       default:
         return (DefWindowProc(hWnd, message, wParam, lParam));
       }
    return (NULL);
    }



char PromptText[41];       /* prompt message text for prompt dlg */
char PromptString[81];     /* user's input from prompt dlg */

/*
 * PromptDlgProc - Processes the "prompt" dialog box
 *
 * globals:  PromptText - prompt message text
 *           PromptString - user's input
 * return: IDOK or IDCANCEL
 */
BOOL FAR PASCAL
PromptDlgProc(HWND hDlg, unsigned message, WORD wParam, LONG lParam)
    {
    switch (message)
       {
       case WM_COMMAND:
         if (wParam == IDOK)
            {
            GetDlgItemText(hDlg, PROMPT_STRING, PromptString, 80);
            EndDialog(hDlg, IDOK);
            return TRUE;
            }
         else if (wParam == IDCANCEL)
            {
            EndDialog(hDlg, IDCANCEL);
            return TRUE;
            }
            return FALSE;
         break;

       case WM_INITDIALOG:
         SetDlgItemText(hDlg, PROMPT_TEXT, PromptText);
         return(TRUE);
         break;

       default:
         return FALSE;
       }
    }

/*
 * prompt - displays message, copies user response into buf
 *
 * return: NULL for cancel, or FAR *  to buf
 */
char *prompt(HWND hWnd, HANDLE hInst, char *message, char *buf)
    {
    int rc;
    FARPROC   lpDlgProc;

    lstrcpy(PromptText, message);

    lpDlgProc = MakeProcInstance(PromptDlgProc, hInst);
    rc = DialogBox(hInst, "PROMPT", hWnd, lpDlgProc);
    FreeProcInstance(lpDlgProc);

    if (rc == IDOK)
       lstrcpy(buf, PromptString);
    else
       buf = NULL;
    return buf;
    }

/*
 * errormsg - displays an error message and the last error
 *
 */
void errormsg(LPSTR cp)
{
    char buf[128];

    wsprintf(buf, "%s (%d)", (LPSTR)cp, WSAGetLastError());
    MessageBox(hWnd, buf, NULL, MB_ICONHAND);
}




<a name="008e_000d">
<a name="008e_000d">
<a name="008e_000d"><B>[LISTING TWO]</B><pre>

#define FINGER        112
#define FINGER_SRV        113
#define CANCEL        114
#define IDM_ABOUT       115
#define QUIT          116

#define PROMPT_STRING   130
#define PROMPT_TEXT     131




<a name="008e_000f">
<a name="008e_0010">
[LISTING THREE]
<a name="008e_0010">

#include "windows.h"
#include "dlg.h"
#include "sock.dlg"

SockMenu MENU
BEGIN
    POPUP        "&Sockets"
    BEGIN
   MENUITEM "Finger &Client",      FINGER
   MENUITEM "&Finger Server...",   FINGER_SRV
   MENUITEM "&Cancel Operation",   CANCEL
   MENUITEM "A&bout...",             IDM_ABOUT
   MENUITEM "&Quit",      QUIT
    END
END














Copyright © 1993, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.