UDP Sockets based Client Server Programs



Introduction

This article talks explains how to write a simple UDP client/server system in C on Linux (or Unix) platform. Writing client server application using UDP sockets is considered very easy, yet we sometimes miss some essential steps which results in spending more time in intense debugging sessions. I went through one such experience. It is quite hard to remember all the socket API details quickly without having to wade through tons of man pages, so this page lists the standard code for UDP Client Server program. I hope it will be useful for others, too.

The module consists of three files i.e., two source files and one header file. The header file i.e., Include.h contains the standard headers, data types and user data structures used in the program. The source file client.c contains UDP Client and file server.c contains UDP server. I have chosen a fixed port for the server i.e., 2152 which is reserved for GTPU protocol (UMTS network). Further the client and server IP addresses needs to be changed for they refer to IP addresses of my workstations.


Warranty :

The code given in this article is tested working in Red Hat Linux 8.0 (kernel 2.4.22). It should work on other Linux /Unix systems too, though some of the header files may be located in other directories on other systems system. To execute, first run Server and then Client.


Source code and its description :

The organization of entire source code is this way. Header file Include.h contains system headers necessary for generic as well as network specific functions, standard data types, network specific parameters and message structure exchanged between client and server.

The Client sends a request message with sender layer as Client, message type as Request and a sequence number to the Server with predefined IP address and fixed port. It then waits for the response. As it is UDP based, so no prior connection is established.

The Server waits for the incoming request from client. On receipt, it sends a response message to Client with sender layer as Server, message type as Response and sequence number received from request message to the Client. As it is UDP based, so connection is not established prior to data transfer.


Include.h

1.	#include <stdio.h>
2.	#include <stdlib.h>
3.	#include <string.h>
4.	#include <assert.h>
5.	#include <errno.h>
6.	#include <sys/socket.h>
7.	#include <sys/types.h>
8.	#include <netinet/in.h>
9.	#include <arpa/inet.h>
10.	#define S8 		char
11.	#define U8 		unsigned char
12.	#define S16		short int
13.	#define U16		unsigned short int
14.	#define S32		int
15.	#define U32		unsigned int
16.	#define S64		long long int
17.	#define U64		unsigned long long int
18.	#define DATA_PORT	2152
19.	#define CLIENTIP	"196.1.105.18" 
20.	#define SERVERIP	"196.1.105.36" 
21.	typedef enum sender{CLIENT=1, SERVER=2} SenderLayer_e;
22.	typedef enum primitive{REQUEST=3, RESPONSE=4} Primitive_e;
23.	typedef struct pdu
24.	{
25.		SenderLayer_e SenderLayerid;
26.		Primitive_e Primitiveid;
27.		U32 number;
28.	}PDU_t;

client.c
1.	int main()
2.	{
3.	S32 k,j;
4.	S32 sockfd;
5.	PDU_t Pdu;
6.	struct sockaddr_in Source, Target;
7.	struct in_addr a;
8.	bzero ((U8*)&Source,sizeof(Source));
9.	Source.sin_family=AF_INET;
10.	Source.sin_addr.s_addr=htonl(INADDR_ANY);
11.	Source.sin_port=htons(0);
12.	if((sockfd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP))==-1)
13.	{
14.		printf ("\n Socket for data msgs cannot be created\n");
15.		exit(-1);
16.	}
17.	/* Now we need to bind it to the os underlying queues */
18.	if(bind(sockfd,(struct sockaddr*)&Source,sizeof(Source))==-1)
19.	{
20.		perror("\n Data socket cannot be binded to OS\n");
21.		exit(-1);
22.	}
23.	bzero((U8*)&Target,sizeof(Target));
24.	Target.sin_family=AF_INET;
25.	Target.sin_port=htons(DATA_PORT);
26.	s_addr=Target.sin_addr.s_addr=inet_addr(SERVERIP);

28.	Pdu.SenderLayerid=CLIENT;
29.	Pdu.Primitiveid=REQUEST;
30.	Pdu.number=1;

32.	printf("\n...start sendto....\n");
33.	j=sendto(sockfd,(void*)&Pdu,sizeof(PDU_t),0,(struct sockaddr*)&Target, sizeof(Target));
34.	printf("\n\n packet send to IP %s at port %d...",inet_ntoa(a),ntohs(Target.sin_port));
35.	printf("\n...end sendto....\n");

37.	printf("\n...start recvfrom....\n");	
38.	bzero ((U8*)&Source,sizeof(Source));
39.	k=sizeof(Source);
40.	j=recvfrom(sockfd,(void*)&Pdu,sizeof(PDU_t),0,(struct sockaddr*)&Source,&k);
41. 	s_addr=((Source.sin_addr.s_addr));
42.	printf ("Received from %s at port %d\n",inet_ntoa(a),ntohs((Source.sin_port)));
43.	printf("\n Sender Layer %d\n",((PDU_t)Pdu).SenderLayerid);
44.	printf("\n Primitive %d\n",((PDU_t)Pdu).Primitiveid);
45.	printf("\n Pdu number %d\n",((PDU_t)Pdu).number);
46.	printf("\n...end recvfrom()....\n");
47.	close(sockfd);
48.	return 0;
49.	}

Internals of Client


server.c
1.	int main()
2.	{
3.	S32 j=-1,sockfd;
4.	U32 i,k;
5.	PDU_t Pdu; 
6.	struct sockaddr_in Source, Target;
7.	struct in_addr a;
8.	bzero ((U8*)&Target,sizeof(Target));
9.	Target.sin_family=AF_INET;
10.	Target.sin_addr.s_addr=htonl(INADDR_ANY);
11.	Target.sin_port=htons(DATA_PORT);	
12.	bzero ((U8*)&Source,sizeof(Source));
13.	if((sockfd=socket(AF_INET, SOCK_DGRAM,IPPROTO_UDP))==-1)
14.	{
15.		printf ("\n Socket for data msgs cannot be created\n");
16.		exit(-1);	
17.	}
18.	/*  Now we need to bind it to the os underlying queues  */  
19.	if(bind(sockfd,(struct sockaddr*)&Target,sizeof(Target))==-1)
20.	{	
21.		printf("\n Data socket cannot be binded to OS\n");
22.		exit(-1);
23.	}
24.	k=sizeof(Source);
25.	printf("\n...start recvfrom....\n");
26.	recvfrom(sockfd,(void*)&Pdu,sizeof(PDU_t),0,(struct sockaddr*)&Source,&k);
27.	s_addr=(Source.sin_addr.s_addr);
28.	printf("\nReceived from %s at port %d\n",inet_ntoa(a),ntohs(Source.sin_port));
29.	printf("\nSender Layer %d\n",((PDU_t)Pdu).SenderLayerid);
30.	printf("\nPrimitive %d\n",((PDU_t)Pdu).Primitiveid);
31.	printf("\nPdu number %d\n",((PDU_t)Pdu).number);
32.	i=((PDU_t)Pdu).number;	
33.	printf("\n...end recvfrom....\n");
34.	Pdu.SenderLayerid=SERVER;
35.	Pdu.Primitiveid=RESPONSE;
36.	Pdu.number=i;

38.	printf("\n...start sendto....\n");
39.	k=sendto(sockfd,(void*)&Pdu,sizeof(PDU_t),0,(struct sockaddr*)&Source, sizeof(Source));
40.	printf("Packet send to %s at port %d\n",inet_ntoa(a), ntohs(Source.sin_port));
41.	printf("\n...end sendto....\n");
42.	close(sockfd);
43.	}

Internals of Server


Conclusion

This article shows a simple client server implementation for UDP sockets. While working with sockets, few things needs to be remembered. Firstly, it is a good practice to check return values of all system calls explicitly. This will minimize the time going in debugging of these programs. Secondly, tracing tools like tcpdump, ethereal, netcat help see the exact behaviour of network interfaces of the system. Further command netstat with option a can be useful to check which sockets are active. Single stepping via GDB is very useful to debug such code.

Concludingly, you should ensure that firewall rules of the sytem are minimal, else both the programs will hang while receiving new datagrams (check ipchains). Normally in Linux with strict firewalls, Client will get a reject response in form of an ICMP Port unreachable packet instead of response message from Server; although some systems may not send any reject response and simple discard the incoming packet. In such a situaton (I was myself caught in once such), tracing tools like ethereal and tcpdump comes handy.


I am Nikhil Bhargava. I am curently working in C-DOT, India for past one year as a Research Engineer. All comments, bug fixes, and suggestions welcome.

Hosted by www.Geocities.ws

1