< Another AppleTalk Example | Winsock2 Supported Protocols Main | Bluetooth >
What do we have in this chapter 4 part 10?
|
Asynchronous Transfer Mode (ATM)
The Asynchronous Transfer Mode (ATM) protocol is one of the protocols available that is supported by Winsock 2 on Windows 98, Windows Me, Windows 2000, and Windows XP however Asynchronous Transfer Mode (ATM) has been removed in Vista. ATM is usually used for high-speed networking on LANs and WANs and can be used for all types of communication, such as voice, video, and data requiring high-speed communication. In the real application ATM mostly adopted for the trunk or backbone line. In general, ATM provides guaranteed QOS using Virtual Connections (VCs) on a network. As you will see in a moment, Winsock is capable of using VCs on an ATM network through the ATM address family. An ATM network, as shown in Figure 4-1, typically comprises endpoints (or computers) that are interconnected by switches that bridge an ATM network together.
Figure 4-1 ATM network
There are a few points to be aware of when programming for the ATM protocol. First, ATM is a media type and not really a protocol. That is, ATM is similar to writing Ethernet frames directly on an Ethernet network. Like Ethernet, the ATM protocol doesn't provide flow control. It is a connection-oriented protocol that provides either message or stream modes. This also means that a sending application might overrun the local buffers if data cannot be sent quickly enough. Likewise, a receiving application must post receives frequently; otherwise, when the receiving buffers become full, any additional incoming data might be dropped. If your application requires flow control, one alternative is to use IP over ATM, which is simply the IP protocol running over an ATM network. As a result, the application follows the IP address family. Of course, ATM does offer some advantages over IP, such as a rooted multicast scheme; however, the protocol that best suits you depends on your application's needs. |
An ATM network has two network interfaces: the user network interface (UNI) and the network node interface (NNI). The UNI interface is the communication established between an endpoint and an ATM switch, while the NNI interface is the communication established between two switches. Each of these interfaces has a related communication protocol. These are described here:
For purposes of setting up an ATM connection through Winsock, we will only discuss certain information elements in the UNI signaling protocol. Winsock on Windows XP, Windows 2000, Windows Me, and Windows 98 (service pack 1) supports the UNI version 3.1 signaling protocol.
Winsock allows a client/server application to communicate over an ATM network by setting up an SAP to form connections using the ATM UNI signaling protocol. ATM is a connection-oriented protocol that requires endpoints to establish virtual connections across an ATM network for communication. An SAP simply allows Winsock applications to register and identify a socket interface for communication on an ATM network through a SOCKADDR_ATM address structure. Once an SAP is established, Winsock uses the SAP to establish a virtual connection between a Winsock client and server over ATM by making calls to the ATM network using the UNI signaling protocol. The SOCKADDR_ATM structure is defined as:
typedef struct sockaddr_atm
{
u_short satm_family;
ATM_ADDRESS satm_number;
ATM_BLLI satm_blli;
ATM_BHLI satm_bhli;
} sockaddr_atm, SOCKADDR_ATM, *PSOCKADDR_ATM, *LPSOCKADDR_ATM;
The satm_family field should always be set to AF_ATM. The satm_number field represents an actual ATM address represented as an ATM_ADDRESS structure using one of two basic ATM addressing schemes: E.164 and Network Service Access Point (NSAP). NSAP addresses are also referred to as an NSAP-style ATM Endsystem Address (AESA). The ATM_ADDRESS structure is defined as:
typedef struct
{
DWORD AddressType;
DWORD NumofDigits;
UCHAR Addr[ATM_ADDR_SIZE];
The AddressType field defines the specified addressing scheme. This should be set to ATM_E164 for the E.164 addressing scheme and ATM_NSAP for the NSAP-style addressing scheme. In addition, the AddressType field can be set to other values defined in Table 4-2 when an application tries to bind a socket to an SAP, which we will discuss in more detail later in this chapter. The NumofDigits field should always be set to ATM_ADDR_SIZE. The Addr field represents an actual ATM 20-byte E.164 or NSAP address.
The satm_blli and satm_bhli fields of the SOCKADDR_ATM structure represent Broadband Lower Layer Information (BLLI) and Broadband Higher Layer Information (BHLI) in ATM UNI signaling, respectively. In general, these structures are used to identify the protocol stack that operates over an ATM connection. Several well-known combinations of BHLI and BLLI values are described in ATM Form/IETF documents. (A particular combination of values identifies a connection as being used by LAN Emulation over ATM, another combination identifies native IP over ATM, and so on.) Complete ranges of values for the fields in these structures are given in the ATM UNI 3.1 standards book. ATM Form/IETF documents can be found at www.ietf.org.
Table 4-2 ATM Socket Address Types
|
|
ATM_ADDRESS AddressType Setting |
Type of Address |
ATM_E164 |
An E.164 address; applies when connecting to an SAP |
ATM_NSAP |
An NSAP-style ATM Endsystem Address (AESA); applies when connecting to an SAP |
SAP_FIELD_ANY_AESA_SEL |
An NSAP-style ATM Endsystem Address with the selector octet wildcarded; applies to binding a socket to an SAP |
SAP_FIELD_ANY_AESA_REST |
An NSAP-style ATM Endsystem Address with all the octets except for the selector octet wildcarded; applies to binding a socket to an SAP |
The BHLI and BLLI data structures are defined as:
typedef struct
{
DWORD HighLayerInfoType;
DWORD HighLayerInfoLength;
UCHAR HighLayerInfo[8];
} ATM_BHLI;
typedef struct
{
DWORD Layer2Protocol;
DWORD Layer2UserSpecifiedProtocol;
DWORD Layer3Protocol;
DWORD Layer3UserSpecifiedProtocol;
DWORD Layer3IPI;
UCHAR SnapID[5];
} ATM_BLLI;
Further details of the definition and use of these fields are beyond the scope of this book. An application that simply wants to form Winsock communication over an ATM network should set the following fields in the BHLI and BLLI structures to the SAP_FIELD_ABSENT value:
When these fields are set to this value, none of the other fields in either structure are used. The following pseudocode demonstrates how an application might use the SOCKADDR_ATM structure to set up an SAP for an NSAP address:
SOCKADDR_ATM atm_addr;
UCHAR MyAddress[ATM_ADDR_SIZE];
atm_addr.satm_family = AF_ATM;
atm_addr.satm_number.AddressType = ATM_NSAP;
atm_addr.satm_number.NumofDigits = ATM_ADDR_SIZE;
atm_addr.satm_blli.Layer2Protocol = SAP_FIELD_ABSENT;
atm_addr.satm_blli.Layer3Protocol = SAP_FIELD_ABSENT;
atm_addr.satm_bhli.HighLayerInfoType = SAP_FIELD_ABSENT;
memcpy(&atm_addr.satm_number.Addr, MyAddress, ATM_ADDR_SIZE);
ATM addresses are normally represented as a hexadecimal ASCII string of 40 characters, which corresponds to the 20 bytes that make up either an NSAP-style or an E.164 address in an ATM_ADDRESS structure. For example, an ATM NSAP-style address might look like this:
47000580FFE1000000F21A1D540000D10FED5800
Converting this string to a 20-byte address can be a tedious task. However, Winsock provides a protocol-independent API function, WSAStringToAddress(), which can allow you to convert a 40-character ATM hexadecimal ASCII string to an ATM_ADDRESS structure. Another way to convert a hexadecimal ASCII string to hexadecimal (binary) format is to use the function AtoH() defined in the following code. This function isn't part of Winsock. However, it is simple enough to develop, and you will see it used in the ATM sample described later.
// Function: AtoH
//
// Description: This function coverts the ATM
// address specified in string (ASCII) format to
// binary (hexadecimal) format
void AtoH(CHAR *szDest, CHAR *szSource, INT iCount)
{
while (iCount--)
{
*szDest++ = (BtoH (*szSource++) << 4)
+ BtoH (*szSource++);
}
return;
}
// Function: BtoH
// Description: This function returns the equivalent
// binary value for an individual character specified
// in 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 address will not be accepted
return -1;
}
In ATM, applications can create only connection-oriented sockets because ATM allows communication only over a VC. Therefore, data can be transmitted either as a stream of bytes or in a message-oriented fashion. To open a socket using the ATM protocol, call the socket function or the WSASocket() function with the address family AF_ATM and the socket type SOCK_RAW, and set the protocol field to ATMPROTO_AAL5. For example:
s = socket(AF_ATM, SOCK_RAW, ATMPROTO_AAL5);
s = WSASocket(AF_ATM, SOCK_RAW, ATMPROTO_AAL5, NULL, 0, WSA_FLAG_OVERLAPPED);
By default, opening a socket (as in the example) creates a stream-oriented ATM socket. Windows also features an ATM provider that can perform message-oriented data transfers. Using the message-oriented provider requires you to explicitly specify the native ATM protocol provider to the WSASocket() function by using a WSAPROTOCOL_INFO structure. This is necessary because the three elements in the socket call and the WSASocket() call (address family, socket type, and protocol) match every ATM provider available in Winsock. By default, Winsock returns the protocol entry that matches those three attributes and is marked as default, which in this case is the stream-oriented provider. The following pseudocode demonstrates how to retrieve the ATM message-oriented provider and establish a socket:
dwRet = WSAEnumProtocols(NULL, lpProtocolBuf, &dwBufLen);
for (i = 0; i < dwRet; i++)
{
if ((lpProtocolBuf[i].iAddressFamily == AF_ATM) &&
(lpProtocolBuf[i].iSocketType == SOCK_RAW) &&
(lpProtocolBuf[i].iProtocol == ATMPROTO_AAL5) &&
(lpProtocolBuf[i].dwServiceFlags1 & XP1_MESSAGE_ORIENTED))
{
s = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
FROM_PROTOCOL_INFO, lpProtocolBuf[i], 0, WSA_FLAG_OVERLAPPED);
}
}
ATM addresses are actually quite complicated because the 20 bytes they comprise contain many informational elements. Winsock application programmers need not worry about all the specific details of these elements with the exception of the last byte. The last byte in NSAP-style and E.164 addresses represents a selector value that uniquely allows your application to define and specify a particular SAP on an endpoint. As we described earlier, Winsock uses an SAP to form communication over an ATM network.
When Winsock applications want to communicate over ATM, a server application must register an SAP on an endpoint and wait for a client application to connect on the registered SAP. For a client application, this simply involves setting up a SOCKADDR_ATM structure with the ATM_E164 or ATM_NSAP address type and supplying the ATM address associated with the server's SAP. To create an SAP to listen for connections, your application must first create a socket for the AF_ATM address family. Once the socket is created, your application must define a SOCKADDR_ATM structure using the SAP_FIELD_ANY_AESA_SEL, SAP_FIELD_ANY_AESA_REST, ATM_E164, or ATM_ NSAP address type as defined in Table 4-2. For an ATM socket, an SAP will be created once your application calls the Winsock bind() API function and these address types define how Winsock creates an SAP on your endpoint.
The address type SAP_FIELD_ANY_AESA_SEL tells Winsock to create an SAP that is capable of listening for any incoming ATM Winsock connection, which is known as wildcarding an ATM address and the selector. This means that only one socket can be bound to this endpoint listening for any connection; if another socket tries to bind with this address type, it will fail with Winsock error WSAEADDRINUSE. However, you can have another socket bound explicitly to your endpoint on a particular selector. The address type SAP_FIELD_ANY_AESA_REST can be used to create an SAP that is explicitly bound to a specified selector on an endpoint. This is known as wildcarding only the ATM address and not the selector. You can have only one socket at a time bound to a particular selector on an endpoint or the bind call will fail with error WSAEADDRINUSE. When you use the SAP_FIELD_ANY_AESA_SEL type, you should specify an ATM address of all zeros in the ATM_ADDRESS structure. If you use SAP_FIELD_ANY_AESA_REST, you should specify all zeros for the first 19 bytes of the ATM address and the last byte should indicate which selector number you plan to use.
Sockets that are bound to explicit selectors (SAP_FIELD_ANY_AESA_REST) take higher precedence than those sockets that are bound to a wildcarded selector (SAP_FIELD_ANY_AESA_SEL). Those that are bound to explicit selectors (SAP_FIELD_ANY_AESA_REST) or explicit interfaces (ATM_NSAP and ATM_E164) will get first choice at connections. (That is, if a connection comes in on the endpoint and the selector that a socket is explicitly listening on, that socket gets the connection.) Only when no explicitly bound socket is available will a wildcarded selector socket get the connection.
Finally, a Windows utility named ATMADM allows you to retrieve all ATM address and virtual connection information on an endpoint. This utility can be useful when you are developing an ATM application and need to know which interfaces are available on an endpoint. The command line options listed in Table 4-3 are available.
Table 4-3 ATMADM Program Options
|
|
Parameter |
Description |
-c |
Lists all connections (VC). Lists the remote address and the local interface. |
-a |
Lists all registered addresses (such as all local ATM interfaces and their addresses). |
-s |
Prints statistics (such as current number of calls, number of signaling and ILMI packets sent/received). |
Currently, there are no name providers available for ATM under Winsock. Unfortunately, this requires applications to specify the 20-byte ATM address to establish socket communication over an ATM network. Chapter 8 discusses the Windows 2000 and Windows XP domain name space that can be generically used to register ATM addresses with user-friendly service names. Some info on Windows ATM support can be found at NDIS 5.0 and ATM Support in Windows. More info on Windows support for ATM can be found in Windows ATM Services, Asynchronous Transfer Mode and ATM article. The following program example tries to demonstrate the ATM protocol use in Winsock.
Create a new empty Win32 console mode application and add the project/solution name.
Add the following source code.
// Description:
// This is a simple Winsock ATM client/server echo program.
// The server side requires a local interface to bind to.
// This can be accomplished with the -l:interface option.
// For the client side, simply specify the sever address
// to connect to. The server handles client connections in
// the same thread as the listening socket. This sample
// is meant to illustrate how to use the ATM address family
// and is not how an actual client/server should be implemented
//
// Command Line Parameters/Options
// -a Print out local ATM address
// -s Act as the server
// -l <if > Interface to bind to. Either the index of the
// interface or an NSAP ATM address
// -r <addr> Specify the server's NSAP ATM address (another version is ATM E.164 address)
// -p <port> The 2 digit selector byte (hexadecimal port number)
//
#include "support.h"
#include <stdio.h>
#define MAX_ATM_ADDR_LEN 64
#define MAX_BUFFER 1024
#define NSAP_ADDR_LEN 38
#define SELECTOR_BYTE_LEN 2
WSAPROTOCOL_INFO *lpSocketProtocol=NULL;
char szServerAddr[39], // Server's ATM NSAP address
szPort[3]; // Port number
BOOL bServer = FALSE;
DWORD dwInterface=-1; // Which interface to bind to?
// Function: usage()
// Description: The usage information
int usage(char *progname)
{
printf("Usage: %s -a -s -l interface -r server-addr -p port\n", progname);
printf(" -a Print a list of ATM interface addresses\n");
printf(" -s Act as server\n");
printf(" -l <if > Interface to bind to:\n");
printf(" Interface number (e.g. 0, 1, etc.)\n");
printf(" NSAP ATM address (38 character address)\n");
printf(" -r <addr> NSAP ATM address of server\n");
printf(" -p <port> Selector byte (2 digit hexadecimal port number)\n");
printf("Server example: -s -l 2 -p 12\n");
printf("Sender example: -l 3 -r 4700918100000000613E5BFE010020480811F300 -p 12\n");
printf(" -l 1 -r 11.111122223333444455556666.112233445566.00 -p 12\n");
return 0;
}
// Function: EnumerateATMAddresses()
// Description: Enumerate all ATM interfaces and print their addresses
void EnumerateATMAddresses(WSAPROTOCOL_INFO *lpProtocol)
{
SOCKET s;
SOCKADDR_ATM atm_addr;
char szAddress[MAX_ATM_ADDR_LEN];
DWORD dwNumInterfaces, dwAddrLen=MAX_ATM_ADDR_LEN, i;
s = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, lpProtocol, 0, WSA_FLAG_OVERLAPPED);
if (s == INVALID_SOCKET)
{
printf("WSASocket() failed with error code %d\n", WSAGetLastError());
return;
}
else
printf("WSASocket() is OK!\n");
dwNumInterfaces = GetNumATMInterfaces(s);
for(i=0; i < dwNumInterfaces ;i++)
{
ZeroMemory((PVOID)&atm_addr, sizeof(SOCKADDR_ATM));
ZeroMemory((PVOID)szAddress, MAX_ATM_ADDR_LEN);
if (GetATMAddress(s, i, &atm_addr.satm_number) == FALSE)
{
printf("GetATMAddress() failed!\n");
break;
}
atm_addr.satm_family = AF_ATM;
atm_addr.satm_number.AddressType = ATM_NSAP;
atm_addr.satm_number.NumofDigits = ATM_ADDR_SIZE;
atm_addr.satm_blli.Layer2Protocol = SAP_FIELD_ANY;
atm_addr.satm_blli.Layer3Protocol = SAP_FIELD_ABSENT;
atm_addr.satm_bhli.HighLayerInfoType = SAP_FIELD_ABSENT;
if (WSAAddressToString((LPSOCKADDR)&atm_addr, sizeof(atm_addr),lpProtocol, (LPWSTR)szAddress, &dwAddrLen))
{
printf("WSAAddressToString() failed with error code %d\n", WSAGetLastError());
break;
}
printf("ATM ADDRESS <%d>: '%s'\n", i, szAddress);
}
closesocket(s);
return;
}
// Function: ValidateArgs()
// Description: Parse command line arguments and set global variables accordingly
void 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(argv[0]);
break;
case 'a': // Get my ATM address
EnumerateATMAddresses(lpSocketProtocol);
ExitProcess(1);
break;
case 's': // Act as server
bServer = TRUE;
break;
case 'l': // Use this local interface
if(i+1 < argc)
{
if(strlen(argv[i+1]) == NSAP_ADDR_LEN)
strncpy_s(szServerAddr, sizeof(szServerAddr),argv[i+1],NSAP_ADDR_LEN);
else if(strlen(argv[i+1]) == 1)
dwInterface = atoi(argv[i+1]);
else
usage(argv[0]);
++i;
}
break;
case 'r': // server's address
if(i+1 < argc)
{
if(strlen(argv[i+1]) == NSAP_ADDR_LEN)
strncpy_s(szServerAddr, sizeof(szServerAddr),argv[i+1],NSAP_ADDR_LEN);
else
usage(argv[0]);
++i;
}
break;
case 'p': // server's selector byte (port)
if(i+1 < argc)
{
if(strlen(argv[i+1]) == SELECTOR_BYTE_LEN)
strncpy_s(szPort,sizeof(szPort),argv[i+1],SELECTOR_BYTE_LEN);
else
usage(argv[0]);
++i;
}
break;
default:
usage(argv[0]);
break;
}
}
}
}
return;
}
// Function: main()
// Description:
// This function parses arguments and starts either the client or server
// depending on the arguments. The server will enumerate local ATM
// interfaces if necessary and bind to a local address and wait for
// client connections. The server handles the client connection in the
// same thread as the listening socket. The client only handles one
// connection and then exits. If running the client, create a socket
// and connect to the server. Once connected, send a message and read it back
int main(int argc, char **argv)
{
WSADATA wsd;
SOCKET s;
DWORD dwAddrLen = sizeof(SOCKADDR_ATM);
SOCKADDR_ATM atm_addr;
int ret, i;
char szAddress[41];
// Initialize Winsock and parse command line arguments
if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)
{
printf("WSAStartup() failed with error code %d\n", WSAGetLastError());
return -1;
}
else
printf("WSAStartup() is OK!\n");
// Find the protocol entry supporting ATM and create a socket
lpSocketProtocol = FindProtocol();
if (lpSocketProtocol == NULL)
{
printf("FindProtocol() returned NULL!\n");
return -1;
}
else
printf("FindProtocol() is OK!\n");
s = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
lpSocketProtocol, 0, WSA_FLAG_OVERLAPPED);
if (s == INVALID_SOCKET)
{
printf("WSASocket() failed with error code %d\n", WSAGetLastError());
return -1;
}
else
printf("WSASocket() is OK!\n");
ValidateArgs(argc, argv);
ZeroMemory((PVOID)&atm_addr, sizeof(atm_addr));
atm_addr.satm_family = AF_ATM;
atm_addr.satm_number.AddressType = ATM_NSAP;
atm_addr.satm_number.NumofDigits = 20;
atm_addr.satm_blli.Layer2Protocol = SAP_FIELD_ABSENT;
atm_addr.satm_blli.Layer3Protocol = SAP_FIELD_ABSENT;
atm_addr.satm_bhli.HighLayerInfoType = SAP_FIELD_ABSENT;
// If an interface number was not supplied (i.e. a 38 character NSAP ATM address was supplied)
if ((!bServer) || (dwInterface == -1))
{
strncpy_s(&szAddress[0], sizeof(szAddress[0]),szServerAddr, 38);
strncpy_s(&szAddress[38], sizeof(szAddress[38]),szPort, 2);
szAddress[40] = 0;
AtoH((char *) &atm_addr.satm_number.Addr[0], szAddress, 20);
}
else
{
// An index was supplied, look it up and get its address
if (GetATMAddress(s, dwInterface, &atm_addr.satm_number) == FALSE)
{
printf("Unable to get ATM interface!\n");
}
AtoH((char *) &atm_addr.satm_number.Addr[19], szPort, 1);
}
if (bServer)
{
SOCKET sclient;
SOCKADDR_ATM atm_client;
int atmaddrsz = sizeof(SOCKADDR_ATM);
DWORD dwClientAddrLen=MAX_ATM_ADDR_LEN;
struct fd_set fdaccept;
char recvbuf[MAX_BUFFER], szClientAddr[MAX_ATM_ADDR_LEN], szAddr[MAX_BUFFER];
dwAddrLen = MAX_BUFFER;
// Print out the address we're binding to
if (WSAAddressToString((LPSOCKADDR)&atm_addr, sizeof(atm_addr), lpSocketProtocol, (LPWSTR)szAddr, &dwAddrLen))
{
printf("WSAAddressToString() failed with error code %d\n", WSAGetLastError());
}
else
printf("WSAAddressToString() is OK!\n");
printf("Binding to: <%s>\n", szAddr);
if (bind(s, (SOCKADDR *)&atm_addr, sizeof(atm_addr)) == SOCKET_ERROR)
{
printf("bind() failed with error code %d\n", WSAGetLastError());
return -1;
}
else
printf("bind() should be fine!\n");
if(listen(s, 7) == SOCKET_ERROR)
printf("listen() failed with error code %d\n", WSAGetLastError());
else
printf("listen() is OK! I am listening...\n");
FD_ZERO(&fdaccept);
FD_SET(s, &fdaccept);
if (select(0, &fdaccept, NULL, NULL, NULL) == SOCKET_ERROR)
printf("Something wrong with select() with error code %d\n", WSAGetLastError());
else if (select(0, &fdaccept, NULL, NULL, NULL) == 0)
printf("select() - the time limit expired!\n");
else
printf("select() should be OK!\n");
sclient = WSAAccept(s, (SOCKADDR *)&atm_client, &atmaddrsz, NULL, 0);
if (sclient == INVALID_SOCKET)
{
printf("WSAAccept() failed with error code %d\n", WSAGetLastError());
return -1;
}
else
printf("WSAAccept() should be OK!\n");
ZeroMemory((PVOID)szClientAddr, MAX_ATM_ADDR_LEN);
if (WSAAddressToString((LPSOCKADDR)&atm_client, sizeof(atm_client), lpSocketProtocol, (LPWSTR)szClientAddr, &dwClientAddrLen))
{
printf("WSAAddressToString() failed with herror code %d\n", WSAGetLastError());
}
else
printf("WSAAddressToString() is OK!\n");
printf("Client's ATM ADDRESS: <%s>\n", szClientAddr);
// Handle the client connection until it closes
while (1)
{
ret = recv(sclient, recvbuf, MAX_BUFFER, 0);
if (ret == SOCKET_ERROR)
{
if(WSAEDISCON == WSAGetLastError())
printf("recv() - a connection closed by peer...\n");
else
printf("recv() failed with error code %d\n", WSAGetLastError());
return -1;
}
else if (ret == 0)
{
printf("recv() - a graceful close!\n");
break;
}
else
printf("recv() is OK!\n");
recvbuf[ret] = '\0';
printf("Read: '%s'\n", recvbuf);
ret = send(sclient, recvbuf, ret, 0);
if (ret == SOCKET_ERROR)
{
if(WSAEDISCON == WSAGetLastError())
printf("send() - a connection closed by peer!\n");
else
printf("send() failed with error code %d\n", WSAGetLastError());
return -1;
}
else
printf("send() should be OK!\n");
printf("wrote %d bytes...\n", ret);
}
closesocket(sclient);
}
else
{
char sendbuf[MAX_BUFFER], szAddr[MAX_BUFFER];
dwAddrLen = MAX_BUFFER;
// Connect and then send and recv data
if (WSAAddressToString((LPSOCKADDR)&atm_addr, sizeof(atm_addr), lpSocketProtocol, (LPWSTR)szAddr, &dwAddrLen))
{
printf("WSAAddressToString() failed with error code %d\n", WSAGetLastError());
}
else
printf("WSAAddressToString() is OK\n");
printf("Connect to: <%s>\n", szAddr);
if (connect(s, (SOCKADDR *)&atm_addr, sizeof(atm_addr)) == SOCKET_ERROR)
{
printf("connect() failed with error code %d\n", WSAGetLastError());
return -1;
}
else
printf("connect() is OK!\n");
memset(sendbuf, '$', 512);
for(i=0; i < 10 ;i++)
{
ret = send(s, sendbuf, 64, 0);
if (ret == SOCKET_ERROR)
{
if(WSAEDISCON == WSAGetLastError())
printf("send() - connection closed by peer!\n");
else
printf("send() failed with error code %d\n", WSAGetLastError());
return -1;
}
else
printf("send() should be OK!\n");
printf("Sent %d bytes\n", ret);
ret = recv(s, sendbuf, ret, 0);
if (ret == SOCKET_ERROR)
{
if(WSAEDISCON == WSAGetLastError())
printf("recv() - connection closed by peer!\n");
else
printf("recv() failed with error code %d\n", WSAGetLastError());
return -1;
}
else if (ret == 0)
{
printf("recv() - a graceful close\n");
break;
}
else
printf("recv() is OK!\n");
sendbuf[ret] = '\0';
printf("Read: '%s'\n", sendbuf);
}
}
closesocket(s);
WSACleanup();
return 0;
}
Next, add a new header file.
---------------------------------------------------------------
Then, add the source code.
// Description:
// This file contains function prototypes for functions defined
// in support.cpp
#include <winsock2.h>
#include <Ws2atm.h>
int GetNumATMInterfaces(SOCKET s);
BOOL GetATMAddress(SOCKET s, int device, ATM_ADDRESS *atmaddr);
WSAPROTOCOL_INFO *FindProtocol();
void AtoH( CHAR *szDest, CHAR *szSource, INT iCount );
Next, add the definition file for support.h.
Then, add the source code.
// Description:
// This file contains various support routines used in the
// wsockatm.c file. The functions in this file deal with
// enumerating local ATM interfaces as well as encoding
// a character string addresses into NSAP style ATM addresses
#include "support.h"
#include <stdio.h>
// Function prototype
UCHAR BtoH(CHAR ch);
// Function: GetNumATMInterfaces()
// Description: This function enumerates the number of ATM interfaces on the local machine
int GetNumATMInterfaces(SOCKET s)
{
DWORD dwNum, nbytes=sizeof(DWORD);
if (WSAIoctl(s, SIO_GET_NUMBER_OF_ATM_DEVICES, NULL, 0, (LPVOID)&dwNum, sizeof(DWORD),
&nbytes, NULL, NULL) == SOCKET_ERROR)
{
return -1;
}
else
printf("WSAIoctl(SIO_GET_NUMBER_OF_ATM_DEVICES) seems working...\n");
return dwNum;
}
// Function: GetATMAddress()
// Description:
// This function returns the ATM_ADDRESS corresponding to
// the given interface number. The GetNumATMInterfaces function
// returns the number of interfaces. These number can be
// passed as the device number into this function (ATM devices begin with number 0)
BOOL GetATMAddress(SOCKET s,int device, ATM_ADDRESS *atmaddr)
{
DWORD bytes;
if (WSAIoctl(s, SIO_GET_ATM_ADDRESS, (LPVOID)&device, sizeof(DWORD),
(LPVOID)atmaddr, sizeof(ATM_ADDRESS), &bytes, NULL, NULL) == SOCKET_ERROR)
{
return FALSE;
}
printf("WSAIoctl(SIO_GET_ATM_ADDRESS) seems working...\n");
return TRUE;
}
// Function: FindProtocol()
// Description:
// This function enumerates protocols installed on the system
// with WSAEnumProtocols and searches for ATM transports
WSAPROTOCOL_INFO *FindProtocol()
{
WSAPROTOCOL_INFO *lpProtocolBuf=NULL, *lpRet=NULL;
DWORD dwErr, dwRet, dwBufLen=0, i;
if (WSAEnumProtocols(NULL, lpProtocolBuf, &dwBufLen) != SOCKET_ERROR)
{
// This should never happen as there is a NULL buffer
printf("WSAEnumProtocols() failed with error code %d\n", WSAGetLastError());
return FALSE;
}
else if ((dwErr = WSAGetLastError()) != WSAENOBUFS)
{
// We failed for some reason not relating to buffer size - also odd
printf("WSAEnumProtocols() failed with error code %d\n", dwErr);
return FALSE;
}
else
printf("WSAEnumProtocols() should be fine!\n");
// Allocate the correct buffer size for WSAEnumProtocols as
// well as the buffer to return
lpProtocolBuf = (WSAPROTOCOL_INFO *)GlobalAlloc(GMEM_FIXED, dwBufLen);
if (lpProtocolBuf == NULL)
{
printf("GlobalAlloc() failed with error code %d\n", GetLastError());
return FALSE;
}
else
printf("GlobalAlloc() is OK!\n");
dwRet = WSAEnumProtocols(NULL, lpProtocolBuf, &dwBufLen);
if (dwRet == SOCKET_ERROR)
{
printf("WSAEnumProtocols() failed with error code %d\n", WSAGetLastError());
GlobalFree(lpProtocolBuf);
return FALSE;
}
else
printf("WSAEnumProtocols() should be fine!\n");
// Loop through the returned protocol information looking for those
// that are in the AF_ATM address family
for (i=0; i < dwRet ;i++)
{
if ( (lpProtocolBuf[i].iAddressFamily == AF_ATM) && (lpProtocolBuf[i].iSocketType == SOCK_RAW) &&
(lpProtocolBuf[i].iProtocol == ATMPROTO_AAL5))
{
lpRet = GlobalAlloc(GMEM_FIXED, sizeof(WSAPROTOCOL_INFO));
memcpy(lpRet, &lpProtocolBuf[i], sizeof(WSAPROTOCOL_INFO));
break;
}
}
GlobalFree(lpProtocolBuf);
return lpRet;
}
// Function: AtoH()
// Description:
// This function coverts the ATM 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++ );
}
return;
}
// Function: BtoH
// Description:
// This function 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 address will not be excepted
return -1;
}
Build and run the program.
In order to test the server and sender program, you need to have an ATM adapter card (NIC) installed on you machine. Unfortunately, we don’t have it!
< Another AppleTalk Example | Winsock2 Supported Protocols Main | Bluetooth >