Example: Accept connections from both IPv6 and IPv4 clients

Use this sample program 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 function calls and what they do within the socket application that accepts requests 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() function allows application to reuse the local address when the server is restarted before the required wait time expires.
  3. A bind() function 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() function allows the server to accept incoming client connections. In this example, the programmer sets the backlog to 10, which allows the system to queue 10 connection requests before the system starts rejecting incoming requests.
  5. The server uses the accept() function 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() function 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() function receives that 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 specify that he or she does not want the recv() to wake up until all 250 bytes of data have arrived.
  8. The send() function echoes the data back to the client.
  9. The close() function 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() function returns a socket descriptor representing an endpoint. The statement also identifies the address family, socket type, and protocol using the information returned from getaddrinfo().
  4. The connect() function establishes a connection to the server regardless of whether the server is IPv4 or IPv6..
  5. The send() function sends the data request to the server.
  6. The recv() function receives data from the server application.
  7. The close() function closes any open socket descriptors.

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

Note: By using the code 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 representing   */
      /* 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);
}
Related reference
Examples: Connection-oriented designs
Example: IPv4 or IPv6 client
Related information
socket()
setsockopt()
bind()
listen()
accept()
getpeername()
recv()
send()
close()
inet_pton()
getaddrinfo()
connect()