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
Related Topics
Privacy Policy, Terms and Conditions, DMCA Policy and Compliant
Copyright © 2018-2024 BrainKart.com; All Rights Reserved. Developed by Therithal info, Chennai.