< Chap 4: Winsock2 Supported Protocols | Winsock2 Programming | IPX/SPX Protocol >


 

 

Winsock 2: Other Supported Protocols 4 Part 1

 

 

What do we have in this chapter 4 part 1?

  1. Infrared Sockets

  2. The Addressing Scheme

  3. Name Resolution

  4. Enumerating IrDA Devices

  5. IrDA and getsockopt() using IRLMP_ENUMDEVICES

  6. Initiating a Discovery

  7. Running a Lazy Discovery

  8. Querying the IAS

  9. Socket Options

  10. The IrDA Server Example

  11. The IrDA Client Example

 

To establish communication through Winsock, you must understand how to address a workstation using a particular protocol. In Chapter 3, we described how to do so using the IPv4 and IPv6 address families specifically. This chapter explains other protocols that Winsock supports: IrDA, IPX/SPX, NetBIOS, AppleTalk, ATM and bluetooth and how each protocol resolves an address specific to that family to an actual machine on the network. We'll cover only the basic knowledge necessary to form an address structure for each protocol family. In another chapter we will cover the registration and name resolution functions, which advertise a service of a given protocol family. (This is a bit different from just resolving a name.) For each covered address family, we will discuss the basics of how to address a machine on a network. We will then create a socket for each family. In addition, we will cover the protocol-specific options for name resolution.

 

Infrared Sockets

 

Infrared sockets, or IrSock, are technology first introduced on the Windows CE platform. Infrared sockets allow two PCs to communicate with each other through an infrared serial port. Infrared sockets already available on Windows 98, Windows Me, Windows 2000, and Windows XP. Infrared sockets differ from traditional sockets in that infrared sockets are designed to take into account the transient nature of portable computing. Infrared sockets present a new name resolution model that will be discussed in the next section.

 

The Addressing Scheme

 

Because most computers with Infrared Data Association (IrDA) devices are likely to move around, traditional name-resolution schemes don't work well. Conventional resolution methods assume that the use of static resources such as name servers, which cannot be used when a person is moving a handheld PC or laptop computer running a network client. To circumvent this problem, IrSock is designed to browse in-range resources in an ad hoc manner without the overhead of a large network, and it doesn't use standard Winsock name service functions or even IP addressing. Instead, the name service has been incorporated into the communication stream, and a new address family has been introduced to support services bound to infrared serial ports. The IrSock address structure includes a service name that describes the application used in bind() and connect() calls and a device identifier that describes the device on which the service runs. This pair is analogous to the IP address and port number pair used by conventional TCP/IP sockets. The IrSock address structure is defined as:

 

typedef struct _SOCKADDR_IRDA

{

    u_short     irdaAddressFamily;

    u_char      irdaDeviceID[4];

    char        irdaServiceName[25];

} SOCKADDR_IRDA, *PSOCKADDR_IRDA, FAR *LPSOCKADDR_IRDA;

 

The irdaAddressFamily field is always set to AF_IRDA. The irdaDeviceID is a four-character string that uniquely identifies the device on which a particular service is running. This field is ignored when an IrSock server is created. However, the field is significant for a client because it specifies which IrDA device to connect to because there can be multiple devices in range. Finally, the irdaServiceName field is the name of the service that the application either will register itself with or is trying to connect to.

 

Name Resolution

 

Addressing can be based on IrDA Logical Service Access Point Selectors (LSAP-SELs) or on services registered with the Information Access Services (IAS). The IAS abstracts a service from an LSAP-SEL into a user-friendly text service name, in much the same way that an Internet domain name server maps names to numeric IP addresses. You can use either an LSAP-SEL or a user-friendly name to connect successfully, but user-friendly names require name resolution. For the most part, you shouldn't use the direct LSAP-SEL “address” because its address space for IrDA services is limited. The Windows implementation allows LSAP-SEL integer identifiers in the range of 1 to 127. Essentially, an IAS server can be thought of as a WINS server because it associates an LSAP-SEL with a textual service name.

An actual IAS entry has three fields of importance: class name, attribute, and attribute value. For example, let's say a server wishes to register under the service name MyServer. This is accomplished when the server issues the bind() call with the appropriate SOCKADDR_IRDA structure. Once this occurs, an IAS entry is added with a class name MyServer, the attribute Irda:TinyTP:LsapSel, and an attribute value of, say, 3. The attribute value is the next unused LSAP-SEL assigned by the system upon registration. The client, on the other hand, passes in a SOCKADDR_IRDA structure to the connect call. This initiates an IAS lookup for a service with the class name MyServer and the attribute Irda:TinyTP:LsapSel. The IAS query will return the value 3. You also can formulate your own IAS query by using the socket option IRLMP_IAS_QUERY in the getsockopt() call.

If you want to bypass IAS altogether (which is not recommended), you can specify an LSAP-SEL address directly for a server name or an endpoint to which a client wants to connect. You should bypass IAS only to communicate with legacy IrDA devices that don't provide any kind of IAS registration (such as infrared-capable printers). You can bypass the IAS registration and lookup by specifying the service name in the SOCKADDR_IRDA structure as LSAP-SEL-xxx, in which xxx is the attribute value between 1 and 127. For a server, this would directly assign the server to the given LSAP-SEL address (assuming the LSAP-SEL address is unused). For a client, this bypasses the IAS lookup and causes an immediate attempt to connect to whatever service is running on that LSAP-SEL.

 

 

 

 

Enumerating IrDA Devices

 

Because infrared devices move in and out of range, a method of dynamically listing all available infrared devices within range is necessary. This section describes how to accomplish that. Let's begin with a few platform discrepancies between the Windows CE implementation and the Windows 98, Windows Me, Windows 2000, and Windows XP implementation. Windows CE supported IrSock before the other platforms and provided minimal information about infrared devices. Later, Windows 98, Windows Me, Windows 2000, and Windows XP provided support for IrSock, but they added additional “hint” information that the enumeration request returned. (This hint information will be discussed shortly.) As a result, the AF_IRDA.H header file for Windows CE contains the original, minimal structure definitions; however, the new header file for the other platforms contains conditional structure definitions for each platform that now supports IrSock. We recommend that you use the later AF_IRDA.H header file for consistency.

The AF_IRDA.H header file must be included by Windows Sockets applications to support IrDA. Several incompatible versions of AF_IRDA.H have been distributed, including those shipped with Windows CE and Windows 95 Ir3.0 DDKs and SDKs. A common AF_IRDA.H header file that supports all three platforms is available with the Windows IrDA DDK. This file may continue to evolve as Windows programmatic elements and platforms continue to merge. To compile for a target platform, one of the following must be defined:

 

_WIN32_WINNT

_WIN32_WCE

_WIN32_WINDOWS

 

IrDA and getsockopt() using IRLMP_ENUMDEVICES

 

The Windows Sockets getsockopt() function is used to run a discovery. Before a connection can be initiated, a device address must be obtained by performing a discovery operation. An extension to the Winsock getsockopt() call returns a list of all currently visible IrDA devices. A device address returned from this list is copied into a SOCKADDR_IRDA structure to be used by the connect() function call.

 

Initiating a Discovery

 

Performing a getsockopt() function call with the IRLMP_ENUMDEVICES option causes a single discovery to be run on each idle adapter. The list of discovered devices and cached devices (on active adapters) will be returned immediately. The following code demonstrates this:

 

SOCKADDR_IRDA     DestSockAddr = { AF_IRDA, 0, 0, 0, 0, "SampleIrDAService" };

 

#define DEVICE_LIST_LEN    10

 

unsigned char    DevListBuff[sizeof(DEVICELIST) - sizeof(IRDA_DEVICE_INFO) + (sizeof(IRDA_DEVICE_INFO) * DEVICE_LIST_LEN)];

int        DevListLen    = sizeof(DevListBuff);

PDEVICELIST    pDevList    = (PDEVICELIST) &DevListBuff;

 

pDevList->numDevice = 0;

 

// Sock is not in connected state

if (getsockopt(Sock, SOL_IRLMP, IRLMP_ENUMDEVICES, (char *) pDevList, &DevListLen) = = SOCKET_ERROR)

{

// WSAGetLastError

}

 

if (pDevList->numDevice = = 0)

{

    // no devices discovered or cached

    // not a bad idea to run a couple of times

}

else

{

    // one per discovered device

for (i = 0; i < (int) pDevList->numDevice; i++)

{

// typedef struct _IRDA_DEVICE_INFO

// {

        //    u_char    irdaDeviceID[4];

        //    char    irdaDeviceName[22];

        //    u_char    irdaDeviceHints1;

        //     u_char    irdaDeviceHints2;

        //    u_char    irdaCharSet;

// } _IRDA_DEVICE_INFO;

 

    // pDevList->Device[i]. see _IRDA_DEVICE_INFO for fields

    // display the device names and let the user select one

}

}

 

// assume the user selected the first device [0]

memcpy(&DestSockAddr.irdaDeviceID[0], &pDevList->Device[0].irdaDeviceID[0], 4);

 

if (connect(Sock, (const struct sockaddr *) &DestSockAddr, sizeof(SOCKADDR_IRDA)) == SOCKET_ERROR)

{

    // WSAGetLastError()

}

 

Running a Lazy Discovery

 

It is also possible to run a lazy discovery, the application will not be notified until the discovered device list changes from the last discovery run by the stack.

Now that you know how to enumerate infrared devices, creating a client or a server is simple. The server side of the equation is a bit simpler because it looks like a “normal” server. That is, no extra steps are required. The five general steps for an IrSock server are as follows:

 

1.      Create a socket of address family AF_IRDA and of socket type SOCK_STREAM.

 

The Windows Sockets socket() function creates a connection endpoint of type SOCKET. The connection endpoint is an application anchor for future references to a connection; the connection is not established with this function call. Clients and servers begin all communication by opening a socket. The following sample demonstrates using the socket() function for an IrDA application:

 

SOCKET ServSock;

 

// if(s = WSASocket(AF_IRDA, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED) == == INVALID_SOCKET)

 

if ((ServSock = socket(AF_IRDA, SOCK_STREAM, 0)) == INVALID_SOCKET)

{

        // WSAGetLastError()

   }

 

If you want to be specific, you can pass IRDA_PROTO_SOCK_STREAM as the protocol parameter of either function. However, the protocol parameter isn't required because the transport catalog has only one entry of address family AF_IRDA. Specifying AF_IRDA causes that transport entry to be used by default.

As in the standard socket programming, IrDA programmers must call the Windows Sockets WSAStartup() function before making any other IrDA function calls. The following is an example of making a WSAStartup() function call for an IrDA application:

 

WORD        WSAVerReq = MAKEWORD(2,2);

WSADATA        WSAData;

 

if (WSAStartup(WSAVerReq, &WSAData) != 0)

{

        // wrong winsock dlls?

   }

 

2.      Fill out a SOCKADDR_IRDA structure with the service name of the server.

3.      Call bind() with the socket handle and the SOCKADDR_IRDA structure.

 

The Windows Sockets bind() function is used by IrDA server applications to register to receive incoming connections addressed to a specified service name on the specified socket. The bind() function associates a server socket with an application-level address. The following sample demonstrates using the bind() function for an IrDA application:

 

SOCKADDR_IRDA     ServSockAddr = {AF_IRDA, 0, 0, 0, 0, "SampleIrDAService”};

int        SizeOfSockAddr;

 

if (bind(ServSock, (const struct sockaddr *) &ServSockAddr, sizeof(SOCKADDR_IRDA)) == SOCKET_ERROR)

{

    // WSAGetLastError()

   }

 

A bind function call in an IrDA application causes the stack to generate a new local LSAP-SEL and add it to the IAS database associated with the service name supplied in SOCKADDR_IRDA.

 

4.      Call listen() with the socket handle and the backlog limit.

 

The Windows Sockets listen() function is used by IrDA server applications to place the stack in a mode to receive incoming connections on the specified socket. The listen() function does not block.

 

if (listen(ServSock, 2) == SOCKET_ERROR)

{

    // WSAGetLastError()

   }

 

The backlog parameter specifies how many inbound connections the stack should accept on behalf of the application before the application is able to further process (using the Windows Sockets accept() function) those connections.

 

5.      Block on an accept() call for incoming clients.

 

Once an IrDA server application puts its server socket into listen mode, the IrDA server application calls the Windows Sockets accept() function and blocks until an incoming connection is received. The following sample demonstrates using the accept() function for an IrDA application:

 

SOCKET NewSock;

 

while(1)

{

      sizeofSockAddr = sizeof(SOCKADDR_IRDA);

     

      if ((NewSock = accept(ServSock, (struct sockaddr *) &PeerSockAddr, &sizeofSockAddr)) == INVALID_SOCKET)

      {

                  // WSAGetLastError()

                  // exit

      }

     

      // NewSock is a connected socket

      // create a new thread and pass it NewSock, return to accept() on main

      // or use NewSock here until done, then close it

}

 

An unusual characteristic of the accept() function with IrDA is that it returns a new socket. The reason is that the server application may wish to continue accepting new inbound connections. To support that, the server typically creates a new thread to handle the new connection, then blocks again on another accept function call. There is no requirement that a simple server supports multiple connections, and it is free to ignore the old listening socket until it is finished with the newly created socket. The SOCKADDR_IRDA structure passed to the accept() function is filled in with the peer's addresses, and can usually be ignored.

 

The steps for a client are a bit more involved because you must enumerate infrared devices. The following four steps are necessary for an IrSock client.

 

1.      Create a socket of address family AF_IRDA and socket type SOCK_STREAM.

2.      Enumerate available infrared devices by calling getsockopt() with the IRLMP_ENUM_DEVICES option.

3.      For each device returned, fill out a SOCKADDR_IRDA structure with the device ID returned and the service name you want to connect to.

4.      Call the connect() function with the socket handle and with the SOCKADDR_IRDA structure. Do this for each structure filled out in step 3 until a connect succeeds.

 

// Connect to server.

if (connect(ConnectSocket, (SOCKADDR_IRDA *) &clientService, sizeof(clientService) ) == SOCKET_ERROR)

{

    printf("Failed to connect.\n");

    WSACleanup();

    return;

}

 

printf("Connected to server.\n");

 

Some notes:

 

a.             The AF_IRDA.H header file must be explicitly included.

b.             If an existing IrDA connection is detected at the media-access level, WSAENETDOWN is returned.

c.             If active connections to a device with a different address exist, WSAEADDRINUSE is returned.

d.             If the socket is already connected or an exclusive/multiplexed mode change failed, WSAEISCONN is returned.

e.             If the socket was previously bound to a local service name to accept incoming connections using bind(), WSAEINVAL is returned. Note that once a socket is bound, it cannot be used for establishing an outbound connection.

 

IrDA implements the connect() function with addresses of the form sockaddr_irda. Typically, a client application will create a socket with the socket function, scan the immediate vicinity for IrDA devices with the IRLMP_ENUMDEVICES socket option, choose a device from the returned list, form an address, and then call connect(). There is no difference between blocking and nonblocking semantics.

 

6.      Then we are ready to send and receive data

 

The send() and recv() functions are used by IrDA applications to transfer data. The recv() function blocks until data is available, and the send() function normally will not block. The following sample demonstrates using the send() and recv() functions for an IrDA application:

 

int    BytesRead, BytesSent;

char    Buffer[4096];

 

// recv() example

if ((BytesRead = recv(Sock, Buffer, sizeof(Buffer), 0)) == SOCKET_ERROR)

{

    // WSAGetLastError()

}

if (BytesRead == 0)

{

    // peer has closed the connection and I have all the data

    // close the socket now

}

 

// send() example

if ((BytesSent = send(Sock, Buffer, sizeof(Buffer), 0)) == SOCKET_ERROR)

{

    // WSAGetLastError()

}

     // check that BytesSent == sizeof(Buffer)

 

The send() function can block if the peer is not receiving data, indicated by its cessation of recv() function calls. The send() function stops blocking when the peer resumes recv() calls. A recv() call with a length of zero has the special meaning: the client has performed a graceful close on the socket. In this case, the application can assume it has received all data that was sent by the peer. If any unrecoverable protocol error occurs during the connection, the send() or recv() functions return an error code and the connection is aborted.

 

For both server and client don’t forget to close those opening sockets. The Windows Sockets closesocket() function initiates a graceful close on the connection, and releases the socket handle.

The following sample demonstrates using the closesocket() functions for an IrDA application:

 

if (closesocket(Sock) == SOCKET_ERROR)

{

    // WSAGetLastError()

}

 

 

 

 

Querying the IAS

 

Only limited access to the IAS database is available through Windows Sockets, however, the IAS database is not normally used by applications. The IAS database exists to support connections to non-Windows devices that are not compliant with Windows Sockets and IrDA conventions.

The following structure, IAS_SET, is used with the Windows Sockets setsockopt() function and IRLMP_IAS_SET to manage the local IAS database:

 

typedef struct _IAS_SET

{

            char    irdaClassName[IAS_MAX_CLASSNAME];

            char    irdaAttribName[IAS_MAX_ATTRIBNAME];

            u_long    irdaAttribType;

 

            union

            {

                        LONG irdaAttribInt;

                        struct

                        {

                                    u_short    Len;

                                    u_char    OctetSeq[IAS_MAX_OCTET_STRING];

                        } irdaAttribOctetSeq;

                        struct

                        {

                                    u_char    Len;

                                    u_char    CharSet;

                                    u_char    UsrStr[IAS_MAX_USER_STRING];

                        } irdaAttribUsrStr;

            } irdaAttribute;

} IAS_SET, *PIAS_SET, FAR *LPIAS_SET;

 

The IAS_QUERY structure, defined below, is used with the getsockopt() function and IRLMP_IAS_QUERY to query the IAS database on a peer:

 

typedef struct _WINDOWS_IAS_QUERY

{

            u_char    irdaDeviceID[4];

            char    irdaClassName[IAS_MAX_CLASSNAME];

            char    irdaAttribName[IAS_MAX_ATTRIBNAME];

            u_long    irdaAttribType;

 

            union

            {

                        LONG    irdaAttribInt;

 

                        struct

                        {

                                    u_long    Len;

                                    u_char    OctetSeq[IAS_MAX_OCTET_STRING];

                        } irdaAttribOctetSeq;

 

                        struct

                        {

                                    u_long  Len;

                                    u_long    CharSet;

                                    u_char    UsrStr[IAS_MAX_USER_STRING];

                        } irdaAttribUsrStr;

            } irdaAttribute;

} IAS_QUERY, *PIAS_QUERY, FAR *LPIAS_QUERY;

 

Performing a query for the LSAP-SEL number of a particular service is simple: set the irdaClassName field to the property string for LSAP-SELs, which is “IrDA:IrLMP:LsapSel”, and set the irdaAttributeName field to the service name you want to query for. In addition, you have to set the irdaDeviceID with a valid device within range.

 

Socket Options

 

Many SO_ socket options aren't meaningful to IrDA. Only SO_LINGER and SO_DONTLINGER are specifically supported. The IrSock-specific socket options are of course supported only on sockets of the address family AF_IRDA. These options are also covered in another chapter, which summarizes all socket options and their parameters.

The following is a basic client and server IrSock application program examples.

 

The IrDA Server Example

 

// Link to ws2_32.lib

#include <winsock2.h>

#include <af_irda.h>

#include <stdio.h>

 

// Change the service name accordingly

#define IR_SERVICE_NAME                     "MIKEBLUR"

#define MAX_BUFFER                               4096

 

int main(int argc, char **argv)  

{

            WSADATA       wsaData;

            SOCKET        sock, sockClient;

            // irdaAddressFamily - Address family. This member is always AF_IRDA.

            // irdaDeviceID[4] - Device identifier (ID) of the IrDA device to which the

            //                                                          client wants to issue the connect function call. Ignored by server applications.

            // irdaServiceName[25] - Well-known service name associated with a server application.

            //                                                                      Specified by servers during their bind function call.

            SOCKADDR_IRDA irAddr = {AF_IRDA, 0, 0, 0, 0, "\0"}, remoteIrAddr;

            int           iIrSize = sizeof(SOCKADDR_IRDA);

            BOOL          bDone=FALSE;

            int           ret;

            char          szRecvBuff[MAX_BUFFER];

 

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

            {

                        printf("IRServer: WSAStartup() failed with error code %ld\n", WSAGetLastError());

                        return 1;

            }

            else

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

 

            if ((sock = socket(AF_IRDA, SOCK_STREAM, 0)) == INVALID_SOCKET)

            {

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

                        WSACleanup();

                        return 1;

            }

            else

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

 

            printf("IRServer: Filling up the service name into the SOCKADDR_IRDA struct...\n");

            // Better to verify strcpy_s() returned value, not done here - EINVAL or ERANGE

            strcpy_s(irAddr.irdaServiceName, sizeof(irAddr.irdaServiceName), IR_SERVICE_NAME);

 

            // Binding our socket to the local service name

            printf("IRServer: Binding to the service name: %s\n", irAddr.irdaServiceName);

            if (bind(sock, (struct sockaddr *)&irAddr, sizeof(SOCKADDR_IRDA)) == SOCKET_ERROR)

            {

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

                closesocket(sock);

                WSACleanup();

                return 1;

            }

            else

                printf("IRServer: bind() looks pretty much OK!\n");

 

            if (listen(sock, 10) == SOCKET_ERROR)

            {

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

                        closesocket(sock);

                        WSACleanup();

                        return 1;

            }

            else

                        printf("IRServer: listen() is OK! I were bound and listening lol...\n");

 

        while (1)

        {

            if ((sockClient = accept(sock, (struct sockaddr *)&remoteIrAddr, &iIrSize)) == SOCKET_ERROR)  

            {

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

                break;

           }

           else

           {

                printf("IRServer: accept() is fine....\n");

                printf("Remote info: %s\n", remoteIrAddr.irdaDeviceID);

           }

 

               if ((ret = recv(sock, szRecvBuff, MAX_BUFFER, 0)) == SOCKET_ERROR)

               {

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

                           break;

               }

               else if (ret == 0)

               {

                           printf("IRServer: connection has been gracefully closed, error code %ld\n", WSAGetLastError());

                           break;

               }

               else

                           printf("IRServer: Successfully received %d bytes\n", ret);

 

               // Echo data back to client

               printf("IRServer: Echoing the received data back to client...\n");

               if ((ret = send(sock, szRecvBuff, ret, 0)) == SOCKET_ERROR)

               {

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

                           break;

               }

               else

               {

                           printf("IRServer: send() is working!\n");

                           printf("IRServer: Successfully sent %d bytes\n", ret);

               }

            }

 

            printf("IRServer: Closing connection...\n");

            if(closesocket(sock) == SOCKET_ERROR)

                        printf("IRServer: Failed to close sock socket, error code %ld!\n", WSAGetLastError());

            else

                        printf("IRServer: sock socket successfully closed!\n");

 

            if(WSACleanup() == SOCKET_ERROR)

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

            else

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

 

            return 0;

}

 

Build and run the project. The following screenshot is a sample server/receiver output.

 

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

Winsock 2, Other Supported Protocols: a basic client and server IrSock application program examples - sample output

 

The IrDA Client Example

 

#include <winsock2.h>

#include <af_irda.h>

#include <stdio.h>

 

// This service name should match the name set in the

// server program, change accordingly

#define IR_SERVICE_NAME "MIKEBLUR"

#define TEST_STRING "Sending all my love to youuu! Retard IR server!"

#define MAX_RETRIES     10

#define MAX_BUFFER      4096

 

//    This function attempts to locate any IR capable devices within

//    range. This is done by calling the IRLMP_ENUMDEVICES socket

//    option. We call this several times in a loop to make a good

//    effort to find any.  Upon success a DEVICELIST structure is

//    filled in with the device IDs of the discovered device.

int FindDevices(SOCKET s, DEVICELIST *devlist)

{

            int          dwNumRetries=0, dwDevListSz;

            int          ret;

            dwDevListSz = sizeof(*devlist);

            devlist->numDevice = 0;

 

            while ((devlist->numDevice == 0) && (dwNumRetries <= MAX_RETRIES))

            {

                        printf("IR-Client: Retry #%d\n", dwNumRetries);

                        ret = getsockopt(s, SOL_IRLMP, IRLMP_ENUMDEVICES, (char *)devlist, &dwDevListSz);

 

                        if (ret == SOCKET_ERROR)

                        {

                                    printf("IR-Client: getsockopt() failed with error %d\n", WSAGetLastError());

                                    // return 0 indicates error

                                    return 0;

                        }

                        else

                                    printf("IR-Client: getsockopt() looks fine!\n");

 

                        dwNumRetries++;

                        // Sleep for a while...

                        Sleep(1000);

            }

 

            if (dwNumRetries > MAX_RETRIES)

            {

                        // Number of retries expired

                        printf("IR-Client: Number of %d retries expired!\n", MAX_RETRIES);

                        return 0;

            }

            // Else return the device number...

            return devlist->numDevice;

}

 

int main(int argc, char **argv)

{

            WSADATA         wsaData;

            SOCKET          sock;

            SOCKADDR_IRDA   irAddr = {AF_IRDA, 0, 0, 0, 0, "\0"};

            DWORD           dwIrSize = sizeof(SOCKADDR_IRDA), i, j;

            int             ret;

            DEVICELIST      devlist;

            char            szRecvBuff[MAX_BUFFER];

 

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

            {

                        printf("IR-Client: Unable to load the Winsock library!\n");

                        return 1;

            }

            else

                        printf("IR-Client: Winsock library 2.2 loaded!\n");

 

            if ((sock = socket(AF_IRDA, SOCK_STREAM, 0)) == INVALID_SOCKET)

            {

                        printf("IR-Client: socket() failed with error %d\n", WSAGetLastError());

                        return 1;

            }

            else

                        printf("IR-Client: socket() looks fine!\n");

 

            // Find devices in range and store them in devlist...

            if (FindDevices(sock, &devlist) == 0)

            {

                        printf("IR-Client: No IrDA devices in range!\n");

                        // returns 1 indicates error

                        return 1;

            }

            else

                        printf("IR-Client: FindDevices() is OK!\n");

 

            printf("IR-Client: Found %d devices\n", devlist.numDevice);

 

            // Setup the SOCKADDR_IRDA structure with the service name we want to connect to.

            strcpy_s(irAddr.irdaServiceName, sizeof(irAddr.irdaServiceName), IR_SERVICE_NAME);

            // For every device...

            for(i=0; i < devlist.numDevice ;i++)

            {

                        printf("IR-Client: i = %d, devlist.numDevice = %d\n", i, devlist.numDevice);

                        // Fill up the device ID into irdaDeviceID[j] array

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

                        {

                                    irAddr.irdaDeviceID[j] = devlist.Device[i].irdaDeviceID[j];

                        }

                        // Attempt to connect...

                        printf("IR-Client: Attempting connection to %x%x%x%x, %s\n", devlist.Device[i].irdaDeviceID[0],

                                    devlist.Device[i].irdaDeviceID[1],

                                    devlist.Device[i].irdaDeviceID[2],

                                    devlist.Device[i].irdaDeviceID[3], irAddr.irdaServiceName);

 

                        if (connect(sock, (struct sockaddr *)&irAddr, sizeof(SOCKADDR_IRDA)) == SOCKET_ERROR)

                        {

                                    if (i == (devlist.numDevice - 1))

                                    {

                                                printf("IR-Client: Unable to locate service: '%s'\n", irAddr.irdaServiceName);

                                                printf("IR-Client: Error code is %ld\n", WSAGetLastError());

                                                return 1;

                                    }

                                    else

                                                printf("IR-Client: Connection attempt failed with error code %ld\n", WSAGetLastError());

                                    continue;

                        }

                        else

                        {

                                    printf("connect() should be OK!\n");

                                    break;

                        }

            }

 

            // Send the TEST_STRING to the server and receive it back.

            if ((ret = send(sock, TEST_STRING, strlen(TEST_STRING), 0)) == SOCKET_ERROR)

            {

                        printf("IR-Client: send() failed with error %d\n", WSAGetLastError());

                        closesocket(sock);

                        WSACleanup();

                        return 1;

            }

            else

                        printf("IR-Client: send() also looks fine!\n");

 

            printf("IR-Client: Successfully sent %d bytes\n", ret);

            if ((ret = recv(sock, szRecvBuff, MAX_BUFFER, 0)) == SOCKET_ERROR)

            {

                        printf("IR-Client: recv() failed with error code%d\n", WSAGetLastError());

                        closesocket(sock);

                        WSACleanup();

                        return 1;

            }

            else

                        printf("IR-Client: recv() seems working...\n");

 

            printf("Successfully received %d bytes\n", ret);

            printf("Closing socket\n");

 

            if(closesocket(sock) == SOCKET_ERROR)

                        printf("IR-Client: Failed to close sock socket, error code %ld!\n", WSAGetLastError());

            else

                        printf("IR-Client: sock socket successfully closed!\n");

 

            if(WSACleanup() == SOCKET_ERROR)

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

            else

                        printf("IR-Client: WSACleanup() is OK!\n");

            return 0;

}

 

Build and run the project. The following screenshot is a sample client/sender output.

 

Winsock 2, Other Supported Protocols: The IrDA Client Example - a sample output

 

After certain amount of time, the client will exit with error code 10060 if there is no listening server.

 

Winsock 2, Other Supported Protocols: The IrDA Client Example - a sample output with 10060 error code

 

In order to test both client and server program successfully, you must make sure the infrared transceivers are functioning and both receivers are in the range. We can’t use 'loopback' in this case. This thing can be verified through the infrared icon as shown below. Unfortunately, the infrared devices on the machine we are using to test these infrared client-server programs are not working.

 

Winsock 2, Other Supported Protocols: The IrDA icon seen on the tray icon

 

 

 


< Chap 4: Winsock2 Supported Protocols | Winsock2 Programming | IPX/SPX Protocol >