< IrDA (Infrared) Protocol | Winsock2 Supported Protocols Main | NetBIOS >


 

 

Winsock 2: Other Supported Protocols 4 Part 2

 

 

What do we have in this chapter 4 part 2?

  1. IPX/SPX

  2. The Addressing Scheme

  3. Creating a Socket

  4. Binding a Socket

  5. Network Number vs. Internal Network Number

  6. Setting IPX Packet Types Through Winsock

  7. Name Resolution

  8. IPX Client-server Program Example

  9. Testing the IPX/SPX Client-server program

 

 

IPX/SPX

 

The IPX protocol is known as the protocol most often used with computers featuring Novell NetWare client/server networking services. IPX provides connectionless communication between two processes; therefore, if a workstation transmits a data packet, there is no guarantee that the packet will be delivered to the destination. If an application needs guaranteed delivery of data and insists on using IPX, it can use a higher-level protocol over IPX, such as the Sequence Packet Exchange (SPX) and SPX II protocols, in which SPX packets are transmitted through IPX. Winsock provides applications with the capability to communicate through IPX on Windows 95, Windows 98, Windows Me, and Windows NT platforms but not on Windows CE.

 

The Addressing Scheme

 

In an IPX network, network segments are bridged using an IPX router. Every network segment is assigned a unique four-byte network number. As more network segments are bridged, IPX routers manage communication between different network segments using the unique network segment numbers. When a computer is attached to a network segment, it is identified using a unique six-byte node number, which is usually the network adapter's physical address. A node (which is a computer) is typically capable of having one or more processes forming communication over IPX. IPX uses socket numbers to distinguish communication for processes on a node.

To prepare a Winsock client or server application for IPX communication, you have to set up a SOCKADDR_IPX structure. The SOCKADDR_IPX structure is defined in the WSIPX.H header file, and your application must include this file after including WINSOCK2.H. The SOCKADDR_IPX structure is defined as:

 

typedef struct sockaddr_ipx

{

    short          sa_family;

    char           sa_netnum[4];

    char           sa_nodenum[6];

    unsigned short sa_socket;

} SOCKADDR_IPX, *PSOCKADDR_IPX, FAR *LPSOCKADDR_IPX;

 

The sa_family field should always be set to the AF_IPX value. The sa_netnum field is a four-byte number representing a network number of a network segment on an IPX network. The sa_nodenum field is a six-byte number representing a node number of a computer's physical address. The sa_socket field represents a socket or port used to distinguish IPX communication on a single node.

 

Creating a Socket

 

Creating a socket using IPX offers several possibilities. To open an IPX socket, call the socket function or the WSASocket() function with the address family AF_IPX, the socket type SOCK_DGRAM, and the protocol NSPROTO_IPX, as follows:

 

s = socket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX);

 

s = WSASocket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX, NULL, 0, WSA_FLAG_OVERLAPPED);

 

Note that the third parameter protocol must be specified and cannot be 0. This is important because this field can be used to set specific IPX packet types.

IPX provides unreliable connectionless communication using datagrams. If an application needs reliable communication using IPX, it can use higher-level protocols over IPX, such as SPX and SPX II. This can be accomplished by setting the type and protocol fields of the socket and WSASocket() calls to the socket type SOCK_SEQPACKET or SOCK_STREAM, and the protocol NSPROTO_SPX or NSPROTO_SPXII.

If SOCK_STREAM is specified, data is transmitted as a continuous stream of bytes with no message boundaries, similar to how sockets in TCP/IP behave. On the other hand, if SOCK_SEQPACKET is specified, data is transmitted with message boundaries. For example, if a sender transmits 2000 bytes, the receiver won't return until all 2000 bytes have arrived. SPX and SPX II accomplish this by setting an end-of-message bit in an SPX header. When SOCK_SEQPACKET is specified, this bit is respected, meaning Winsock recv() and WSARecv() calls won't complete until a packet is received with this bit set. If SOCK_STREAM is specified, the end-of-message bit isn't respected, and recv completes as soon as any data is received, regardless of the setting of the end-of-message bit. From the sender's perspective (using the SOCK_SEQPACKET type), sends smaller than a single packet are always sent with the end-of-message bit set. Sends larger than single packets are packetized with the end-of-message bit set on only the last packet of the send.

 

Binding a Socket

 

When an IPX application associates a local address with a socket using bind, you shouldn't specify a network number and a node address in a SOCKADDR_IPX structure. The bind() function populates these fields using the first IPX network interface available on the system. If a machine has multiple network interfaces (a multihomed machine), it isn't necessary to bind to a specific interface. Windows 95, Windows 98, Windows Me, and Windows NT platforms provide a virtual internal network in which every network interface can be reached regardless of the physical network it is attached to. We will describe internal network numbers in greater detail later in this chapter. After your application binds successfully to a local interface, you can retrieve local network number and node number information using the getsockname() function, as in the following code fragment:

 

SOCKET sdServer;

SOCKADDR_IPX IPXAddr;

int addrlen = sizeof(SOCKADDR_IPX);

 

if ((sdServer = socket (AF_IPX, SOCK_DGRAM, NSPROTO_IPX)) == INVALID_SOCKET)

{

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

    return;

}

 

ZeroMemory(&IPXAddr, sizeof(SOCKADDR_IPX));

IPXAddr.sa_family = AF_IPX;

IPXAddr.sa_socket = htons(5150);

 

if (bind(sdServer, (PSOCKADDR) &IPXAddr, sizeof(SOCKADDR_IPX))  == SOCKET_ERROR)

{

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

    return;

}

 

if (getsockname(sdServer, (PSOCKADDR) &IPXAddr, &addrlen) == SOCKET_ERROR)

{

    printf("getsockname failed with error %d", WSAGetLastError());

    return;

}

 

// Print out SOCKADDR_IPX information returned from getsockname()

 

Network Number vs. Internal Network Number

 

A network number (known as an external network number) identifies network segments in IPX and is used for routing IPX packets between network segments. Windows 95, Windows 98, Windows Me, and Windows NT platforms and so on also feature an internal network number that is used for internal routing purposes and to uniquely identify the computer on an inter-network (several networks bridged together). The internal network number is also known as a virtual network number, the internal network number identifies another (virtual) segment on the inter-network. Thus, if you configure an internal network number for a computer running Windows 95, Windows 98, Windows Me, or Windows NT platforms, a NetWare server or an IPX router will add an extra hop in its route to that computer.

The internal virtual network serves a special purpose in the case of a multihomed computer. When applications bind to a local network interface, they shouldn't specify local interface information but instead should set the sa_netnum and sa_nodenum fields of a SOCKADDR_IPX structure to 0. This is because IPX is able to route packets from any external network to any of the local network interfaces using the internal virtual network. For example, even if your application explicitly binds to the network interface on Network A, and a packet comes in on Network B, the internal network number will cause the packet to be routed internally so that your application receives it.

 

Setting IPX Packet Types Through Winsock

 

Winsock allows your application to specify IPX packet types when you create a socket using the NSPROTO_IPX protocol specification. The packet type field in an IPX packet indicates the type of service offered or requested by the IPX packet. In Novell, the following IPX packet types are defined:

 

  1. 01h Routing Information Protocol (RIP) Packet
  2. 04h Service Advertising Protocol (SAP) Packet
  3. 05h Sequenced Packet Exchange (SPX) Packet
  4. 11h NetWare Core Protocol (NCP) Packet
  5. 14h Propagated Packet for Novell NetBIOS

 

To modify the IPX packet type, simply specify NSPROTO_IPX + n as the protocol parameter of the socket API, with n representing the packet type number. For example, to open an IPX socket that sets the packet type to 04h (SAP Packet), use the following socket call:

 

s = socket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX + 0x04);

 

Name Resolution

 

As you can probably tell, addressing IPX in Winsock is sort of ugly because you must supply multi-byte network and node numbers to form an address. IPX provides applications with the ability to locate services by using user-friendly names to retrieve network number, node number, and port number in an IPX network through the SAP protocol. Winsock 2 provides a protocol-independent method for name registration using the WSASetService() API function. Through the SAP protocol, IPX server applications use WSASetService() to register under a user-friendly name the network number, node number, and port number they are listening on. Winsock 2 also provides a protocol-independent method of name resolution through the following API functions: WSALookupServiceBegin(), WSALookupServiceNext(), and WSALookupServiceEnd().

It is possible to perform your own name-service registration and lookups by opening an IPX socket and specifying an SAP packet type. After opening the socket, you can begin broadcasting SAP packets to the IPX network to register and locate services on the network. This requires that you understand the SAP protocol in great detail and that you deal with the programming details of decoding an IPX SAP packet.

 

IPX Client-server Program Example

 

The following program example is an IPX client and server application that demonstrates how to transmit datagrams over IPX or transmit reliable data communication over SPX. Create a new empty Win32 console mode application and add the project/solution name.

 

// Description:

//    This sample illustrates using IPX/SPXII client and

//    servers. The server is very simple and can only handle

//    one client connection at a time. Both client and server codes

//            bundled together in order to share common functions

//

//

// Command Line Parameters/Options:

//    IPXSPXClientServerExample -s -c -n IPX-Addr -e Socket -l IPX-Addr -p [d|s|p] -m -b bytes -r num

//    -s           Act as server

//    -c           Act as client

//    -n IPX-Addr  Servers IPX addr (AABBCCDD.AABBCCDDEEFF)

//    -e Socket    Socket endpoint server is listening on

//    -l IPX-Addr  Local IPX address

//    -p [d|s|p]   Protocol to use

//       d           Datagram   (IPX)

//       s           Stream     (SPXII)

//       p           Seq Packet (SPXII)

//    -m           Enumerate local IPX addresses

//    -b Bytes     Number of bytes to send

//    -r Num       How many sends to perform (client only)

//

//    To run the application as a server, the following command

//    line is an example:

//        IPXSPXClientServerExample -s -e 7171 -p s

//

//    To run the application to act as a client, the following

//    command line is an example:

//        IPXSPXClientServerExample -c -n AABBCCDD.AABBCCDDEEFF -e 7171 -p s

//

//    To enumerate the local IPX adapters, the following command

//    line is an example:

//        IPXSPXClientServerExample -m

 

#include <winsock2.h>

#include <stdio.h>

#include <wsipx.h>

#include <wsnwlink.h>

 

#define MAX_DATA_LEN 64000

 

// Global Variables, not a good approach!

BOOL   bServer = TRUE,     // client or server

bEnumerate = FALSE;                     // enumerate addresses

 

SOCKET sock = INVALID_SOCKET, newsock = INVALID_SOCKET;

char  *pszServerAddress,    // Server's IPX address string

*pszLocalAddress,                           // Local IPX address string

*pszServerEndpoint,                         // Server's endpoint (socket) string

chProtocol = 's';                                 // Protocol

DWORD  dwNumBytes=128,                      // Number of bytes to send

dwNumToSend=5;                                        // Number of times to send

 

// Function: CreateSocket

// Description:

//    Create a socket based upon the command line parameters. This

//    creates the main socket (i.e. the listening socket for the

//    server and the connecting socket for the client).

//    SPX sockets use either SOCK_STREAM or SOCK_SEQPACKET but must

//    be of the protocol NSPROTO_SPX or NSPROTO_SPXII.

//    IPX sockets must use SOCK_DGRAM and NSPROTO_IPX.

int CreateSocket()

{

            int  proto, sockettype;

 

            // Find out the socket type

            if (chProtocol == 'd')

                        sockettype = SOCK_DGRAM;

            else if (chProtocol == 's')

                        sockettype = SOCK_STREAM;

            else

                        sockettype = SOCK_SEQPACKET;

 

            // Get the protocol

            if (chProtocol == 'd')

                        proto = NSPROTO_IPX;

            else

                        proto = NSPROTO_SPX;

 

            sock = socket(AF_IPX, sockettype, proto);

            if (sock == INVALID_SOCKET)

            {

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

                        return 1;

            }

            else

            {

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

            }

            return 0;

}

 

// Function: usage

// Description: Print the usage information.

int usage(char *progname)

{

            printf("Usage: %s [-s|-c] -e Socket -n ServerAddr -l LocalAddr -p [d|s|p] -m -b bytes\n", progname );

            printf("\t -s           Act as server (default)\n");

            printf("\t -c           Act as client\n");

            printf("\t -e Socket    Server's socket (port)\n" );

            printf("\t -n Addr      Server's IPX Address\n" );

            printf("\t -l Addr      Local IPX Address\n" );

            printf("\t -p d|s|p     Protocol type\n");

            printf("\t    d         datagram (IPX)\n");

            printf("\t    s         stream   (SPXII)\n");

            printf("\t    p         sequenced packet (SPXII)\n");

            printf("\t -m           Enumerate Local Addresses\n");

            printf("\t -b int       Number of bytes to send\n");

            printf("\t -r num       Number of repitions to send\n");

            printf("\n");

            printf("Example run as server: %s -s -e 7171 -p s\n", progname);

            printf("Example run as client: %s -c -n AABBCCDD.AABBCCDDEEFF -e 7171 -p s\n", progname);

            printf("Example to enumerate the local IPX adapters: %s -m\n", progname);

            return 0;

 }  

 

 // Function: PrintIpxAddress

 // Description: This function prints out an IPX address in human readable form.

void PrintIpxAddress(char *lpsNetnum, char *lpsNodenum)

{

            int i;

 

            // Print the network number first

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

            {

                        printf("%02X", (UCHAR)lpsNetnum[i]);

            }

            printf(".");

 

            // Print the node number

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

            {

                        printf("%02X", (UCHAR) lpsNodenum[i]);

            }

            printf("\n");

}

 

// Function: BtoH

// Description: BtoH () returns the equivalent binary value for an individual

//    character specified in the ascii format.

UCHAR BtoH(char ch)

{

            if ((ch >= '0') && (ch <= '9'))

            {

                        return(ch - '0');

            }

            if ((ch >= 'A') && (ch <= 'F'))

            {

                        return(ch - 'A' + 0xA);

            }

            if ((ch >= 'a') && (ch <= 'f'))

            {

                        return(ch - 'a' + 0xA);

            }

           

            // Illegal values in the IPX address will not be accepted

            printf("Illegal characters in IPX address!\n");

            return -1;

 }

 

// Function: AtoH

// Description: AtoH () coverts the IPX address specified in the string

//    (ascii) format to the binary (hexadecimal) format.

void AtoH(char *szDest, char *szSource, int iCount)

{

            while (iCount--)

            {

                        *szDest++ = (BtoH(*szSource++) << 4) + BtoH(*szSource++);

            }

}

 

// Function: FillIpxAddress

// Description:

//    FillIpxAddress() fills a structure of type SOCKADDR_IPX

//    with relevant address-family, network number, node number

//    and socket (endpoint) parameters

void FillIpxAddress(SOCKADDR_IPX *psa, LPSTR lpsAddress, LPSTR lpsEndpoint)

{

            LPSTR pszPoint;

           

            ZeroMemory(psa, sizeof(SOCKADDR_IPX));

            psa->sa_family = AF_IPX;

           

            // Check if an address is specified

            if (lpsAddress != NULL)

            {

                        // Get the offset for node number/network number separator

                        pszPoint = strchr(lpsAddress, '.');

                       

                        if (pszPoint == NULL)

                        {

                                    printf("IPX address does not have a separator\n");

                                    return;

                        }

                       

                        // convert the address in the  string format to binary format

                        AtoH((CHAR *)psa->sa_netnum,  lpsAddress, 4);

                        AtoH((CHAR *)psa->sa_nodenum, pszPoint + 1, 6);

            }

            if (lpsEndpoint != NULL)

            {

                        psa->sa_socket = (USHORT)atoi(lpsEndpoint);

            }

}

 

// Function: BindSocket

// Description:

//    BindSocket() binds the global socket descriptor 'sock' to

//    the specified address. If an endpoint is specified it uses

//    that or it binds to a system  assigned port.

int BindSocket(SOCKADDR_IPX *psa, LPSTR lpsAddress, LPSTR lpsEndpoint)

{

            int ret, len;

 

            // Fill the givenSOCKADDR_IPX structure

            FillIpxAddress(psa, lpsAddress, lpsEndpoint);

 

            ret = bind(sock, (SOCKADDR *) psa, sizeof (SOCKADDR_IPX));

            if (ret == SOCKET_ERROR)

            {

                        printf("bind() failed with error code %ld\n", WSAGetLastError());

                        return 1;

            }

            else

                        printf("bind() is OK...\n");

 

            // Print the address we are bound to. If a particular interface is not

            // mentioned in the BindSocket() call, this may print the address as

            // 00000000.0000000000000000. This is because of the fact that an

            // interface is picked only when the actual connection establishment

            // occurs, in case of connection oriented socket

            len = sizeof(SOCKADDR_IPX);

            getsockname(sock, (SOCKADDR *)psa, &len);

            printf("Bound to Local Address: ");

            PrintIpxAddress((char *) psa->sa_netnum, (char *) psa->sa_nodenum);

            return 0;

}

 

// Function: EnumerateAdapters

// Description:EnumerateAdapters () will enumerate the available IPX adapters

//    and print the addresses.

void EnumerateAdapters()

{

            SOCKADDR_IPX     sa_ipx;

            IPX_ADDRESS_DATA ipx_data;

            int        ret, cb, nAdapters, i=0;

 

            // Create a local socket

            chProtocol = 'd';

            if(CreateSocket() == 0)

                        printf("CreateSocket() is OK...\n");

            else

            {

                        printf("CreateSocket() failed with error code %ld\n", WSAGetLastError());

                        return;

            }

 

            // Bind to a local address and endpoint

            if(BindSocket(&sa_ipx, NULL, NULL) == 0)

                        printf("BindSocket() failed!...\n");

            else

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

 

            // Call getsockopt() see the total number of adapters

            cb = sizeof(nAdapters);

            ret = getsockopt(sock, NSPROTO_IPX, IPX_MAX_ADAPTER_NUM, (CHAR *) &nAdapters, &cb);

            if (ret == SOCKET_ERROR)

            {

                        printf("getsockopt(IPX_MAX_ADAPTER_NUM) failed with error code %ld\n", WSAGetLastError());

                        return;

            }

            else

                        printf("getsockopt(IPX_MAX_ADAPTER_NUM) is OK...\n");

 

            printf("Total number of adapters: %d\n", nAdapters);

            // Get the address of each adapter

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

            {

                        memset (&ipx_data, 0, sizeof(ipx_data));

                        ipx_data.adapternum = i;

                        cb = sizeof(ipx_data);

 

                        ret = getsockopt(sock, NSPROTO_IPX, IPX_ADDRESS, (CHAR *) &ipx_data, &cb);

                        if (ret == SOCKET_ERROR)

                        {

                                    printf("getsockopt(IPX_ADDRESS) failed with error code %ld\n", WSAGetLastError());

                                    return;

        }

                        else

                                    printf("getsockopt(IPX_ADDRESS) #%d is OK...\n", i);

                       

                        // Print each address

                        PrintIpxAddress((char *) ipx_data.netnum, (char *) ipx_data.nodenum);

            }

}

 

// SendData() is generic routine to send some data over a

// connection-oriented IPX socket.

int SendData(SOCKET s, char *pchBuffer)

{

            int ret;

 

            ret = send(s, pchBuffer, strlen(pchBuffer), 0);

            if (ret == SOCKET_ERROR)

            {

                        printf("send() failed with error code %ld\n", WSAGetLastError());

                        return -1;

            }

            else

                        printf("send() is OK...\n");

 

            return ret;

}

 

// ReceiveData() is generic rotuine to receive some data over a

// connection-oriented IPX socket.

int ReceiveData(SOCKET s, char *pchBuffer)

{

            int ret,iTotal = 0,iLeft=dwNumBytes;

 

            while (iLeft > 0)

            {

                        ret = recv(s, &pchBuffer[iTotal], iLeft, 0);

                        if (ret == SOCKET_ERROR)

                        {

                                    // This error just redundant...

                                    if (WSAGetLastError() == WSAEWOULDBLOCK)

                                    {

                                                printf("recv() failed with error code %ld\n", WSAGetLastError());

                                                break;

                                    }

                                    if (WSAEDISCON == WSAGetLastError())

                                                printf("Connection closed by peer...\n");

                                    else

                                                printf("recv() failed with error code %ld\n", WSAGetLastError());

                                    return -1;

                        }

                        if (ret == 0)

                        {

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

                                    break;

                        }

                        iTotal += ret;

                        iLeft  -= ret;

            }

            return iTotal;

}

 

// SendDatagram() is generic routine to send a datagram to a specifid host.

int SendDatagram(SOCKET s, char *pchBuffer, SOCKADDR_IPX *psa)

{

            int ret;

 

            ret = sendto(s, pchBuffer, strlen (pchBuffer), 0,(SOCKADDR *)psa, sizeof(SOCKADDR_IPX));

 

            if (ret == SOCKET_ERROR)

            {

                        printf("sendto() failed with error code %ld\n", WSAGetLastError());

                        return -1;

            }

            else

                        printf("sendto() is OK...\n");

            return ret;

}

 

// ReceiveDatagram() is generic routine to receive a datagram from a specified host.

int ReceiveDatagram(SOCKET s, char *pchBuffer, SOCKADDR_IPX *psa, int *pcb)

{

            int ret;

 

            ret = recvfrom(s, pchBuffer, MAX_DATA_LEN, 0, (SOCKADDR *) psa, pcb);

            if (ret == SOCKET_ERROR)

            {

                        printf("recvfrom() failed with error code %ld\n", WSAGetLastError());

                        return -1;

            }

            else

                        printf("recvfrom() is OK...\n");

 

            return ret;

}

 

// Server () performs the connection-less/connection-oriented server related tasks

void Server()

{

            SOCKADDR_IPX  sa_ipx,  // Server address

                        sa_ipx_client;                        // Client address

            char     chBuffer[MAX_DATA_LEN]; // Data buffer

            int        ret,cb;

 

            if(CreateSocket() == 0)

                        printf("CreateSocket() is OK...\n");

            else

            {

                        printf("CreateSocket() failed with error code %ld\n", WSAGetLastError());

                        return;

            }

 

            // Bind to a local address and endpoint

            if(BindSocket(&sa_ipx, pszLocalAddress, pszServerEndpoint) == 0)

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

            else

                        printf("BindSocket() failed!\n");

 

            // Check the Specified protocol. Call listen(), accept() if a connection

            // oriented protocol is specified

            if (chProtocol != 'd')

            {

                        ret = listen(sock, 5);

                        if (ret == SOCKET_ERROR)

                        {

                                    printf("listen() failed with error code %ld\n", WSAGetLastError());

                        }

                        else

                        {

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

                                    printf("Waiting for a Connection...\n");

                        }

 

                        // Wait for a connection

                        while (1)

                        {

                                    cb = sizeof(sa_ipx_client);

                                    newsock = accept(sock, (SOCKADDR *) &sa_ipx_client, &cb);

 

                                    if (newsock == INVALID_SOCKET)

                                    {

                                                printf("accept() failed: %d\n", WSAGetLastError());

                                                return;

                                    }

                                    else

                                                printf("accept() is OK...\n");

 

                                    // Print the address of connected client

                                    printf("Connected to Client Address: ");

                                    PrintIpxAddress(sa_ipx_client.sa_netnum, sa_ipx_client.sa_nodenum);

 

                                    while (1)

                                    {

                                                // Receive data on newly created socket

                                                ret = ReceiveData(newsock, chBuffer);

                                                if (ret == 0)

                                                            break;

                                                else if (ret == -1)

                                                            return;

 

                                                // Print the contents of received data

                                                chBuffer[ret] = '\0';

                                                printf("%d bytes of data received: %s\n", ret, chBuffer);

 

                                                // Send data on newly created socket

                                                // printf("Sending data...\n");

                                                ret = SendData(newsock, chBuffer);

                                                if (ret == 0)

                                                            break;

                                                else if (ret == -1)

                                                            return;

                                                printf("%d bytes of data sent\n", ret);

                                    }

                                    closesocket(newsock);

                        }

            }

            else  // Server will recv and send datagrams

            {

                        // Receive a datagram on the bound socket

                        while (1)

                        {

                                    cb = sizeof (sa_ipx_client);

                                    ret = ReceiveDatagram(sock, chBuffer, &sa_ipx_client, &cb);

                                    if (ret == -1)

                                                return;

 

                                    // Print the contents of received datagram and the senders address

                                    printf("Message Received from Client Address - ");

                                    PrintIpxAddress( sa_ipx_client.sa_netnum, sa_ipx_client.sa_nodenum );

                                    chBuffer[ret] = '\0';

 

                                    printf("%d bytes of data received->%s\n", ret, chBuffer);

                                    // Echo the datagram on the bound socket to the client

                                    ret = SendDatagram(sock, chBuffer, &sa_ipx_client);

                                    if (ret == -1)

                                                return;

                                    printf("%d bytes of data sent\n", ret);

                        }

                        closesocket(newsock);

            }

}

 

// Client () performs the connection-less/connection-oriented client related tasks

void Client()

{

            SOCKADDR_IPX  sa_ipx,  // client address

                        sa_ipx_server;                       // server address

            char     chBuffer[MAX_DATA_LEN]; // data buffer

            int        ret,cb;

            DWORD i;

 

            if(CreateSocket() == 0)

                        printf("CreateSocket() is OK...\n");

            else

            {

                        printf("CreateSocket() failed with error code %ld\n", WSAGetLastError());

                        return;

            }

 

            // Bind to a local address and endpoint

            if(BindSocket(&sa_ipx, pszLocalAddress, NULL) == 0)

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

            else

                        printf("BindSocket() failed!\n");

 

            if (pszServerEndpoint == NULL)

            {

                        printf("Server Endpoint must be specified....Exiting\n");

                        return;

            }

 

            // Fill the sa_ipx_server address with server address and endpoint

            if (pszServerAddress != NULL)

            {

                        FillIpxAddress ( &sa_ipx_server, pszServerAddress, pszServerEndpoint);

            }

            else

            {

                        printf("Server Address must be specified....Exiting\n");

                        return;

            }

 

            // Check the Specified protocol. Call connect() if a connection oriented

            // protocol is specified

            if (chProtocol != 'd')

            {

                        printf("Connecting to Server: ");

                        PrintIpxAddress ( sa_ipx_server.sa_netnum, sa_ipx_server.sa_nodenum );

 

                        // Connect to the server

                        ret = connect(sock, (SOCKADDR *) &sa_ipx_server, sizeof sa_ipx_server);

                        if (ret == SOCKET_ERROR)

                        {

                                    printf("connect() failed with error code %ld\n", WSAGetLastError());

                        }

                        else

                                    printf("connect() is OK...\n");

 

                        printf("Connected to Server Address: ");

                        PrintIpxAddress( sa_ipx_server.sa_netnum, sa_ipx_server.sa_nodenum );

 

                        // Send data to the specified server

                        memset(chBuffer, '$', dwNumBytes);

                        chBuffer[dwNumBytes] = 0;

 

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

                        {

                                    ret = SendData(sock, chBuffer);

                                    if (ret == 0)

                                                return;

                                    else if (ret == -1)

                                                return;

                                    printf("%d bytes of data sent\n", ret);

 

                                    // Receive data from the server

                                    ret = ReceiveData(sock, chBuffer);

                                    if (ret == 0)

                                                return;

                                    else if (ret == -1)

                                                return;

                                    // Print the contents of received data

                                    chBuffer[ret] = '\0';

                                    printf("%d bytes of data received: %s\n", ret, chBuffer);

                        }

            }

            else

            {

                        // Send a datagram to the specified server

                        memset(chBuffer, '$', dwNumBytes);

                        chBuffer[dwNumBytes] = 0;

 

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

                         {

                                     ret = SendDatagram(sock, chBuffer, &sa_ipx_server);

                                     if (ret == -1)

                                                 return;

                                     printf("%d bytes of data sent...\n", ret);

 

                                     // Receive a datagram from the server

                                     cb = sizeof(sa_ipx_server);

                                     ret = ReceiveDatagram(sock, chBuffer, &sa_ipx_server, &cb);

                                     if (ret == -1)

                                                 return;

 

                                     // Print the contents of received data

                                     chBuffer[ret] = '\0';

                                     printf("%d bytes of data received: %s\n", ret, chBuffer);

                         }

            }

}

 

// Function: CheckProtocol

// Description: CheckProtocol() checks if a valid protocol is specified on the command line.

BOOL CheckProtocol(char chProtocol)

{

            if (('d' != chProtocol) && ('s' != chProtocol) && ('p' != chProtocol))

            {

                        return FALSE;

            }

            return TRUE;

}

 

// Function: ValidateArgs

// Description:

//    Parses the command line arguments and sets some global

//    variables to determine the behavior of the application.

void ValidateArgs(int argc, char **argv)

{

            int   i;

 

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

            {

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

                        {

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

                                    {

                                    case '?':

                                                usage(argv[0]);

                                                break;

                                    case 's':   // act as server

                                                bServer = TRUE;

                                                break;

                                    case 'c':   // act as client

                                                bServer = FALSE;

                                                break;

                                    case 'e':   // server endpoint (port)

                                                if(i+1 < argc)

                                                {

                                                            pszServerEndpoint = argv[i+1];

                                                            ++i;

                                                }

                                                break;

                                    case 'n':   // server address

                                                if(i+1 < argc)

                                                {

                                                            pszServerAddress = argv[i+1];

                                                            ++i;

                                                }

                                                break;

                                    case 'l':   // local address

                                                if(i+1 < argc)

                                                {

                                                            pszLocalAddress = argv[i+1];

                                                            ++i;

                                                }

                                                break;

                                    case 'm':   // enumerate local addresses

                                                bEnumerate = TRUE;

                                                break;

                                    case 'p':   // protocol

                                                if(i+1 < argc)

                                                {

                                                            chProtocol = tolower(argv[i+1][0]);

                                                            if(FALSE == CheckProtocol(chProtocol))

                                                            {

                                                                        printf("Unknown protocol specified!\n\n");

                                                                        usage(argv[0]);

                                                            }

                                                            ++i;

                                                }

                                                break;

                                    case 'b':   // number of bytes to send

                                                if(i+1 < argc)

                                                {

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

                                                            ++i;

                                                }

                                                break;

                                    case 'r':   // number of repititions

                                                if(i+1 < argc)

                                                {

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

                                                            ++i;

                                                }

                                                break;

                                    default:

                                                usage(argv[0]);

                                                break;

                                    }

                        }

            }

}

 

// Function: main

// Description:

//    The main function which loads Winsock, parses the command line,

//    and initiates either the client or server routine depending

//    on the parameters passed.

int main(int argc, char **argv)

{

            WSADATA    wsd;

 

            // Check whether there are arguments supplied

            if(argc == 1)

            {

                        usage(argv[0]);

                        return 1;

            }

            else

            {

                        printf("There are argument(s) supplied...\n");

                        ValidateArgs(argc, argv);

            }

 

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

     {

        printf("WSAStartup() failed with error code %ld\n", GetLastError());

        return -1;

     }

             else

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

 

             // Check to see if the role of the application is

             // just to enumerate the local adapters

             if (bEnumerate)

                         EnumerateAdapters();

             else

             {

                         // Act as server

                         if (bServer)

                                     Server();

                         // Act as client

                         else

                                     Client();

             }

 

             // Need to do the shutdown() for SOCK_STREAM/TCP?

 

             // Close the bound socket

             if(closesocket(sock) == 0)

                         printf("closesocket(sock) is OK!\n");

             else

                         printf("closesocket(sock) failed with error code %ld\n",WSAGetLastError());

 

             // Do the WSA* cleanup

             if(WSACleanup() == 0)

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

             else

                         printf("WSACleanup() failed with error code %ld\n",WSAGetLastError());

             return 0;  

 }

 

If the IPX/SPX protocol was installed on your Windows machine, it should be visible as NWLink IPX/SPX/NetBIOS Compatible Transport Protocol shown below seen in the Local Area Connection Properties page.

 

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

IPX client and server application that demonstrates how to transmit datagrams over IPX or transmit reliable data communication over SPX - The NWLink IPX/SPX/NetBIOS compatible Transport Protocol

 

For program example testing purpose, if the IPX/SPX protocol not installed on your testing machine, follow the following steps (shown for Windows XP Pro SP2 machine).

 

IPX client and server application that demonstrates how to transmit datagrams over IPX or transmit reliable data communication over SPX - installing the IPX/SPX protocol

 

IPX client and server application that demonstrates how to transmit datagrams over IPX or transmit reliable data communication over SPX - selecting Protocol network component to be installed

 

IPX client and server application that demonstrates how to transmit datagrams over IPX or transmit reliable data communication over SPX - selecting the NWLink IPX/SPX/NetBIOS Compatible Transport Protocol component

 

 

IPX client and server application that demonstrates how to transmit datagrams over IPX or transmit reliable data communication over SPX - installation completed

 

The following screenshots show some of the IPX/SPX compatible protocol settings.

 

IPX client and server application that demonstrates how to transmit datagrams over IPX or transmit reliable data communication over SPX - Some of the adapter settings for IPX/SPX

 

IPX client and server application that demonstrates how to transmit datagrams over IPX or transmit reliable data communication over SPX - Setting the IPX/SPX internal network number

 

Next, we install the client component for IPX/SPX.

 

IPX client and server application that demonstrates how to transmit datagrams over IPX or transmit reliable data communication over SPX - seleting the Netware client component

 

IPX client and server application that demonstrates how to transmit datagrams over IPX or transmit reliable data communication over SPX - Selecting the Client Service for NetWare component

 

After completing the IPX/SPX components installation, we just disable and then enable the Local Area Connection to make the new settings effective instead of restarting the machine.

 

Disabling the Local Area Connection

 

Re-enabling the Local Area Connection

 

Testing the IPX/SPX Client-server program

 

Now, let test our IPX/SPX program example demonstrating the client-server communication.

 

IPX client and server application that demonstrates how to transmit datagrams over IPX or transmit reliable data communication over SPX - running the program without any argument

 

Firstly, we run the program as a server using the following arguments.

 

IPXSPXClientServerExample -s -e 7171 -p s

 

IPX client and server application that demonstrates how to transmit datagrams over IPX or transmit reliable data communication over SPX - running the program as a server

 

Then run the program as client using the server’s IPX address, 12345678.000000000001 and server’s listening socket, 7171. The command is as follows:

 

IPXSPXClientServerExample -c -n 12345678.000000000001 -e 7171 -p s

 

IPX client and server application that demonstrates how to transmit datagrams over IPX or transmit reliable data communication over SPX - running the program as a client

 

The previous server program screenshot is shown below.

 

IPX client and server application that demonstrates how to transmit datagrams over IPX or transmit reliable data communication over SPX - the server sample output when the communication was completed

 

The following screenshot shows the local IPX addresses enumeration output sample.

 

IPX client and server application that demonstrates how to transmit datagrams over IPX or transmit reliable data communication over SPX - another sample output with -m option

 

 

The IPX/SPX protocol owned by Novell used in its NetWare OS.

 

 

 


< IrDA (Infrared) Protocol | Winsock2 Supported Protocols Main | NetBIOS >