< IrDA (Infrared) Protocol | Winsock2 Supported Protocols Main | NetBIOS >
What do we have in this chapter 4 part 2?
|
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 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.
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()
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.
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:
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);
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.
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.
--------------------------------------------------------------------
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).
The following screenshots show some of the IPX/SPX compatible protocol settings.
Next, we install the client component for IPX/SPX.
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.
Now, let test our IPX/SPX program example demonstrating the client-server communication.
Firstly, we run the program as a server using the following arguments.
IPXSPXClientServerExample -s -e 7171 -p s
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
The previous server program screenshot is shown below.
The following screenshot shows the local IPX addresses enumeration output sample.
The IPX/SPX protocol owned by Novell used in its NetWare OS. Later on, Novell has been taken over by Microfocus.
< IrDA (Infrared) Protocol | Winsock2 Supported Protocols Main | NetBIOS >