Escolar Documentos
Profissional Documentos
Cultura Documentos
UDP is connectionless, unreliable, datagram protocol, quite unlike the connectionoriented, reliable byte stream provided by TCP.
UDP server
socket()
bind()
UDP client
socket()
recvfrom()
sendto()
data (re
q
uest)
Process request
recvfrom()
close()
data (reply)
sendto()
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buff, size_t nbytes, int flags,
struct sockaddr *from, socklen_t *addrlen);
ssize_t sendto(int sockfd, const void *buff, size_t nbytes, int flags,
const struct sockaddr *to, socklen_t *addrlen);
Both return number of bytes read or written if OK, -1 on error.
sockfd is the socket descriptor of the host machine.
For now flags is set to 0 .
to argument in sendto( ) is the socket address structure of where the data is to
be sent.
from argument in recvfrom( ) will get the socket address of from whom we have
received the datagram.
The final argument addrlen is a value-result argument.
Writing a datagram of 0 length is OK. In UDP this results in a datagram with IP
header, 8-byte UDP header and no data.
This also means that if the function recvfrom() returns the value 0, it is OK for
UDP, it does not mean that the peer has closed the connection.
With UDP, though it is connectionless, there is no such thing as closing the UDP
connection.
fgets
fputs
UDP
Client
sendto
recvfrom
recvfrom
sendto
UDP
Server
dg_echo( ) function
void dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen)
{
int n;
socklen_t len;
char mesg[MAXLINE];
for( ; ; ) {
len = clilen;
n = recvfrom (sockfd, mesg, MAXLINE, 0, pcliaddr, &len);
sendto (sockfd, mesg, n, 0, pcliaddr, len);
}
}
client
connection
Server
child
fork
Listening
server
fork
Server
child
connection
client
TCP
TCP
connection
connection
TCP
There are 2 TCP connected sockets and each of the two connected sockets on the
server host has its own socket receive buffer.
client
server
client
Socket receive
buffer
UDP
UDP
UDP
datagram
datagram
There is only one server process and it has a single socket on which it receives
all arriving datagrams and sends all responses.
That socket has a receive buffer into which all arriving datagrams are placed.
dg_cli( ) function
void dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
{
int n;
char sendline[MAXLINE], recvline[MAXLINE + 1];
while (fgets (sendline, MAXLINE, fp) != NULL)
{
sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
recvline[n] = 0; //null terminate
fputs (recvline, stdout);
}
}
With UDP socket, the first time the process calls sendto(), if the socket has not
yet had a port bound to it, an ephemeral port is chosen by the kernel for the
socket.
Last two arguments in recvfrom() being NULL imply that we are not interested in
knowing the address of from whom we are receiving the datagram.
Lost Datagrams
If a client datagram is lost, the client will block forever in its call to recvfrom( ) in
the function dg_cli( ), waiting for a server reply that will never arrive.
Similarly, if the client datagram arrives at the server but the servers reply is lost,
the client will again block forever in its call to recvfrom( ).
The only way to prevent this is to place a timeout on the clients call to recvfrom( )
But just placing a timeout is not entire solution, coz by placing a timeout we can
not tell whether our datagram never made it to the server or the servers reply
never made it back.
If the clients request was something like transfer a certain amount of money
from account A to account B. The above two possibilities does make a lot of
difference.
Though in UDP, anyone can send any datagram to the echo client at any moment
of time, these might get mixed with the echo server reply that the client is waiting
for.
So the client can verify that the datagram it has received is from the same IP
address to whom it had sent the echo request (echo server).
The problem with the previous version of dg_cli is that if the host is multihomed,
then the program can fail.
The solution will be to check for responding hosts domain name instead of IP
address.
TCP server
UDP server
Source IP address
accept()
recvfrom( )
accept()
recvfrom( )
Destination IP address
getsockname( )
recvmsg( )
getsockname( )
getsockname( )
There are 4 pieces of information that a server might want to know fro an IP
datagram that arrives : Source IP address , Source port number, Destination IP
address, Destination port number.
TCP server always has easy access to all four pieces of information. And these
values remain constants for the lifetime of a connection.
Since UDP is connectionless, the destination IP address can change for each
datagram that is sent to the server. A UDP server can also receive datagrams
destined for one of the hosts broadcast or multicast addresses.
A UDP client or UDP server can call connect( ) only if that process uses the UDP
socket to communicate with exactly one peer.
Calling connect( ) multiple times for a UDP socket
A process with a connected UDP socket can call connect( ) again for that socket,
to either:
Specify a new IP address and port number
Unconnect the socket
We can not call connect( ) for a TCP socket.
To unconnect a UDP socket we call connect but set the family member of the
address structure as AF_UNSPEC. This might return an error of
EAFNOSUPPORT, but that is OK.
To incorporate flow control in UDP, we will change our dg_cli to write a fixed
number of bytes to the server.
And on the server side , we will make sure that dg_echo counts and prints the
number of datagrams received.
dg_echo() that counts and prints number of datagrams received from d client.
static void recvfrom_int(int);
static int count;
void dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen)
{
socklen_t len;
char mesg[MAXLINE];
signal(SIGINT, recvfrom_int);
for( ; ; ){
len=clilen;
recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);
count++;
}
}
static void recvfrom_int(int signo)
{
printf(received %d datagrams\n, count);
}
Though in UDP a program never terminates, well have to explicitly terminate the
server with terminal interrupt, which will generate the SIGINT signal.
We want to catch the SIGINT signal and call the signal handler function
recvfrom_int( ), this is done by signal( ) function.
Another aspect in flow control is the size of the UDP socket receive buffer.
The number of UDP datagrams that are queued up by UDP for a given socket is
limited by the size of the socket receive buffer. We can change this using
SO_RCVBUF socket option.
dg_echo() that increases the size of the socket receive queue
void dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen)
{
int n;
socklen_t len;
char mesg[MAXLINE];
signal(SIGINT, recvfrom_int);
n = 240 * 1024;
setsockopt (sockfd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));
for( ; ; ){
len=clilen;
recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);
count++;
}
}
static void recvfrom_int(int signo)
{
printf(received %d datagrams\n, count);
}
UDP Echo server that handles TCP an UDP both using select( )
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);
//create 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);
if(FD_ISSET(listenfd, &rset)) {
//tcp socket is ready
len = sizeof(cliaddr);
connfd = accept(listenfd, (SA*)&cliaddr, &len);
if((childpid = fork()) == 0) {
//child process
close(listenfd);
str_echo(connfd);
exit(0);
}
close(connfd);
}
if(FD_ISSET(udpfd, &rset)) {
//udp socket is ready
len = sizeof(cliaddr);
n = recvfrom(udpfd, mesg, MAXLINE, 0 (SA *)&cliaddr, &len);
sendto(udpfd, mesg, n, 0, (SA*)&cliaddr, len);
}
}
}