Example: Accepting connections from both IPv6 and IPv4 clients

This example program demonstrates how to create a server/client model that accepts requests from both IPv4 (those socket applications that use the AF_INET address family) and IPv6 (those applications that use the AF_INET6 address family).

Currently your socket application can only use the AF_INET address family, which allows for TCP and User Datagram Protocol (UDP) protocol; however, this might change with the increase in the use of IPv6 addresses. You can use this sample program to create your own applications that accommodate both address families.

This figure shows how this example program works:

Socket flow of events: Server application that accepts requests from both IPv4 and IPv6

Socket flow of events: Server application that accepts requests from both IPv4 and IPv6 clients

This flow describes each of the API calls and what they do within the socket application that accepts requests from both IPv4 and IPv6 clients.

  1. The socket() API specifies a socket descriptor that creates an endpoint. It also specifies the AF_INET6 address family, which supports IPv6, and the TCP transport (SOCK_STREAM) is used for this socket.
  2. The setsockopt() API allows an application to reuse the local address when the server is restarted before the required wait time expires.
  3. A bind() API supplies a unique name for the socket. In this example, the programmer sets the address to in6addr_any, which (by default) allows connections to be established from any IPv4 or IPv6 client that specifies port 3005 (that is, the bind is done to both the IPv4 and IPv6 port spaces).
    Note: If the server only needs to handle IPv6 clients, then IPv6_ONLY socket option can be used.
  4. The listen() API allows the server to accept incoming client connections. In this example, the programmer sets the backlog to 10, which allows the system to queue ten connection requests before the system starts rejecting incoming requests.
  5. The server uses the accept() API to accept an incoming connection request. The accept() call blocks indefinitely, waiting for the incoming connection to arrive from an IPv4 or IPv6 client.
  6. The getpeername() API returns the client's address to the application. If the client is an IPv4 client, the address is shown as an IPv4–mapped IPv6 address.
  7. The recv() API receives 250 bytes of data from the client. In this example, the client sends 250 bytes of data over. Knowing this, the programmer uses the SO_RCVLOWAT socket option and specifies that the recv() API to not wake up until all 250 bytes of data have arrived.
  8. The send() API echoes the data back to the client.
  9. The close() API closes any open socket descriptors.

Socket flow of events: Requests from either IPv4 or IPv6 clients

Note: This client example can be used with other server application designs that want to accept request for either IPv4 or IPv6 nodes. Other server designs can be used with this client example.
  1. The inet_pton() call converts the text form of the address to the binary form. In this example, two of these calls are issued. The first determines if the server is a valid AF_INET address. The second inet_pton() call determines whether the server has an AF_INET6 address. If it is numeric, getaddrinfo() should be prevented from doing any name resolution. Otherwise a host name was provided that needs to be resolved when the getaddrinfo() call is issued.
  2. The getaddrinfo() call retrieves the address information needed for the subsequent socket() and connect() calls.
  3. The socket() API returns a socket descriptor, which represents an endpoint. The statement also identifies the address family, socket type, and protocol using the information returned from the getaddrinfo() API call.
  4. The connect() API establishes a connection to the server regardless of whether the server is IPv4 or IPv6.
  5. The send() API sends the data request to the server.
  6. The recv() API receives data from the server application.
  7. The close() API closes any open socket descriptors.

The following sample code shows the server application for this scenario.

Note: By using the examples, you agree to the terms of the Code license and disclaimer information.
/**************************************************************************/
/* Header files needed for this sample program                            */
/**************************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

/**************************************************************************/
/* Constants used by this program                                         */
/**************************************************************************/
#define SERVER_PORT     3005
#define BUFFER_LENGTH    250
#define FALSE              0

void main()
{
   /***********************************************************************/
   /* Variable and structure definitions.                                 */
   /***********************************************************************/
   int sd=-1, sdconn=-1;
   int rc, on=1, rcdsize=BUFFER_LENGTH;
   char buffer[BUFFER_LENGTH];
   struct sockaddr_in6 serveraddr, clientaddr;
   int addrlen=sizeof(clientaddr);
   char str[INET6_ADDRSTRLEN];

   /***********************************************************************/
   /* A do/while(FALSE) loop is used to make error cleanup easier.  The   */
   /* close() of each of the socket descriptors is only done once at the  */
   /* very end of the program.                                            */
   /***********************************************************************/
   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)) < 0)
      {
         perror("socket() failed");
         break;
      }

      /********************************************************************/
      /* The setsockopt() function is 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)) < 0)
      {
         perror("setsockopt(SO_REUSEADDR) failed");
         break;
      }

      /********************************************************************/
      /* 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 that specifies port  */
      /* 3005. (that is, the bind is done to both the IPv4 and IPv6 TCP/IP    */
      /* stacks).  This behavior can be modified using the IPPROTO_IPV6   */
      /* level socket option IPV6_V6ONLY if required.                      */
      /********************************************************************/
      memset(&serveraddr, 0, sizeof(serveraddr));
      serveraddr.sin6_family = AF_INET6;
      serveraddr.sin6_port   = htons(SERVER_PORT);
      /********************************************************************/
      /* Note: applications use in6addr_any similarly to the way they use */
      /* INADDR_ANY in IPv4.  A symbolic constant IN6ADDR_ANY_INIT also   */
      /* exists but can only be used to initialize an in6_addr structure  */
      /* at declaration time (not during an assignment).                  */
      /********************************************************************/
      serveraddr.sin6_addr   = in6addr_any;
      /********************************************************************/
      /* Note: 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)) < 0)
      {
         perror("bind() failed");
         break;
      }

      /********************************************************************/
      /* The listen() function allows the server to accept incoming       */
      /* client connections.  In this example, the backlog is set to 10.  */
      /* This means that the system will queue 10 incoming connection     */
      /* requests before the system starts rejecting the incoming         */
      /* requests.                                                        */
      /********************************************************************/
      if (listen(sd, 10) < 0)
      {
         perror("listen() failed");
         break;
      }

      printf("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)) < 0)
      {
         perror("accept() failed");
         break;
      }
      else
      {
         /*****************************************************************/
         /* Display the client address.  Note that if the client is       */
         /* an IPv4 client, the address will be shown as an IPv4 Mapped   */
         /* IPv6 address.                                                 */
         /*****************************************************************/
         getpeername(sdconn, (struct sockaddr *)&clientaddr, &addrlen);
         if(inet_ntop(AF_INET6, &clientaddr.sin6_addr, str, sizeof(str))) {
            printf("Client address is %s\n", str);
            printf("Client port is %d\n", ntohs(clientaddr.sin6_port));
         }
      }

      /********************************************************************/
      /* In this example we know that the client will send 250 bytes of   */
      /* data over.  Knowing this, we can use the SO_RCVLOWAT socket      */
      /* option and specify that we don't want our recv() to wake up      */
      /* until all 250 bytes of data have arrived.                        */
      /********************************************************************/
      if (setsockopt(sdconn, SOL_SOCKET, SO_RCVLOWAT,
                     (char *)&rcdsize,sizeof(rcdsize)) < 0)
      {
         perror("setsockopt(SO_RCVLOWAT) failed");
         break;
      }

      /********************************************************************/
      /* Receive that 250 bytes of data from the client                   */
      /********************************************************************/
      rc = recv(sdconn, buffer, sizeof(buffer), 0);
      if (rc < 0)
      {
         perror("recv() failed");
         break;
      }

      printf("%d bytes of data were received\n", rc);
      if (rc == 0 ||
          rc < sizeof(buffer))
      {
         printf("The client closed the connection before all of the\n");
         printf("data was sent\n");
         break;
      }

      /********************************************************************/
      /* Echo the data back to the client                                 */
      /********************************************************************/
      rc = send(sdconn, buffer, sizeof(buffer), 0);
      if (rc < 0)
      {
         perror("send() failed");
         break;
      }

      /********************************************************************/
      /* Program complete                                                 */
      /********************************************************************/

   } while (FALSE);

   /***********************************************************************/
   /* Close down any open socket descriptors                              */
   /***********************************************************************/
   if (sd != -1)
      close(sd);
   if (sdconn != -1)
      close(sdconn);
}