Nexus Integration
WSA - The Windows Socket API - winsock.dll

WSA consists of a collection of function calls and data structures which provides a standard access to the network services. It is a standard network interface for applications in the same way as a wall socket is a standard electric interface. The TCP/IP protocol stack is available on several media - Ethernet, Token Ring and Serial Line. WSA is derived from the Berkeley Socket API, first implemented on Berkeley Unix. The main difference between the Windows version and the Unix version is the message-based operation in the Windows environment. WSA has two main operation modes: synchronous and asynchronous. The asynchronous mode is the best integration with the message-based Windows environment.
Three transport layers are available: TCP, UDP and ICMP. TCP provide a reliable data transport with connection. UDP provide a connectionless data transport layer. ICMP is used for error and control messages. The Ping protocol is based on ICMP. A lot of protocols/services are based on the TCP/IP stack: FTP, SMTP, LPD, HTTPD, NFS (RPC/XDR) and Telnet.
All code fragments included in this first small introduction are all based on the asynchronous interface. The code fragments are part of Nexus Terminal. The WinSock interface has been integrated in a standard Visual C++ MFC application based on an App (CNmtApp), a frame (CMainFrame), a doc (CTn3270Doc) and a view (CImgView) class. There is a socket class in MFC, but I prefer to use the clean WSA interface. The WSA startup function (WSAStartup) must be called before any other functions. An application must also inform WSA with the function WSACleanup at termination. WSAStartup is done in CNmtApp::InitInstance ().
All other WSA functions are included in the CMainFrame class (WSACleanup is done from CMainFrame::OnDestroy ()). The communication part is done in CMainFrame, the emulation in CTn3270Doc, and the image presentation is done in CImgView.

WSA functions included: WSAStartup, WSACleanup, WSAGetLastError, WSASetLastError, inet_addr, inet_ntoa, ntohs, htons, htonl, WSAAsyncSelect, WSAAsyncGetHostByName, WSACancelAsyncRequest, closesocket, WSAGETSELECTERROR, WSAGETSELECTEVENT, TCP socket, UDP socket, bind, connect, recv, send, recvfrom, sendto, ioctlsocket FIOASYNC, ioctlsocket FIONBIO, getsockopt, setsockopt


BOOL CNmtApp::InitInstance ()
{         
   WSADATA  WSA_Data;

   if (WSAStartup (0x101, & WSA_Data)) {
      AfxMessageBox ("No Winsock.dll in path or wrong version.", MB_ICONSTOP | MB_OK); 
      return FALSE;
   }   

   // The rest is standard initialization

   return TRUE;
} // Back to top of page

// mainfrm.h : Interface of the CMainFrame class
// Only the WSA related code, the rest of the code has been removed

#include "winsock.h"

#define WM_HOST_RESOLVED      (WM_USER + 1)
#define WM_ASYNC_IO           (WM_USER + 2)

#define WM_ASYNC_BC           (WM_USER + 3)

#define WM_MAX_SESSIONS       (WM_USER + 10)
#define WM_START_BC_SERVER    (WM_USER + 11)

#define BC_PORTNUMBER               1234

#define WS_WRITE_SIZE                512

#define WS_READ_BUFFER              4096
#define WS_WRITE_BUFFER             8192
                                        
typedef unsigned long         IP_ADDR;
typedef IP_ADDR FAR *         LP_IP_ADDR;
typedef LP_IP_ADDR FAR *      LPP_IP_ADDR;

/// // /

class CMainFrame : public CFrameWnd
{
protected: // Create from serialization only
   CMainFrame();
   DECLARE_DYNCREATE(CMainFrame)

// Attributes
public:

// Operations
public:

   BOOL TraceOn () const { return m_bTrace; }
   BOOL Connected () const { return m_bConnected; }        
   BOOL BroadcastServer () const { return m_bBroadcastServer; }
   
   BOOL ConnectHost (LPCSTR, LPCSTR, int), DisconnectSession (),
        GetIoBuffer (LPBYTE *), FlushIoBuffer (int), IpAddress (LPCSTR);
                                                                    
   void SendBroadcaste (LPSTR),
        SetStatusText (LPSTR), SetStatusText (LPSTR, int);
   
// Implementation
public:
   virtual ~CMainFrame();
#ifdef _DEBUG
   virtual void AssertValid() const;
   virtual void Dump(CDumpContext& dc) const;
#endif

// Attributes and Operations
protected:  

   // Async winsock 
   HANDLE m_hCancelAsyncRequest;
   SOCKET m_Socket, m_ServerSocket;  
   struct sockaddr_in   m_RemoteAddr;   

   // Telnet 
   TnState  m_TelnetState;
   
   int  m_Port, m_nTnBufferCount, m_nSessions, m_wcount;
   
   BOOL m_bConnected, m_bConnecting, 
        m_bBroadcastServer, m_bBroadcastMsg, m_bViolation;
        
   LPBYTE m_rbp, m_wbp, m_tbp, m_wbufferp, m_grb, m_gwb;
      
   char m_EntryBuffer [MAXGETHOSTSTRUCT], 
        m_TerminalName [MAX_TERMINAL_NAME], m_StatusPane [STATUS_PANE_SIZE];
        
   HGLOBAL  m_hSessionInformation, m_hReadBuffer, m_hWriteBuffer;
   LPCSP    m_pFirstCS, m_pLastCS, m_pcs;
   
   int  TelnetParser (LPBYTE, int), SetSocketBufferSize (SOCKET, int, int);
   void StartBroadcastServer (), SetWinsockError (int),
        LoadEntryBuffer (IP_ADDR), PostBroadcastMsg (UINT, WPARAM);
         
// Generated message map functions
protected:
   //{{AFX_MSG(CMainFrame)
   afx_msg void OnDestroy();
   // Other msg functions included by ClassWizard
   //}}AFX_MSG
   // Msg functions included manualy
   afx_msg LRESULT OnHostResolved (WPARAM wParam, LPARAM lParam);
   afx_msg LRESULT OnAsyncIo (WPARAM wParam, LPARAM lParam);
   afx_msg LRESULT OnAsyncBroadcast (WPARAM wParam, LPARAM lParam);
   afx_msg LRESULT OnBroadcastServer (WPARAM wParam, LPARAM lParam);
   DECLARE_MESSAGE_MAP()
}; // Back to top of page

// mainfrm.cpp : Implementation of the CMainFrame class

#include "stdafx.h"

#include "nmt.h"
#include "mainfrm.h"       // winsock.h and tnconst.h included

IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
   //{{AFX_MSG_MAP(CMainFrame)
   ON_WM_DESTROY()
   // Other msg functions included by ClassWizard
   //}}AFX_MSG_MAP
   // Asynchronous Winsock functions
   ON_MESSAGE(WM_HOST_RESOLVED, OnHostResolved)
   ON_MESSAGE(WM_ASYNC_IO, OnAsyncIo)
   ON_MESSAGE(WM_ASYNC_BC, OnAsyncBroadcast)
   ON_MESSAGE(WM_START_BC_SERVER, OnBroadcastServer)
END_MESSAGE_MAP()
//  Back to top of page 

BOOL CMainFrame::ConnectHost (LPCSTR hnp, LPCSTR tnp, int port)
{                                               
   IP_ADDR ipa;

   if (m_bConnected) 
      return FALSE;

   // hnp - Host name ptr: abc | abc.def.gh | 123.45.67.98
   // tnp - Terminal name ptr
                            
   WSASetLastError (0);   // Reset WSA error

   m_bConnecting = FALSE; 
   m_hCancelAsyncRequest = 0;
   m_wcount = 0; m_wbp = m_gwb; m_rbp = m_grb;
   
   TelnetSetup (tnp); m_Port = port;

   if ((ipa = inet_addr (hnp)) != INADDR_NONE) {
      m_bConnecting = TRUE; LoadEntryBuffer (ipa); 
      PostMessage (WM_HOST_RESOLVED, 0, 0); return TRUE;
   }

   wsprintf (m_psp, "Resolve %s", hnp); SetStatusText (m_psp);

   if (! (m_hCancelAsyncRequest =
      WSAAsyncGetHostByName (m_hWnd, WM_HOST_RESOLVED, 
             hnp, (LPSTR) & m_EntryBuffer[0], MAXGETHOSTSTRUCT))) {
      wsprintf (m_psp, "Async GetHostByName error %d", WSAGetLastError ());
      SetStatusText (m_psp); return FALSE;
   } 
         
   return (m_bConnecting = TRUE); 
}  // Back to top of page 
BOOL CMainFrame::DisconnectSession ()
{  
   CTn3270Doc * pDoc;
   
   m_bConnecting = m_bConnected = FALSE;
                       
   m_wcount = 0; m_wbp = m_gwb; m_rbp = m_grb;
   
   if (pDoc = ((CNmtApp *) AfxGetApp())->m_pTn3270Doc)
      pDoc->SetDisconnected ();
   
   if (m_Socket != INVALID_SOCKET) {
      WSAAsyncSelect (m_Socket, m_hWnd, 0, 0);
      
      closesocket (m_Socket); m_Socket = INVALID_SOCKET;
   } else if (m_hCancelAsyncRequest) {

      if (WSACancelAsyncRequest (m_hCancelAsyncRequest) == SOCKET_ERROR) {
         wsprintf (m_psp, "Cancel GetHostByName error %d", WSAGetLastError ());
         SetStatusText (m_psp);
      } else SetStatusText ("Disconnected");
   } else return FALSE;
   m_hCancelAsyncRequest = 0; return TRUE;
}  // Back to top of page               
void CMainFrame::LoadEntryBuffer (IP_ADDR ipa) 
{                                              
   LPHOSTENT phe = (LPHOSTENT) m_EntryBuffer;
   LPP_IP_ADDR ppipa = (LPP_IP_ADDR) (m_EntryBuffer + sizeof (HOSTENT));
   LP_IP_ADDR pipa = (LP_IP_ADDR) (m_EntryBuffer + sizeof (HOSTENT) + sizeof (LPP_IP_ADDR));
   
   memset (phe, 0, sizeof (HOSTENT));
   
   // LoadEntryBuffer will put the IP addr into 
   // m_EntryBuffer with the same format as GetHostByName
   
   phe->h_length = sizeof (IP_ADDR);
   phe->h_addr_list = (char FAR * FAR *) ppipa;
   
   * ppipa = pipa; * pipa = ipa;
}  // Back to top of page               
LRESULT CMainFrame::OnHostResolved (WPARAM wParam, LPARAM lParam)
{  
   LPSTR ap;
   HOSTENT * hp;         
   u_long nAsync = 1;
   
   int nSelectError = (int) WSAGETSELECTERROR (lParam), nLastError;    
   
   CTn3270Doc * pDoc = ((CNmtApp *) AfxGetApp())->m_pTn3270Doc;
   
   m_hCancelAsyncRequest = 0;

   if (nSelectError) {
      if (nSelectError == WSANO_DATA) {
         SetStatusText ("Unknown host");
      } else SetStatusText ("Resolve host error", WSAGetLastError ());

      if (pDoc)
         pDoc->SetDisconnected ();
   
      return 0;
   } else hp = (HOSTENT *) m_EntryBuffer;

   if ((m_Socket = socket (PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
      SetStatusText ("No more sockets"); m_bConnecting = FALSE; 
      
      if (pDoc)
         pDoc->SetDisconnected ();
   
      return 0;
   } memset (& m_RemoteAddr, 0, sizeof (m_RemoteAddr));
   
   ap = (LPSTR) & m_RemoteAddr.sin_addr.s_addr;
   
   memcpy (ap, hp->h_addr, hp->h_length);
   m_RemoteAddr.sin_family = PF_INET;

   m_RemoteAddr.sin_port = htons (m_Port);

   wsprintf (m_psp, "Connecting %s", inet_ntoa (m_RemoteAddr.sin_addr)); 
   SetStatusText (m_psp);
   
   ioctlsocket (m_Socket, FIOASYNC, (u_long FAR *) & nAsync);
      
   
   if (WSAAsyncSelect (m_Socket, m_hWnd, WM_ASYNC_IO, FD_CONNECT | FD_READ | FD_WRITE | FD_CLOSE) ||
   
       connect (m_Socket, (struct sockaddr far *) & m_RemoteAddr, sizeof (struct sockaddr_in))) {
       
      if ((nLastError = WSAGetLastError ()) != WSAEWOULDBLOCK) 
         SetStatusTextAndDisconnect ("Async connect error", nLastError);
   } return 0;
}  // Back to top of page 
LRESULT CMainFrame::OnAsyncIo (WPARAM wParam, LPARAM lParam)
{
   CTn3270Doc * pDoc;
   int nSelectError = (int) WSAGETSELECTERROR (lParam),
   
       nSelectEvent = (int) WSAGETSELECTEVENT (lParam),
       nLastError, rc, wc;
   
   if (! nSelectError) {
      switch (nSelectEvent) {
         case FD_READ:

            if (! m_bInParser) {
               m_bInParser = TRUE;
               
               if ((rc = recv (m_Socket, (LPSTR) m_rbp, WS_READ_BUFFER, 0)) > 0) {
                  if ((rc = TelnetParser (m_rbp, rc)) > 0) {
                     // Send rest of the data to the Tn3270 document
                     if (pDoc = ((CNmtApp *) AfxGetApp())->m_pTn3270Doc)
                        pDoc->HostInput (m_rbp, rc);
                  }
               } else {
                  if ((nLastError = WSAGetLastError ()) != WSAECONNRESET) {
                     SetStatusTextAndDisconnect ("Connection closed", nLastError);
                  } else SetStatusTextAndDisconnect ("Connection closed by host system", 0);
               } m_bInParser = FALSE;
            } else PostMessage (WM_ASYNC_IO, wParam, lParam);
            break;
               
         case FD_WRITE:

            if (m_wcount) {                               
               wc = m_wcount > WS_WRITE_SIZE ? WS_WRITE_SIZE : m_wcount;
               
               while (m_wcount && (wc = send (m_Socket, (LPSTR) m_wbp, wc, 0)) > 0) {
                  m_wcount -= wc; m_wbp += wc; 
                  if (m_wcount <= 0) {
                     m_wcount = wc = 0; m_wbp = m_gwb;
                  } else
                     wc = m_wcount > WS_WRITE_SIZE ? WS_WRITE_SIZE : m_wcount;
               }  
               
               if (wc < 0 && (nLastError = WSAGetLastError ()) != WSAEWOULDBLOCK) {
                  SetStatusTextAndDisconnect ("Async send error", nLastError);
               } // Else wait for the next async FD_WRITE if more data in send buffer
            } break;
                     
         case FD_CONNECT:

            m_hCancelAsyncRequest = 0;
            m_nTnBufferCount = 0; ResetStatusText ();
            m_bConnecting = FALSE; m_bConnected = TRUE; 
            
            SetSocketBufferSize (m_Socket, SO_RCVBUF, 4096);
            SetSocketBufferSize (m_Socket, SO_SNDBUF, 2048);
            
            if (pDoc = ((CNmtApp *) AfxGetApp())->m_pTn3270Doc)
               pDoc->SetConnected ();
                        
            break;
                  
         case FD_CLOSE:
         default:               
            SetStatusTextAndDisconnect ("Connection closed", 0);
      }
   } else {    
      nLastError = WSAGetLastError ();
      
      SetWinsockError (nSelectError, nLastError);
      DisconnectSession (FALSE);   
   } return 0;     
}  // Back to top of page
int CMainFrame::SetSocketBufferSize (SOCKET sd, int option, int size)
{                                          
   int bsize = size, ssize = 0, sor = sizeof (int);

   getsockopt (sd, SOL_SOCKET, option, (LPSTR) & ssize, & sor);

   if (ssize >= size) {
      return ssize;
   } else sor = sizeof (int);
   
   if (setsockopt (sd, SOL_SOCKET, option, (LPSTR) & bsize, sor) != SOCKET_ERROR) {
      if (getsockopt (sd, SOL_SOCKET, option, (LPSTR) & ssize, & sor) != SOCKET_ERROR) {
         if (ssize >= size)
            return (ssize);
      }
   }

   return (option == SO_SNDBUF ? WS_WRITE_SIZE : WS_READ_BUFFER);
}  // Back to top of page               
void CMainFrame::StartBroadcastServer ()
{                                   
   int i, ms, r;
   SOCKADDR_IN ba;
   u_long nNonBlocking = TRUE;
   
   WSASetLastError (0); 

   m_hSessionInformation = NULL;
   ms = ((CNmtApp *) AfxGetApp())->GetMaxSessions ();
   
   if (ms < MAX_CSESSIONS) {
   
      if ((m_ServerSocket = socket (AF_INET, SOCK_DGRAM, 0)) != INVALID_SOCKET) {
         ba.sin_family = AF_INET;
         ba.sin_port = htons (BC_PORTNUMBER);

         ba.sin_addr.s_addr = htonl (INADDR_ANY);
      
         r = ioctlsocket (m_ServerSocket, FIONBIO, (u_long FAR *) & nNonBlocking);
                     
         if (bind (m_ServerSocket, (LPSOCKADDR) & ba, sizeof (ba)) != SOCKET_ERROR) {
            if (! WSAAsyncSelect (m_ServerSocket, m_hWnd, WM_ASYNC_BC, FD_READ | FD_WRITE))
               m_bBroadcastServer = TRUE;
         }
      }
   }
}  // Back to top of page               
LRESULT CMainFrame::OnAsyncBroadcast (WPARAM wParam, LPARAM lParam)
{
   int  nSelectError = (int) WSAGETSELECTERROR (lParam),
        nSelectEvent = (int) WSAGETSELECTEVENT (lParam),
        nLastError, rc, alen = sizeof (SOCKADDR_IN);
        
   IN_ADDR aa;
   SOCKADDR_IN ra;
   BOOL bFreeSession = FALSE;
      
   u_short rhwnd;
   char rbuffer [BC_MSG_LENGTH + 1];
            
   if (! nSelectError) {
      switch (nSelectEvent) {
         case FD_READ:
            memset (& ra, 0, alen); 
            
            rc = recvfrom (m_ServerSocket, rbuffer, BC_MSG_LENGTH, 0, (LPSOCKADDR) & ra, & alen);

            if (rc == BC_MSG_LENGTH) {

               rbuffer [BC_MSG_LENGTH] = 0x00;
               memcpy (& aa, & ra.sin_addr.s_addr, 4);           

               TRACE ("Addr %d.%d.%d.%d port %d\n", 
                       aa.S_un.S_un_b.s_b1, aa.S_un.S_un_b.s_b2, 
                       aa.S_un.S_un_b.s_b3, aa.S_un.S_un_b.s_b4, ntohs (ra.sin_port)); 

            } break;
               
         case FD_WRITE:
         case FD_CLOSE:
            break;
      }
   } else {              
      nLastError = WSAGetLastError ();
   } return 0;     
}  // Back to top of page               
void CMainFrame::SendBroadcast (LPSTR pMsg) 
{                                  
   int r, wc;
   SOCKET bcs;
   SOCKADDR_IN aa, ba;
   BOOL bBroadCast = TRUE;

   WSASetLastError (0);
   
   if (pMsg && ((bcs = socket (AF_INET, SOCK_DGRAM, 0)) != INVALID_SOCKET)) {

      memset (& aa, 0, sizeof (SOCKADDR_IN));

      aa.sin_family = AF_INET;
      aa.sin_port = htons (0);
      ba.sin_addr.s_addr = htonl (INADDR_ANY);

      if (bind (bcs, (LPSOCKADDR) & aa, sizeof (aa)) != SOCKET_ERROR) {
               
         ba.sin_family = AF_INET;
         ba.sin_port = htons (BC_PORTNUMBER);
         ba.sin_addr.s_addr = htonl (INADDR_BROADCAST);
         
         r = setsockopt (bcs, SOL_SOCKET, SO_BROADCAST, (LPSTR) & bBroadCast, sizeof (BOOL));
                           
         wc = sendto (bcs, pMsg, BC_MSG_LENGTH, 0, (LPSOCKADDR) & ba, sizeof (ba));
      } 
      
      closesocket (bcs); 
   }   
}  // Back to top of page               
void CMainFrame::OnDestroy ()
{                     
   BOOL bRestartServer = FALSE;
   
   if (m_Socket != INVALID_SOCKET)
      closesocket (m_Socket);   // Close telnet socket
         
   if (m_ServerSocket != INVALID_SOCKET) {
      bRestartServer = TRUE;
      // Close licence control server socket
      closesocket (m_ServerSocket);
   }

   WSACleanup ();        // Terminate WSA interface
   
   if (bRestartServer) 
      PostBroadcastMsg (WM_START_BC_SERVER, 0);
   
   CFrameWnd::OnDestroy();   
}  // Back to top of page