< NetBIOS Server Examples | Winsock2 Supported Protocols Main | AppleTalk Protocol >


 

 

Winsock 2: Other Supported Protocols 4 Part 6

 

 

What do we have in this chapter 4 part 6?

  1. The Netbios Client Program

  2. Testing Both the Netbios Client and Server Programs

 

The Netbios Client Program

 

Create a new empty Win32 console mode application and add the project/solution name.

 

The Netbios Client Program: Creating a new empty Win32 console mode application

 

 

The Netbios Client Program: Adding the source file

 

Add the Netbios client source code.

 

// Description:

//    This sample illustrates how to use the AF_NETBIOS protocol family

//    from a Winsock application. This particular sample is a client

//    that communicates with to the server (NetbiosServer) sample

//

// Command Line Parameters/Options:

//    NetbiosClient options

//

//    -n NAME  - Server's Netbios name

//    -p PORT  - Use this integer value as the 16th byte of server name

//    -l LANA  - Attempt a connection on this LANA number only, the

//               default behavior is to attempt a connection on all

//               LANA numbers

//    -t TYPE  - Specifies datagram (d) or seqpacket (s)

//    -c COUNT - Number of 'types' to send the message

//    -b SIZE  - Size of buffer to send

//

#include <winsock2.h>

#include <wsnetbs.h>

#include <stdio.h>

 

// Common definitions shared by client and server

#include "wsnbdef.h"

 

char    szServerName[NETBIOS_NAME_LENGTH];   // Our NetBIOS name

int     iPort,                               // Our 16th byte

        iLana,                               // LANA to connect on

        iSocketType = SOCK_SEQPACKET;          // Datagram or stream?

DWORD   dwCount=DEFAULT_COUNT,               // How many packets

       dwSize=MAX_BUFFER;                   // Size of buffer to send

BOOL    bOneLana=FALSE;                      // Connect on one LANA

 

// Function: usage

// Description: Print out usage information.

int usage()

{

    printf("Usage: NetbiosClient -n str -p int -l int -t char\n");

    printf("       -n NAME        Our NetBIOS name\n");

    printf("       -p PORT        The 16th byte qualifier of our name\n");

    printf("       -l LANA        Specifies to connect on this LANA only (this is client LANA lor!)\n");

    printf("                       By default attempt connection on all LANAs\n");

    printf("       -t TYPE        Specifies datagram (d) or seqpacket (s)\n");

    printf("       -c COUNT       Number of types to send the message\n");

    printf("       -b SIZE        Size of buffer to send\n");

            printf("Example: NetbiosClient -n MIKEBLUR -p 7172 -l 3 -t s -c 5 -b 64\n");

    return 0;

}

 

// Function: ValidateArgs

// Description

//    Parse the argument list for our NetBIOS name and whether

//    we want to operate on all LANAs or just one

int ValidateArgs(int argc, char **argv)

{

     int        i;

 

     if (argc > 1)

     {

     for (i = 1; i < argc; i++)

     {

          if((argv[i][0] == '-') || (argv[i][0] == '/'))

          {

              switch (tolower(argv[i][1]))

              {

                  case '?':

                       usage();

                       break;

                   case 'n':   // use a unique name

                        if (i+1 < argc)

                     {

                         strncpy_s(szServerName,sizeof(szServerName),argv[i+1],NETBIOS_NAME_LENGTH);

                         if (strlen(szServerName) != 0)

                               ++i;

                      }

                      break;

                 case 'p':   // set the 16th byte

                       if (i+1 < argc)

                        {

                           iPort = atoi(argv[i+1]);

                           ++i;

                        }

                       break;

                   case 'l':   // connect on one lana only

                      if (i+1 < argc)

                        {

                            bOneLana = TRUE;

                            iLana = atoi(argv[i+1]);

                            ++i;

                         }

                         break;  

                     case 't':   // datagram or stream socket?

                        if (i+1 < argc)

                         {

                            if ('s' == tolower(argv[i+1][0]))

                                  iSocketType = SOCK_SEQPACKET;

                              else if ('d' == tolower(argv[i+1][0]))

                                 iSocketType = SOCK_DGRAM;

                              else

                                 usage();

                            ++i;

                         }

                          break;

                      case 'c':   // num types to send message

                         if (i+1 < argc)

                         {

                             dwCount = atol(argv[i+1]);

                             ++i;

                        }   

                        break;   

                    case 'b':   // size of send buffer

                        if (i+1 < argc)

                        {

                            dwSize = (atol(argv[i+1]) > MAX_BUFFER ?MAX_BUFFER : atol(argv[i+1]));

                            ++i;

                        }

                        break;

                    default:

                        usage();

                        break;

                }

             }

         }

      }

    return 0;

 }

 

 // Function: main

 // Description:

 //    This function parses the command line arguments, loads the

 //    Winsock library, and connects to a server. By default, a

 //    connection is attempted on all available LANA numbers. The

 //    first one to succeed is used and the others are cancelled

 int main(int argc, char **argv)

 {

     WSADATA           wsd;

     SOCKET           *socks=NULL;           // array of socket handles

     WSAEVENT         *hEvents=NULL;         // events for each socket

     SOCKADDR_NB       nbaddr;               // NetBIOS addr of server

     WSAPROTOCOL_INFO *wsapi=NULL;

     DWORD             dwRet,dwNumProtocols,dwIndex,dwErr,i;

     char              szMessage[MAX_BUFFER];   // Data buffer

     unsigned long     iOptVal;

     struct fd_set     fdread;

     int j;

 

             // Validate arguments

             if(argc < 2)

             {

                   usage();

                   return 1;

             }

             else

                   ValidateArgs(argc, argv);

 

             // These all just another verification!

             printf("iLana = %d\n", iLana);

             printf("szServerName = %s\n", szServerName);

             printf("iPort = %d\n", iPort);

             printf("iSocketType = %d\n", iSocketType);

             printf("dwCount = %d\n", dwCount);

             printf("dwSize = %d\n", dwSize);

             printf("\n");

 

    if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)

    {

        printf("Unable to load Winsock!\n");

        return 1;

    }

    else

        printf("WSAStartup() is OK!\n");

    // If we're connecting on all LANAs enumerate all AF_NETBIOS

    //  protocols; if not we can just specify the LANA we want

    //  when creating the socket

    if (bOneLana == FALSE)

    {

         // This function will return an array of WSAPROTOCOL_INFO

         //  structures that match our socket type

        if (FindProtocol(&wsapi, &dwNumProtocols) != TRUE)

        {

           printf("FindProtocol(): Unable to find correct protocol!\n");

           return 1;

         }

         else

            printf("FindProtocol(): Correct protocol found!\n");

 

        if (dwNumProtocols == 0)

        {

               printf("FindProtocol(): No NetBIOS capable providers found!\n");

               return 1;

        }

        else

               printf("FindProtocol(): NetBIOS capable providers found!\n");

    }

    else

    {

        dwNumProtocols = 1;

    }

    // Setup the NetBIOS address name

    SET_NETBIOS_SOCKADDR(&nbaddr, NETBIOS_UNIQUE_NAME, szServerName,iPort);

 

            // Allocate some global structures

            //  socks : array of SOCKET handles for each transport we connect on

            //  hEvents : array of WSAEVENT handles for event notification

            socks = (SOCKET *)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,sizeof(SOCKET) * dwNumProtocols);

            if (socks == NULL)

            {

                        printf("Out of memory\n");

                        return 1;

            }

            hEvents = (WSAEVENT *)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,sizeof(WSAEVENT) * dwNumProtocols);

            if (hEvents == NULL)

            {

                        printf("Out of memory\n");

                        return 1;

            }

            else

                        printf("GlobalAlloc() looks fine!\n");

 

    // For each LANA, create a WSAEVENT, create the SOCKET, and

    //  register it for what events we want to receive

     for (i=0; i < dwNumProtocols; i++)

     {

         if (bOneLana)

            printf("Transport LANA #: %d\n", iLana);

         else

            printf("Transport: '%S'\n", wsapi[i].szProtocol);

 

          hEvents[i] = WSACreateEvent();

          if (hEvents[i] == NULL)

          {

              printf("WSACreateEvent() failed with error code %d\n", WSAGetLastError());

              return 1;

          }

          else

              printf("WSACreateEvent() is OK!\n");

 

           if (bOneLana == FALSE)

              socks[i] = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,

                                    FROM_PROTOCOL_INFO, &wsapi[i], 0, WSA_FLAG_OVERLAPPED);

           else

              socks[i] = WSASocket(AF_NETBIOS, SOCK_SEQPACKET, -iLana, NULL,0, WSA_FLAG_OVERLAPPED);

 

          if (socks[i] == INVALID_SOCKET)                        {

              printf("socket() failed with error code %d\n", WSAGetLastError());

           return 1;

        }

        else

              printf("socket() is OK!\n");

 

       if (WSAConnect(socks[i], (SOCKADDR *)&nbaddr, sizeof(nbaddr), NULL, NULL, NULL, NULL) == SOCKET_ERROR)

       {

             printf("WSAConnect() failed with error code %d\n", WSAGetLastError());

             continue;

       }

       if (WSAEventSelect(socks[i], hEvents[i], FD_CONNECT) == SOCKET_ERROR)

       {

              printf("WSAEventSelect() failed with error code %d\n", WSAGetLastError());

              return 1;

        }

        else

              printf("WSAEventSelect() is OK!\n");

         }

 

             // Wait for one of the connects to succeed

             dwIndex = WSAWaitForMultipleEvents(dwNumProtocols, hEvents, FALSE,WSA_INFINITE, FALSE);

             if (dwIndex == WSA_WAIT_FAILED)

             {

                         printf("WSAWaitForMultipleEvents() failed with error code %d\n", WSAGetLastError());

                         return 1;

             }

             else

                         printf("WSAWaitForMultipleEvents() is OK!\n");

 

    // Close the sockets of all other pending connections other than the one that completed first

    dwIndex -= WAIT_OBJECT_0;

    for (i=0; i < dwNumProtocols ;i++)

    {

        if (i != dwIndex)

            closesocket(socks[i]);

    }

 

    // Put the socket in non-blocking mode

    iOptVal = 1L;

    if (ioctlsocket(socks[dwIndex], FIONBIO, &iOptVal) == SOCKET_ERROR)

    {

        printf("ioctlsocket(FIONBIO) failed with error code %d\n", WSAGetLastError());

    }

    else

        printf("ioctlsocket(FIONBIO) is OK!\n");

 

    memset(szMessage, '$', dwSize);

    for (i=0; i < dwCount ;i++)

     {

        // Send the message to the server

        dwRet = send(socks[dwIndex], szMessage, dwSize, 0);

        if (dwRet == 0)

        {

            printf("send() failed coz of graceful close...\n");

            closesocket(socks[dwIndex]);

            return 0;

        }

        else if (dwRet == SOCKET_ERROR)

         {

               if ((dwErr = WSAGetLastError()) == WSAEWOULDBLOCK)

               {

                        i--;

                        continue;

                 }

                 else

                  {

                      printf("send() failed with error code %d\n", dwErr);

                      return 1;

            }

        }

        else

             printf("send() looks fine!\n");

 

        printf("Wrote %d bytes...\n", dwRet);

        // Wait until the server echoes the data back. This really doesn't

        //  matter when using SOCK_SEQPACKET, but if we're using SOCK_DGRAM

        //  then our recv() would fail with WSAEWOULDBLOCK and we'd skip

        //  the returned data (as the server might not have sent it yet)

        FD_ZERO(&fdread);

        FD_SET(socks[dwIndex], &fdread);

        select(0, &fdread, NULL, NULL, NULL);

        // Read the message back

        dwRet = recv(socks[dwIndex], szMessage, MAX_BUFFER, 0);

        if (dwRet == 0)

        {

              printf("recv() failed coz of graceful close!\n");

              closesocket(socks[dwIndex]);

              return 0;

         }

         else if (dwRet == SOCKET_ERROR)

         {

              if ((dwErr = WSAGetLastError()) == WSAEWOULDBLOCK)

              {

                   i--;

                   continue;

               }

               else

               {

                     printf("recv() failed with error code %d\n", dwErr);

                     return 1;

            }

        }

        else

             printf("recv() is OK!\n");

 

          printf("Received money: ");

          for(j=0;szMessage[j] == '$';j++)

                printf("%c", szMessage[j]);

 

        printf("\nRead %d bytes...\n", dwRet);

    }

 

            printf("Cleaning up and de-allocation...\n");

            closesocket(socks[dwIndex]);

            for (i=0; i < dwNumProtocols ;i++)

                        WSACloseEvent(hEvents[i]);

 

            GlobalFree(wsapi);

            GlobalFree(socks);

            GlobalFree(hEvents);

            WSACleanup();

            return 0;

 }

 

Add the wsnbdef.h include and its definition, wsnbdef.cpp files previously created for the server program into this project. Firstly we copy both files and paste them into the project folder.

 

---------------------------------------------------------

The Netbios Client Program: copying the header and its definition files.

 

The Netbios Client Program: pasting the header and its definition files.

 

The Netbios Client Program: The pasted files seen under the project folder

 

Then add those files to the client project.

 

The Netbios Client Program: adding the header and its definition files to the existing project.

 

The Netbios Client Program: selecting the header and its definition files.

 

The Netbios Client Program: the added header and its definition files seen in Solution explorer.

 

Build and run the project. The following screenshot shows a sample output.

 

The Netbios Client Program: a sample output

 

Testing Both the Netbios Client and Server Programs

 

Firstly we run the server program. The following screenshot shows the sample output showing the server is listening for connection.

 

Testing Both the Netbios Client and Server Programs: Running the server

 

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

 

Testing Both the Netbios Client and Server Programs: Running the client

 

Then, the following screenshot shows the server output when the communication was completed.

 

Testing Both the Netbios Client and Server Programs: The server sample output when the communication was completed

 

WARNING! The previous program example not so 'stable'. However we just want to see the working sample of using various APIs for Netbios. Finally, for this section you may want to read the Tips for Writing Windows Sockets Apps That Use AF_NETBIOS.

 

 

 


< NetBIOS Server Examples | Winsock2 Supported Protocols Main | AppleTalk Protocol >