< getprotobyname() & getprotobyport() | Internet Protocol Main | IPv6 Client & Server Examples >


 

Winsock2 and Internet Protocol 3 Part 6

 

What do we have in this chapter 3 part 6?

 

  1. The Server Example

  2. Another Day, Another Example: The IPv6 Server Program

 

The Server Example

 

The server side is a bit more involved than the client side. This is because the Windows IPv6 stack is a dual stack. That is, there is a separate stack for IPv4 and IPv6, so if a server wishes to accept both IPv4 and IPv6 connections, it must create a socket for each one. The two steps for creating an IP independent server are the following:

 

  1. Call getaddrinfo() with hints containing AI_PASSIVE, AF_UNSPEC, and the desired socket type and protocol along with the desired local port to listen or receive data on. This will return two addrinfo structures: one containing the listening address for IPv4 and the other containing the listening address for IPv6.
  2. For every addrinfo structure returned, create a socket with the ai_family, ai_socktype, and ai_protocol fields followed by calling bind() with the ai_addr and ai_addrlen members.

 

The following code illustrates this principle.

 

SOCKET                                slisten[16];

char                                         *szPort="5150";

struct addrinfo                        hints, * res=NULL, * ptr=NULL;

int                                            count=0, rc;

 

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

hints.ai_family = AF_UNSPEC;

hints.ai_socktype = SOCK_STREAM;

hints.ai_protocol = IPPROTO_TCP;

hints.ai_flags = AI_PASSIVE;

 

rc = getaddrinfo(NULL, szPort, &hints, &res);

 

if (rc != 0)

{

            // failed for some reason

}

ptr = res;

while (ptr)

{

            slisten[count] = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);

 

            if (slisten[count] == INVALID_SOCKET)

            {

                        // socket failed

            }

 

            rc = bind(slisten[count], ptr->ai_addr, ptr->ai_addrlen);

            if (rc == SOCKET_ERROR)

            {

                        // bind failed

            }

 

            rc = listen(slisten[count], 7);

            if (rc == SOCKET_ERROR)

            {

                        // listen failed

            }

            count++;

            ptr = ptr->ai_next;

}

 

Once the sockets are created and bound, the application simply needs to wait for incoming connections on each. The following program example tries to demonstrate the server part.

 

#include <winsock2.h>

#include <Ws2tcpip.h>

#include <stdio.h>

#include <string.h>

 

int main(int argc, char **argv)

{

    // Declare and initialize variables

    WSADATA            wsaData;

    SOCKET        slisten[16], NewConnection;

    char     *szPort = "7777";

    struct addrinfo            hints, * res=NULL, * ptr=NULL;

    int        count=0, rc;

    char     recvbuff[1024];

    int                    ByteReceived, i;

 

    // Initialize Winsock

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

    {

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

        return 1;

    }

    else

        printf("Server: WSAStartup() looks fine!\n");

 

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

            hints.ai_family = AF_UNSPEC;

            hints.ai_socktype = SOCK_STREAM;

            hints.ai_protocol = IPPROTO_TCP;

            hints.ai_flags = AI_PASSIVE;

 

            rc = getaddrinfo(NULL, szPort, &hints, &res);

            if (rc != 0)

            {

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

                        WSACleanup();

                        return 1;

            }

            else

                        printf("Server: getaddrinfo() is OK...\n");

 

            ptr = res;

 

            while (ptr)

            {

                        printf("\nServer: count value = %d\n", count);

                        // Use the res struct info for listening...

                        slisten[count] = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);

                        if (slisten[count] == INVALID_SOCKET)

                        {

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

                                    WSACleanup();

                                    return 1;

                        }

                        else

                                    printf("Server: socket() is OK...\n");

 

                        // The res struct info

                        printf("\n The address family: %d\n", res->ai_family);

                        printf(" The socket type: %d\n", res->ai_socktype);

                        printf(" The protocol: %d\n\n", res->ai_protocol);

 

                        // Then bind

                        rc = bind(slisten[count], ptr->ai_addr, ptr->ai_addrlen);

 

                        if (rc == SOCKET_ERROR)

                        {

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

                                    WSACleanup();

                                    return 1;

                        }

                        else

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

 

                        // Next, listen

                        rc = listen(slisten[count], 10);

 

                        if (rc == SOCKET_ERROR)

                        {

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

                                    WSACleanup();

                                    return 1;

                        }

                        else

                        {

                                    printf("Server: listen() is OK...\n");

                                    NewConnection = SOCKET_ERROR;

                                    // While the NewConnection socket equal to SOCKET_ERROR

                                    // which is always true in this case...

                                    while(NewConnection == SOCKET_ERROR)

                                    {

                                                // Accept connection on the slisten[count] socket and assign

                                                // it to the NewConnection socket, let the slisten[count]

                                                // do the listening for more connection

                                                NewConnection = accept(slisten[count], NULL, NULL);

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

                                                printf("Server: New client got connected, ready to receive and send data...\n");

 

                                                // Wait for more connections by calling accept again on ListeningSocket (loop)

                                                // or start sending or receiving data on NewConnection.

                                                ByteReceived = recv(NewConnection, recvbuff, sizeof(recvbuff), 0);

 

                                                // When there is problem

                                                if ( ByteReceived == SOCKET_ERROR )

                                                {

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

                                                            WSACleanup();

                                                            break;

                                                }

                                                else

                                                {

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

                                                            // Print the received bytes. Take note that this is the total

                                                            // byte received, it is not the size of the declared buffer

                                                            printf("Server: Bytes received: %d\n", ByteReceived);

                                                            // Print what those bytes represent

                                                            printf("Server: Those bytes are: \"");

                                                            // Print the string only, discard other

                                                            // remaining 'rubbish' in the 1024 buffer size

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

                                                                        printf("%c", recvbuff[i]);

                                                            printf("\"\n");

                                                }

                                    }

                        }

 

                        if(res->ai_protocol == 6)

                        {

                                    printf("Doing the TCP shutdown on the receiving part...\n");

                                    shutdown(slisten[count], SD_RECEIVE);

                        }

 

                        closesocket(slisten[count]);

                        count++;

                        ptr = ptr->ai_next;

            }

 

            freeaddrinfo(res);

            WSACleanup();

            return 0;

}

 

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

The Windows socket/winsock2 IPv4, IPv6 Internet Protocol programming: the IPv6 and IPv4 program sample output

 

Then we run the previous client from the localhost and another peer computer in the same subnet. The ::1 is the IPv6 address for localhost (and make sure the computer is IPv6 enabled). Take note that more control should be implemented for the getaddrinfo() for example, if the address is numeric, getaddrinfo() should be disallowed from doing any name resolution. Otherwise a host name was provided that needs to be resolved when the getaddrinfo() call is issued

 

The Windows socket/winsock2 IPv4, IPv6 Internet Protocol programming: the IPv4 and IPv6 server sample program output with IPv6 address as the argument

 

The following previous client example was run from another computer with IP address 192.168.1.2.

 

The Windows socket/winsock2 IPv4, IPv6 Internet Protocol programming: the IPv4 and IPv6 client sample program output

 

The following screenshot shows the server sample output when both client connections were completed.

 

The Windows socket/winsock2 IPv4, IPv6 Internet Protocol programming: the IPv4 and IPv6 sample program output with IPv4 address argument

 

Another Day, Another Example: The IPv6 Server Program

 

The server used to test this program example is IPv6 enabled and it will fail on IPv4 server. The setsockopt() and getpeername() functions are introduced in this example.

 

#include <winsock2.h>

#include <Ws2tcpip.h>

#include <stdio.h>

 

// Some constants used by this program

#define SERVER_PORT     7171

#define BUFFER_LENGTH    256

#define FALSE              0

 

int main(int argc, char **argv)

{

            // Variable and structure declarations

            WSADATA    wsaData;

            int sd = -1, RetVal, sdconn = -1;

            int rc, on = 1, j = 0, rcdsize=BUFFER_LENGTH;

            char buffer[BUFFER_LENGTH];

            struct sockaddr_in6 serveraddr, clientaddr;

            int addrlen = sizeof(struct sockaddr_storage);

            WCHAR  MyAddrString[256] = L"Some dummy initializer";

            int str = sizeof(struct sockaddr_storage);

            DWORD str2 = sizeof(struct sockaddr_storage);

 

            // Initialize the use of the Winsock DLL by a process

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

            {

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

                        return 1;

            }

            else

                        printf("Server: WSAStartup() looks fine!\n");

 

            // Using a loop for more 'efficient' code

            do

            {

                        // The socket() function returns a socket descriptor, which represents

                        // an endpoint.  Get a socket for address family AF_INET6 to

                        // prepare to accept incoming connections on

                        if ((sd = socket(AF_INET6, SOCK_STREAM, 0)) == INVALID_SOCKET)

                        {

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

                                    break;

                        }

                        else

                                    printf("Server: socket() looks good!\n");

 

                        // The setsockopt() used to allow the local address to be reused when

                        // the server is restarted before the required wait time expires

                        if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) == SOCKET_ERROR)

                        {

                                    printf("Server: setsockopt(SO_REUSEADDR) failed with error code %ld\n", WSAGetLastError());

                                    break;

                        }

                        else

                                    printf("Server: setsockopt(SO_REUSEADDR) found OK!\n");

 

                        // After the socket descriptor is created, a bind() function gets a

                        // unique name for the socket.  In this example, the user sets the

                        // address to in6addr_any, which (by default) allows connections to

                        // be established from any IPv4 or IPv6 client based on the hostname

                        // that specifies port 7171.

                        // That is, the bind is done to both the IPv4 and IPv6 TCP/IP

                        // stacks. However this sample program only accept the IPv4 hostname, then

                        // the client must prepare to convert the IPv4 address to the hostname

                        // before translating the IP string to network address before making a connection

                        // using various Winsock API such as getaddressinfo() etc.

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

                        serveraddr.sin6_family = AF_INET6;

                        serveraddr.sin6_port   = htons(SERVER_PORT);

 

                        // Applications use in6addr_any similarly to the way they use

                        // INADDR_ANY in IPv4.

                        serveraddr.sin6_addr   = in6addr_any;

                        // The remaining fields in the sockaddr_in6 are currently not

                        // supported and should be set to 0 to ensure upward compatibility

                        if (bind(sd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == SOCKET_ERROR)

                        {

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

                                    break;

                        }

                        else

                                    printf("Server: bind() also looks fine!\n");

 

                        // The listen() function allows the server to accept incoming

                        // client connections.  In this example, the backlog is set to 7

                        // This means that the system will queue 10 incoming connection

                        // requests before the system starts rejecting the incoming requests

                        if (listen(sd, 7) == SOCKET_ERROR)

                        {

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

                                    break;

                        }

                        printf("Server: listen() is OK and ready for client connect()...\n");

 

                        // The server uses the accept() function to accept an incoming

                        // connection request.  The accept() call will block indefinitely

                        // waiting for the incoming connection to arrive from an IPv4 or IPv6 client

                        if ((sdconn = accept(sd, NULL, NULL)) == INVALID_SOCKET)

                        {

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

                                    break;

                        }

                        else

                        {

                                    printf("Server: accept() return success!\n");

                                    // Display the client address.  Note that if the client is

                                    // an IPv4 client, the address will be shown as an IPv4 Mapped IPv6 address

                                    if(getpeername(sdconn, (struct sockaddr *)&clientaddr, &addrlen) == SOCKET_ERROR)

                                    {

                                                printf("Server: getpeername() failed miserably with error code %ld\n", WSAGetLastError());

                                    }

                                    else

                                                printf("Server: getpeername() found OK!\n");

 

                                    // converts an IPv4 or IPv6 Internet network address into a string in Internet standard

                                    // format. The ANSI version of this function is inet_ntop() which is used in UNIX/BSD

                                    RetVal = WSAAddressToString((LPSOCKADDR)&clientaddr, addrlen, NULL, MyAddrString, &str);

                                    if(RetVal != 0)

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

                                    else

                                    {

                                                printf("Server: Client address is %S\n", MyAddrString);

                                                printf("Server: Client port is %d\n", ntohs(clientaddr.sin6_port));

                                    }

                        }

 

                        // In this example we know that the client will send 256 bytes of

                        // data over.  Knowing this, we can use the SO_RCVTIMEO socket

                        // option and specify that we don't want our recv() to wake up

                        // until all 256 bytes of data have arrived

                        // http://msdn.microsoft.com/en-us/library/ms740476(VS.85).aspx

                        // Take note that some parameters are Microsoft specific

                        if (setsockopt(sdconn, SOL_SOCKET, SO_RCVTIMEO, (char *)&rcdsize, sizeof(rcdsize)) == SOCKET_ERROR)

                        {

                                    printf("Server: setsockopt(SO_RCVTIMEO) failed with error code %ld\n", WSAGetLastError());

                                    break;

                        }

                        else

                                    printf("Server: setsockopt(SO_RCVTIMEO) looks good!\n");

 

                        // Receive that 256 bytes of data from the client

                        rc = recv(sdconn, buffer, sizeof(buffer), 0);

                        if (rc == SOCKET_ERROR)

                        {

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

                                    break;

                        }

                        else

                        {

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

                        }

 

                        printf("Server: %d bytes of data were received\n", rc);

                        // Just for display!

                        printf("Server: Received money is - ");

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

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

                        printf("\n");

 

                        if (rc == 0 || rc < sizeof(buffer))

                        {

                                    printf("Server: The client closed the connection before sending all the data!\n");

                                    break;

                        }

                        else

                        {

                                    printf("Server: All the data sent by client!\n");

                        }

 

                        // Echo the data back to the client

                        printf("Server: Echoing back the sent data to client...\n");

                        rc = send(sdconn, buffer, sizeof(buffer), 0);

                        if (rc == SOCKET_ERROR)

                        {

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

                                    break;

                        }

                        else

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

            } while (FALSE);

 

            // Program should complete here

            // Do the shutdown() for TCP so receive and send operation will no longer be allowed

            printf("Server: Ready to do the TCP shutdown()...\n");

            if(shutdown(sdconn, SD_BOTH) == SOCKET_ERROR)

                        printf("Server: shutdown sdconn socket and the other end failed with error %ld\n", WSAGetLastError());

            else

                        printf("Server: shutdown() on both end for sdconn socket looks OK...\n");

 

            // Close down any open socket descriptors,

            // BSD/UNIX uses close()

            if (closesocket(sd) == 0)

                        printf("Server: sd socket was closed...\n");

            if (closesocket(sdconn) == 0)

                        printf("Server: sdconn socket was closed...\n");

 

            // Terminates the use of the Ws2_32.DLL for all threads (if any)

            printf("Server: Doing the WSACleanup()...\n");

            if( WSACleanup() == SOCKET_ERROR)

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

            else

                        printf("Server: WSACleanup() confirmed OK!\n");

            return 0;

}

 

The following screenshot shows a sample output.

 

 

 

 


< getprotobyname() & getprotobyport() | Internet Protocol Main | IPv6 Client & Server Examples >