function

Team-FLY

20.7 Implementation of UICI UDP

UICI UDP functions use the same name resolution functions, addr2name and name2addr , as the UICI TCP functions. Program C.4 shows implementations of these functions. Compile your source with uiciname.c when using UICI UDP.

20.7.1 Implementation of u_openudp

The UICI UDP function u_openudp takes a port number as its parameter and creates a connectionless socket for communication using UDP. The u_openudp function returns a file descriptor if the communication endpoint was successfully created. Servers call u_openudp with their well-known port as a parameter. Clients generally call u_openudp with a parameter of 0, meaning that they will allow the system to choose an ephemeral port when it becomes necessary. The u_openudp function returns “1 and sets errno if an error occurs.

Program 20.9 implements u_openudp . The u_openudp function uses the socket function discussed on page 631 to create the communication endpoint. As in the case of TCP, the domain is AF_INET and the protocol is 0. The type is SOCK_DGRAM rather than SOCK_STREAM .

If the port number parameter is greater than 0, u_openudp associates the newly created socket with this port number by calling bind , a library function described on page 631.

Program 20.9 u_openudp.c

An implementation of u_openudp .

 #include <errno.h> #include <unistd.h> #include <sys/socket.h> #include "restart.h" #include "uiciudp.h" int u_openudp(u_port_t port) {    int error;    int one = 1;    struct sockaddr_in server;    int sock;    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)       return -1;    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) == -1) {       error = errno;       r_close(sock);       errno = error;       return -1;    }    if (port > 0) {       server.sin_family = AF_INET;       server.sin_addr.s_addr = htonl(INADDR_ANY);       server.sin_port = htons((short)port);       if (bind(sock, (struct sockaddr *)&server, sizeof(server)) == -1) {          error = errno;          r_close(sock);          errno = error;          return -1;       }    }    return sock; } 

Comparing u_openudp with u_open on page 634, we see that bind is called only when the port number is greater than 0. Only a server needs to bind the socket to a particular port. Also, it is not necessary to worry about SIGPIPE . A write to a pipe (or a TCP socket) generates a SIGPIPE signal when there are no active readers. In contrast, UDP provides no information about active receivers. A UDP datagram is considered to be sent correctly when it is successfully copied into the buffers of the network subsystem. UDP does not detect an error when an application sends a datagram to a destination that is not waiting to receive it, so sending does not generate a SIGPIPE .

20.7.2 The sendto function

The POSIX sendto function transmits data as a single datagram and returns the number of transmitted bytes if successful. However, sendto checks only local errors, and success does not mean that the receiver actually got the data.

The first three parameters for sendto have the same meaning as for read and write . The socket parameter holds a file descriptor previously opened by a call to socket . The message parameter has the data to be sent, and length is the number of bytes to send. The flags parameter allows special options that we do not use, so this value is always zero. The dest_addr parameter points to a structure filled with information about the destination, including the remote host address and the remote port number. Since we are using the Internet domain, *dest_addr is a struct sockaddr_in structure. The dest_len is the size of the struct sockaddr_in structure.

  SYNOPSIS  #include <sys/socket.h>    ssize_t sendto(int socket, const void *message, size_t length,                   int flags, const struct sockaddr *dest_addr,                   socklen_t dest_len);  POSIX  

If successful, sendto returns the number of bytes sent. If unsuccessful , sendto returns “1 and sets errno . The following table lists the mandatory errors for sendto with unconnected sockets.

errno

cause

EAFNOSUPPORT

address family cannot be used with this socket

EAGAIN or EWOULDBLOCK

O_NONBLOCK is set and operation would block

EBADF

socket parameter is not a valid file descriptor

EINTR

sendto interrupted before any data was transmitted

EMSGSIZE

message too large to be sent all at once as required by socket

ENOTSOCK

socket does not refer to a socket

EOPNOTSUPP

specified flags not supported for this type of socket

The sendto function can be used with sockets connected to a particular destination host and port. However, sendto still determines the destination host and port number by the information in the *dest_addr structure, independently of this connection.

If sendto is used on a socket that is not yet bound to a source port, the network subsystem assigns an unused ephemeral port to bind with the socket. Datagrams originating from this socket include the port number and the source host address along with the data so that the remote host can reply.

20.7.3 Implementation of u_sendto and u_sendtohost

The UICI UDP library provides two functions for sending messages, u_sendto and u_sendtohost , shown in Program 20.10. The u_sendtohost takes the destination host name and port number as parameters. It is meant to be used when initiating a communication with a remote host. The u_sendto function uses a u_buf_t structure that was filled by a previous call to u_recvfrom . The u_buf_t structure is meant to be used in a reply.

Program 20.10 u_sendto.c

An implementation of u_sendto and u_sendtohost .

 #include <errno.h> #include <sys/socket.h> #include "uiciname.h" #include "uiciudp.h" ssize_t u_sendto(int fd, void *buf, size_t nbytes, u_buf_t *ubufp) {    int len;    struct sockaddr *remotep;    int retval;    len = sizeof(struct sockaddr_in);    remotep = (struct sockaddr *)ubufp;    while (((retval = sendto(fd, buf, nbytes, 0, remotep, len)) == -1) &&            (errno == EINTR)) ;    return retval; } ssize_t u_sendtohost(int fd, void *buf, size_t nbytes, char *hostn,                      u_port_t port) {    struct sockaddr_in remote;    if (name2addr(hostn, &(remote.sin_addr.s_addr)) == -1) {       errno = EINVAL;       return -1;    }    remote.sin_port = htons((short)port);    remote.sin_family = AF_INET;    return u_sendto(fd, buf, nbytes, &remote); } 

The u_sendto function is almost identical to sendto except that u_sendto restarts if interrupted by a signal. The u_buf_t data type is defined in uiciudp.h by a typedef that sets it to be equivalent to struct sockaddr_in . This allows a u_buf_t pointer to be cast to a struct sockaddr pointer in the implementation of u_sendto . The user does not need to know anything about the internal representation of the u_buf_t structure, provided that its value was set by u_recvfrom or u_recvfromtimed .

The u_sendtohost function uses name2addr from uiciname.c to convert the host name to an address. If the host name begins with a digit, name2addr assumes that it is an IP address in dotted form and calls inet_addr to decode it. Otherwise , name2addr resolves the host name and fills struct sockaddr_in with the remote host address. The u_sendtohost function fills in the port number and address family and calls u_sendto . Since name2addr does not set errno when an error occurs, the u_sendtohost sets errno to EINVAL when name2addr returns an error.

20.7.4 The recvfrom function

The POSIX recvfrom function blocks until a datagram becomes available on file descriptor representing an open socket. While it is possible to use recvfrom with TCP sockets, we consider only UDP SOCK_DGRAM sockets. Be sure to associate socket with a port, either by explicitly calling bind or by calling sendto , which forces a binding to an ephemeral port. A call to recvfrom on a socket that has not been bound to a port may hang indefinitely.

The buffer parameter of recvfrom points to a user-provided buffer of length bytes that receives the datagram data. The amount of data received is limited by the length parameter. If the datagram is larger than length , recvfrom truncates the message to size length and drops the rest of the datagram. In either case, recvfrom returns the number of bytes of data placed in buffer .

The *address structure is a user-provided struct sockaddr structure that recvfrom fills in with the address of the sender. If address is NULL , recvfrom does not return sender information. The address_len parameter is a pointer to a value-result parameter. Set *address_len to the length of address before calling recvfrom . On return, recvfrom sets *address_len to the actual length of *address . The address_len parameter prevents buffer overflows because recvfrom truncates the sender information to fit in *address . It is not considered an error if the information put in *address is truncated, so be sure to make the buffer is large enough. For our purposes, the buffer should be able to hold a struct sockaddr_in structure.

  SYNOPSIS  #include <sys/socket.h>     ssize_t recvfrom(int socket, void *restrict buffer, size_t length,                      int flags, struct sockaddr *restrict address,                      socklen_t *restrict address_len);  POSIX  

If successful, recvfrom returns the number of bytes that were received. If unsuccessful, recvfrom returns “1 and sets errno . The following table lists the mandatory errors for recvfrom with an unconnected socket.

errno

cause

EAGAIN or EWOULDBLOCK

O_NONBLOCK is set and no data is waiting to be received, or MSG_OOB is set and no out-of- band data is available and either O_NONBLOCK is set or socket does not support blocking with out-of-band data

EBADF

socket is not a valid file descriptor

EINTR

recvfrom interrupted by a signal before any data was available

EINVAL

MSG_OOB is set and no out-of-band data is available

ENOTSOCK

socket does not refer to a socket

EOPNOTSUPP

specified flags not supported for this type of socket

20.7.5 Implementation of u_recvfrom and u_recvfromtimed

Program 20.11 implements u_recvfrom . It is similar to recvfrom except that it restarts recvfrom if interrupted by a signal. The returned sender information is encapsulated in the u_buf_t parameter, which is used as an opaque object for a reply, using u_sendto , to the sender. If successful, u_recvfrom returns the number of bytes received. If unsuccessful, u_recvfrom returns “1 and sets errno . Since UDP datagrams of length 0 are valid, a return value of 0 indicates a datagram of length 0 and should not be interpreted as end-of-file.

Program 20.11 u_recvfrom.c

An implementation of u_recvfrom .

 #include <errno.h> #include <sys/socket.h> #include "uiciudp.h" ssize_t u_recvfrom(int fd, void *buf, size_t nbytes, u_buf_t *ubufp) {    int len;    struct sockaddr *remote;    int retval;    len = sizeof (struct sockaddr_in);    remote = (struct sockaddr *)ubufp;    while (((retval = recvfrom(fd, buf, nbytes, 0, remote, &len)) == -1) &&            (errno == EINTR)) ;    return retval; } 

Since UDP is not reliable, a datagram can be lost without generating an error for either the sender or the receiver. More reliable protocols based on UDP use some form of request-reply or request-reply- acknowledge protocol discussed in Sections 20.4 through 20.6. These protocols require that the receiver not block indefinitely waiting for messages or replies. The u_recvfromtimed function returns after a specified time if it does not receive a datagram. If successful, u_recvfromtimed returns the number of bytes written in *buf . If a timeout occurs, u_recvfromtimed returns “1 and sets errno to ETIME . For other errors, u_recvfromtimed returns “1 and sets errno to the same values as u_recvfrom does.

Strategies for implementing timeouts include socket options for timeout, signals or select . Unfortunately, the socket options supporting timeouts are not universally available. The signal strategy uses a timer to generate a signal after a specified time. When a signal is caught, recvfrom returns with the error EINTR . The use of signals may interfere with other timers that a program might be using.

Program 20.12 implements u_recvfromtimed with the waitfdtimed function from the restart library. The implementation of waitfdtimed using select is shown in Program 4.15 on page 114. The waitfdtimed function takes two parameters: a file descriptor and an ending time. The add2currenttime function from the restart library converts the timeout interval into an ending time. Using the ending time rather than directly using the time interval allows waitfdtimed to restart if interrupted by a signal and still retain the same ending time for the timeout.

Program 20.12 u_recvfromtimed.c

An implementation of u_recvfromtimed .

 #include <errno.h> #include <sys/socket.h> #include <sys/time.h> #include "restart.h" #include "uiciudp.h" ssize_t u_recvfromtimed(int fd, void *buf, size_t nbytes, u_buf_t *ubufp,                          double seconds) {    int len;    struct sockaddr *remote;    int retval;    struct timeval timedone;    timedone = add2currenttime(seconds);    if (waitfdtimed(fd, timedone) == -1)       return (ssize_t)(-1);    len = sizeof (struct sockaddr_in);    remote = (struct sockaddr *)ubufp;    while (((retval = recvfrom(fd, buf, nbytes, 0, remote, &len)) == -1) &&            (errno == EINTR)) ;    return retval; } 
Exercise 20.16

Suppose you call u_recvfromtimed with a timeout of 2 seconds and 10 signals come in 1 second apart. When does u_recvfromtimed time out if no data arrives?

Answer:

It still times out 2 seconds after it is called. The reason is that waitfdtimed times out at a given ending time, independently of the number of times it needs to restart.

20.7.6 Host names and u_buf_t

The UICI UDP library also provides three functions for examining receiver host information. The u_gethostname function, which can be called after u_recvfrom or u_recvfromtimed , creates a string that corresponds to the name of a host. The first parameter of u_gethostname is a u_buf_t structure previously set, for example, by u_recvfrom . The u_gethostname function returns a null- terminated string containing the name of the host in the user-supplied buffer hostn . The third parameter of u_gethostname is the length of hostn . The u_gethostname function truncates the host name so that it fits.

The implementation of u_gethostname in Program 20.13 just calls addr2name and sets its *hostn buffer to the result. Recall that if addr2name cannot convert the address to a host name, it sets *hostn to the dotted-decimal representation of the host address. The addr2name function never returns an error.

Program 20.13 u_gethostname.c

An implementation of u_gethostname .

 #include "uiciname.h" #include "uiciudp.h" void u_gethostname(u_buf_t *ubufp, char *hostn, int hostnsize) {    struct sockaddr_in *remotep;    remotep = (struct sockaddr_in *)ubufp;    addr2name(remotep->sin_addr, hostn, hostnsize); } 

The u_gethostinfo function is similar to u_gethostname but is meant primarily for debugging. The u_gethostinfo function fills in a printable string with both the host name and port number corresponding to a u_buf_t structure. Program 20.14 implements u_gethostinfo .

Program 20.14 u_gethostinfo.c

An implementation of u_gethostinfo .

 #include <stdio.h> #include "uiciudp.h" #define BUFSIZE 1024 void u_gethostinfo(u_buf_t *ubufp, char *info, int infosize) {    int len;    int portnumber;    portnumber = ntohs(ubufp->sin_port);    len = snprintf(info, infosize, "port number is %d on host ", portnumber);    info[infosize-1] = 0;                         /* in case name did not fit */    if (len >= infosize) return;    u_gethostname(ubufp, info+len, infosize-len); } 

The function u_comparehost returns 1 if the given host name and port number match the information given in a u_buf_t structure, *ubufp , and 0 otherwise. The u_comparehost function first checks that the port numbers agree and returns 0 if they do not. Otherwise, u_comparehost calls name2addr to convert the host name to an address and compares the result to the address stored in ubufp . Program 20.15 implements u_comparehost .

Program 20.15 u_comparehost.c

An implementation of u_comparehost .

 #include <string.h> #include <sys/socket.h> #include "uiciname.h" #include "uiciudp.h" int u_comparehost(u_buf_t *ubufp, char *hostn, u_port_t port) {    in_addr_t addr;    struct sockaddr_in *remotep;    remotep = (struct sockaddr_in *)ubufp;    if ((port != ntohs(remotep->sin_port))         (name2addr(hostn, &addr) == -1)         (memcmp(&(remotep->sin_addr.s_addr), &addr, sizeof(in_addr_t)) != 0))       return 0;    return 1; } 
Team-FLY


Unix Systems Programming
UNIX Systems Programming: Communication, Concurrency and Threads
ISBN: 0130424110
EAN: 2147483647
Year: 2003
Pages: 274

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net