Datagram (UDP) ServerClient

 < Day Day Up > 



Datagram (UDP) Server/Client

Now, let’s look at a datagram server and client implementation. Recall that datagram communication is not connection-oriented; therefore, each datagram that we send must include the intended recipient with the data.

Datagram Server

The datagram server is illustrated in Listing 16.3.

As discussed with the stream server, lines 1–5 make visible a number of function prototypes, types, and symbolic constants used by the datagram server. We also define our symbolic constants in lines 7–8, to represent the maximum size of the buffer to write (MAX_BUFFER) and the port to which we’ll bind for our server socket (DAYTIME_SERVER_ PORT).

Listing 16.3 C language Daytime datagram server.

start example
 1   #include <sys/socket.h>  2   #include <arpa/inet.h>  3   #include <stdio.h>  4   #include <time.h>  5   #include <unistd.h>  6  7   #define MAX_BUFFER            128  8   #define DAYTIME_SERVER_PORT   13  9 10   int main ( void ) 11   { 12     int serverFd, fromlen, msglen; 13     struct sockaddr_in servaddr, from; 14     char timebuffer[MAX_BUFFER+1]; 15     time_t currentTime; 16 17     serverFd = socket(AF_INET, SOCK_DGRAM, 0); 18 19     memset(&servaddr, 0, sizeof(servaddr)); 20     servaddr.sin_family = AF_INET; 21     servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 22     servaddr.sin_port = htons(DAYTIME_SERVER_PORT); 23 24     bind(serverFd, 25           (struct sockaddr *)&servaddr, sizeof(servaddr)); 26 27     while ( 1 ) { 28 29       fromlen = sizeof(from); 30 31       msglen = recvfrom(serverFd, &timebuffer, 32                          MAX_BUFFER, 0, &from, &fromlen); 33 34       if (msglen >= 0) { 35 36         currentTime = time(NULL); 37         snprintf(timebuffer, MAX_BUFFER, "%s\n", 38                   ctime(&currentTime)); 39 40         sendto(serverFd, timebuffer, strlen(timebuffer), 0, 41                 (struct sockaddr *)&from, fromlen); 42 43       } 44 45     } 46 47     return(0); 48   }
end example

Line 10 begins our main program for the datagram server. A number of local variables are declared, each of which is discussed as used in the server.

We create our socket at line 17. Note that although we use the domain of AF_INET to create an Internet socket, we specify SOCK_DGRAM as our type (parameter 2). This defines our socket as a datagram socket (compared to a stream socket, using SOCK_STREAM type).

As we’re building a server, we need to name our server so that clients can find it. To do this, we create our address structure in lines 19–22. After clearing out the structure in line 19, we specify the family (socket domain) as AF_INET in line 20. The sin_addr element of our address structure (specifically from the server perspective, line 21) permits a client to connect through any of the available interfaces (INADDR_ANY). This wildcard could have instead been the address of one interface on the host, which would restrict connections to be allowed only through that interface. Finally, at line 22, we specify the port on which this server can be accessed on this host, the Daytime server port (DAYTIME_SERVER_PORT).

To bind the address information to our previously created socket, we use the bind Sockets function at lines 24–25. To bind, we pass our server socket (serverFd), and the address information (servaddr), including the size of the address structure (computed using the sizeof standard function).

Notably absent from Listing 16.3 are the listen and accept calls. Datagram sockets aren’t connected; therefore, these calls are irrelevant in this paradigm.

Our infinite loop begins at line 27. We calculate the size of our address structure at line 29, in preparation for a call to recvfrom at line 31. The recvfrom call is used strictly with datagram sockets (and is blocking). This variant of the recv function, as can be seen from its name, receives not only a datagram, but also information about the source of the datagram. Recall that with stream sockets, this isn’t necessary because all data will arrive from the same source (to which we originally connected). To the recvfrom call, we pass our server socket (serverFd), a buffer to receive our datagram (timebuffer), along with the size of the buffer (MAX_BUFFER). The size is important because it defines the maximum size datagram that can be retrieved. The fourth argument (0) represents the flags; in this case, no flags are provided. Finally, we provide an address structure (from) along with a size argument. In this case, we pass fromlen by reference, so that the recvfrom call can tell us the size that was returned. Finally, recvfrom returns the size of the datagram returned, which is stored within the msglen variable.

At line 34, we check the length returned in msglen to ensure that a valid datagram was received. If so (a nonnegative value was returned), then the from address structure is valid and we generate a new time string in timebuffer (lines 36–38). The send function variant, sendto, is used to send a datagram to a specific recipient (lines 40–41). Note that we pass our server socket (serverFd), our character buffer (timebuffer), and the length of this buffer using the standard function strlen. We specify no flags, and then the address received in the initial recvfrom call (which represents who originally sent us an empty datagram) is used to return the time datagram. Therefore, based upon this code pattern, whoever sent us the empty datagram is to whom we send back the time string.

After the datagram is sent, we loop back around to line 29 and then await another datagram using the recvfrom call. Note that another difference from the stream server is that there is no close call for the client socket. Because datagram sockets operate on a message basis, there is no defined connection between any two endpoints. Datagram sockets simply provide the ability to send messages to other datagram sockets.

Datagram Client

The datagram client is shown in Listing 16.4, and corresponds with the datagram server previously shown in Listing 16.3.

Listing 16.4 C language Daytime datagram client.

start example
 1   #include <sys/socket.h>  2   #include <arpa/inet.h>  3   #include <stdio.h>  4   #include <unistd.h>  5   #include <time.h>  6  7   #define MAX_BUFFER            128  8   #define DAYTIME_SERVER_PORT   13  9 10   int main () 11   { 12     int connectionFd, in; 13     struct sockaddr_in servaddr; 14     char timebuffer[MAX_BUFFER+1]; 15 16     connectionFd = socket(AF_INET, SOCK_DGRAM, 0); 17 18     memset(&servaddr, 0, sizeof(servaddr)); 19     servaddr.sin_family = AF_INET; 20     servaddr.sin_port = htons(DAYTIME_SERVER_PORT); 21 22     servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 23 24     sendto( connectionFd, timebuffer, 1, 0, 25              (struct sockaddr_in *)&servaddr, 26              sizeof(servaddr) ); 27 28     in = recv(connectionFd, timebuffer, MAX_BUFFER, 0 ); 29     timebuffer[in] = 0; 30 31     printf("\n%s", timebuffer); 32 33     close(connectionFd); 34 35     return(0); 36   }
end example

We’ll focus our attention now on the differences between the server discussed previously and the client source shown in Listing 16.4. Note in line 16, that we create our socket in the same manner as the server. There is no name binding to this socket, as was required with the server. In lines 18–22, we create an address structure, but this represents the destination for our datagram. Note the symmetry in this address structure to the one defined for the server. The family and port (AF_INET and DAYTIME_SERVER_PORT) are identical to the server, but in this case, the sin_addr is given an actual address instead of INADDR_ANY (the wildcard). This is because we’re defining where to connect, in this case the loopback address (127.0.0.1).

Now that we have a destination set up for our initial datagram, we send a single byte to the server using the sendto call at lines 24–26. This allows the server to know who we are so that it can send us a response. We provide our client socket descriptor (connectionFd) as well as our temporary string buffer (timebuffer) and a length (1 byte). We provide no flags to sendto, but with the servaddr structure, we define the destination for the datagram.

Next, we await a response from the server in line 28 using the recv call. Recall again the symmetry with the server. After the server receives the initial datagram, it uses the source information of this datagram to send back a response to that client. The client uses the recv call to receive the server’s response. The client socket descriptor is provided along with a temporary string buffer and size. The fourth parameter of recv represents the flags argument that is not used here. The return value of recv is the number of bytes received in the response datagram (stored in variable in). We assume here that a valid datagram was received, and in line 29, we NULL-terminate the timebuffer using the return variable (in) as the index of the end of the string. The string stored in timebuffer is then emitted at line 31 and the client socket is closed at line 33.

Although the client closes its socket, the server has no knowledge of this (compared to stream sockets) because the communication with datagrams is not connected. The client then exits and the dialog between the client and server is complete.



 < Day Day Up > 



BSD Sockets Programming from a Multi-Language Perspective
Network Programming for Microsoft Windows , Second Edition (Microsoft Programming Series)
ISBN: 1584502681
EAN: 2147483647
Year: 2003
Pages: 225
Authors: Jim Ohlund

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