Home | | Network Programming and Management | TCP and UDP Echo Server using Select ()

Chapter: Network Programming and Management : Socket Options, Elementary UDP SOC Sockets

TCP and UDP Echo Server using Select ()

Following example combines the concurrent TCP echo server with iterative UDP echo server into a single server using select function to multiplex the TCP and UDP socket.

TCP and UDP Echo Server using Select ():

 

Following example combines the concurrent TCP echo server with iterative UDP echo server into a single server using select function to multiplex the TCP and UDP socket.


/* include udpservselect01 */  

#include      "unp.h"      

int                        

main(int argc, char **argv)     

{        int               listenfd, connfd, udpfd, nready, maxfdp1;

          char            mesg[MAXLINE];

          pid_t           childpid;

          fd_set                   rset;

          ssize_t                  n;

          socklen_t    len;

          const int      on = 1;

          struct sockaddr_in         cliaddr, servaddr;

          void            sig_chld(int);

          /* 4create listening TCP socket */

          listenfd = Socket(AF_INET, SOCK_STREAM, 0);

          bzero(&servaddr, sizeof(servaddr));

          servaddr.sin_family       = AF_INET;

          servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

          servaddr.sin_port = htons(SERV_PORT);

          Setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

          Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));

          Listen(listenfd, LISTENQ);

                   /* 4create UDP socket */

          udpfd = Socket(AF_INET, SOCK_DGRAM, 0);

          bzero(&servaddr, sizeof(servaddr));

          servaddr.sin_family       = AF_INET;

          servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

          servaddr.sin_port = htons(SERV_PORT);

          Bind(udpfd, (SA *) &servaddr, sizeof(servaddr));

/* end udpservselect01 */

/* include udpservselect02 */

 

Signal(SIGCHLD, sig_chld); /* must call waitpid() */ FD_ZERO(&rset);

 

maxfdp1 = max(listenfd, udpfd) + 1; for ( ; ; ) {

 

FD_SET(listenfd, &rset); FD_SET(udpfd, &rset);

 

if ( (nready = select(maxfdp1, &rset, NULL, NULL, NULL)) < 0) { if (errno == EINTR)

          continue;     /* back to for() */

          Else            

          err_sys("select error");  

          }                

          if (FD_ISSET(listenfd, &rset)) {                

          len = sizeof(cliaddr);               

          connfd = Accept(listenfd, (SA *) &cliaddr, &len);

          if ( (childpid = Fork()) == 0) { /* child process */

          Close(listenfd);     /* close listening socket */

          str_echo(connfd); /* process the request */

          exit(0);                 

          }                

          Close(connfd);      /* parent closes connected socket */

          }                

          if (FD_ISSET(udpfd, &rset)) {                  

len = sizeof(cliaddr);

 

n = Recvfrom(udpfd, mesg, MAXLINE, 0, (SA *) &cliaddr, &len); Sendto(udpfd, mesg, n, 0, (SA *) &cliaddr, len);

 

} }}

/* end udpservselect02 */

Create listening TCP socket

 

A listening TCP socket is created that is bound to the server‘s well known port. We set the SO_REUSEADDR socket option in case of connections exist on this port.

 

Create a UDP socket

 

A UDP socket is also created and bound to the same port. Even though the same port is used for the TCP and UDP sockets, there is no need to set the SO_REUSEADDR socket option before this call to bind because TCP ports are independent of UDP ports.

Establish a signal handler for SIGCHLD:

 

Establised the signal handler SIGCHLD because TCP connections will be handled by a child process.

 

Prepare for Select:

 

A descriptor set is initialized for select and maximum of two descriptors for which the select

waits.

 

Call select:

 

We call select waiting only for readability on the listening TCP socket or readability on the UDP socket. Since our sig_chld handler can interrupt our call to select, we handle an error of EINTR

 

Hane the new client:

 

We accept a new client connection when the listening TCP socket is readable, fork a child and call our str_echo in the child.

 

Handle arrival of datagram.

 

If the UDP socket is readable, a datagram has arrived. We read it with recvfrom and send it back to the client with sendto().

 

Summary:

 

Converting echo-client server to use UDP instead of TCP was simple. But the features provided by TCP are missing: detecting lost packet and retransmitting, verifying responses and so on.

 

UDP socket can generate asynchronous errors that is errors that are reported some time after the packet was sent. IN TCP, these error are always reported to application but not in UDP

 

 

UDP has no flow control. But this is not a big restriction as the UDP requirement are built for request – response application.

 

 

gethostname () :

 

I n the application, as it is easy to input human readable DNS name instead of IP address, there is a need to convert the host names into IP address format. This is achieved by the function gethostname (). When called, if successful, it returns a pointer to a hostent structure that contains all the IPv4 address or all IPv6 address for the host.

 

The syntax is given below:

#include <netdb.h>

struct hostent *gethostbyname (const char * hostname);

returns nonnull pointer if OK , NULL on error with h_errorno set.

 

The non null pointer returned by this function points to the following hostent structure: Struct hostent {

char *h_name; /* official canonical) name of host*/

char **h_aliases ; /* pointer to array of pointers to alias names*/

int     h_addrtype ;         /* host address type: AF_INET or AF_INET6 */

int     h_length ;    /* length of address : 4 or 16*/

char ** h_addr_list        /* ptr to array of ptrs with IPv4 or IPv6 addrs*/

}                          

#define h_addr     h_addr_list[0]      /* first address in list*/

 

In terms of DNS, gethostbyname() performs a querry for an A record or for a AAAA record. This function can return either IPv4 or IPv6 address.

 

Arrangement of hostent structure is shown below:


Similar structure is  ther6 for IPv6 wherein h_addrtype is AF_INET6 and h_length is equal 6.

 

 

Returned h_name is called canonical name of the host. When error occurs, the functions sets the global integer h_errono to one of the following constants defined by including <netdb.h>.

 

         HOST_NOT_FOUND

 

         TRY_AGAIN

 

         NO_RECOVERY

 

•   NO_DATA         (same as N0_ADDRESS)

NO_DATA error is valid and it indicates that the hostent has only MX record

 

Example:

 

#include      "unp.h"

int main(int argc, char **argv)

 

{

char                      *ptr, **pptr;

char                      str[INET6_ADDRSTRLEN]; /* handles longest IPv6 address */

struct hostent      *hptr;

 

while (--argc > 0) { ptr = *++argv;

 

if ( (hptr = gethostbyname(ptr)) == NULL) { err_msg("gethostbyname error for host: %s: %s",

ptr, hstrerror(h_errno));

 

continue;

 

}

printf("official hostname: %s\n", hptr->h_name);

 

for (pptr = hptr->h_aliases; *pptr != NULL; pptr++) printf("\talias: %s\n", *pptr);

 

switch (hptr->h_addrtype) { case AF_INET:

 

#ifdef  AF_INET6

 

case AF_INET6:

#endif

pptr = hptr->h_addr_list;

 

for ( ; *pptr != NULL; pptr++) printf("\taddress: %s\n",

 

Inet_ntop(hptr->h_addrtype, *pptr, str, sizeof(str)));

break;

default:

err_ret("unknown address type");

 

break;

}

}

 

 

exit(0);

 

}

 

IN this gethostname() is called for each command line argument. The official hostname output followed by the list of alias names. This program supports both IPv4 and IPv6 address type.

 

RES_USE_INET6 Resolver Option:

 

This option is used to tell the resolver that we want IPv6 addresses returned by gethostname() instead of IPv4 addresses.

 

        A application can set this option itself by first calling the resolver‘s re_init function and than enabling the option as shown below:

 

o #include <resolv.h> res_init();

res.options |=RESUSE_INET6;

 

This must be done before the first call to gethostbyname() or gethostbyaddr(). The effect of this option is only on the application that sets the option,.

 

        IF the environment variable RES_OPTION contains the string inet6, the option is enabled. It is set in the file .profile file with the export attribute as in

export RES_OPTIONS = inet6;

 

This setting affects every program that we run from out login shell. But if it set in the command line, then if affects only that command.

 

         The resolver configuration file – normally /etc/resolv.conf – can contain the line

 

options inet6

Setting this option ion the resolver configuration file affects all applications on the host that call the resolver functions. Hence this should not be used until all applications in the host are capable of handling IPv6 addresses returned in a hostent structure.

 

The first method sets the option on a per application basis, the second method on a per user basis and the third method on a per system basis.

gethostbyname2() function and                  IPv6 support:

gethostbyname2() allows us    to specify the address family.

#include      <netdb.h>            

struct hostent       *gethostbyname2(cost char *hostname, int family);

returns : non null pointer if OK,       NULL pointer on error with h_errono set

The return value is the same as with gethostbyname, a pointer to a hostent structure and this structure remains the same. The logic function depends on the family argument and on the RES_USE_INET6 option.

 

Following table summarizes the operation of the gethostbyname6 with regard to the new RES_USEINET6 option.

 

getbostbyname and gethostbyname2 with resolver RES_USE_INET6 options The logic works on

 

o whether the RES_USE_INET6 options is on or off

o whether the second argument to gethostbyname2() is AF_INET OR AF_INET6

o whether the resolver searches for  A records or for AAAA records and

. whether the returned addresses are of length 4 or 16.


          The operation of gethostbyname 2   is as follows:       

 

•        If the family argument is AF_INET, a query is made for the A records. IF unsuccessful, the function returns a null pointer. IF successful, the type and assize of the returned addresses depends on the new RES_USE_INET6 resolver option : if the option is not set, IPv4 address are returned and the h_length members of the hostent structure will be 4. if the option is set, IPv4 mapped IPv6 address are returned and the h_length member of the hostent structure will be 16.

 

•        If the family argument is AF_INET6, a query is made for AAAA records. IF successful, IPv6 addresses are returned and the h_length member of the hostent structure will be 16. otherwise the function returns a null pointer.

 

The source code that is given below describe the action of gethostbyname and RES_USE_INET^ options.

Strcut hostent * gethostbyname (const char *name) {

 

Struct hostent

 

}

Study Material, Lecturing Notes, Assignment, Reference, Wiki description explanation, brief detail
Network Programming and Management : Socket Options, Elementary UDP SOC Sockets : TCP and UDP Echo Server using Select () |


Privacy Policy, Terms and Conditions, DMCA Policy and Compliant

Copyright © 2018-2024 BrainKart.com; All Rights Reserved. Developed by Therithal info, Chennai.