Programming UDP sockets in C on Linux – Client and Server example

By | July 31, 2020

UDP sockets

This article describes how to write a simple echo server and client using udp sockets in C on Linux/Unix platform.

UDP sockets or Datagram sockets are different from the TCP sockets in a number of ways.

The most important difference is that UDP sockets are not connection oriented. More technically speaking, a UDP server does not accept connections and a udp client does not connect to server.

The server will bind and then directly receive data and the client shall directly send the data.

Simple UDP Server

So lets first make a very simple ECHO server with UDP socket. The flow of the code would be

socket() -> bind() -> recvfrom() -> sendto()

C code

/*
	Simple udp server
*/
#include<stdio.h>	//printf
#include<string.h> //memset
#include<stdlib.h> //exit(0);
#include<arpa/inet.h>
#include<sys/socket.h>

#define BUFLEN 512	//Max length of buffer
#define PORT 8888	//The port on which to listen for incoming data

void die(char *s)
{
	perror(s);
	exit(1);
}

int main(void)
{
	struct sockaddr_in si_me, si_other;
	
	int s, i, slen = sizeof(si_other) , recv_len;
	char buf[BUFLEN];
	
	//create a UDP socket
	if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
	{
		die("socket");
	}
	
	// zero out the structure
	memset((char *) &si_me, 0, sizeof(si_me));
	
	si_me.sin_family = AF_INET;
	si_me.sin_port = htons(PORT);
	si_me.sin_addr.s_addr = htonl(INADDR_ANY);
	
	//bind socket to port
	if( bind(s , (struct sockaddr*)&si_me, sizeof(si_me) ) == -1)
	{
		die("bind");
	}
	
	//keep listening for data
	while(1)
	{
		printf("Waiting for data...");
		fflush(stdout);
		
		//try to receive some data, this is a blocking call
		if ((recv_len = recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen)) == -1)
		{
			die("recvfrom()");
		}
		
		//print details of the client/peer and the data received
		printf("Received packet from %s:%d\n", inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port));
		printf("Data: %s\n" , buf);
		
		//now reply the client with the same data
		if (sendto(s, buf, recv_len, 0, (struct sockaddr*) &si_other, slen) == -1)
		{
			die("sendto()");
		}
	}

	close(s);
	return 0;
}

Run the above code by doing a gcc server.c && ./a.out at the terminal. Then it will show waiting for data like this

$ gcc server.c && ./a.out 
Waiting for data...

Next step would be to connect to this server using a client. We shall be making a client program a little later but first for testing this code we can use netcat.

Test the server with netcat

Open another terminal and connect to this udp server using netcat and then send some data. The same data will be send back by the server. Over here we are using the ncat command from the nmap package.

$ ncat -vv localhost 8888 -u
Ncat: Version 5.21 ( http://nmap.org/ncat )
Ncat: Connected to 127.0.0.1:8888.
hello
hello
world
world

Note : We had to use netcat because the ordinary telnet command does not support udp protocol. The -u option of netcat specifies udp protocol.

Check open port with netstat

The netstat command can be used to check if the udp port is open or not.

$ netstat -u -a
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
udp        0      0 localhost:11211         *:*                                
udp        0      0 localhost:domain        *:*                                
udp        0      0 localhost:45286         localhost:8888          ESTABLISHED
udp        0      0 *:33320                 *:*                                
udp        0      0 *:ipp                   *:*                                
udp        0      0 *:8888                  *:*                                
udp        0      0 *:17500                 *:*                                
udp        0      0 *:mdns                  *:*                                
udp        0      0 localhost:54747         localhost:54747         ESTABLISHED
udp6       0      0 [::]:60439              [::]:*                             
udp6       0      0 [::]:mdns               [::]:*

Note the *:8888 entry of output. Thats our server program.
The entry that has localhost:8888 in "Foreign Address" column, indicates some client connected to it, which is netcat over here.

UDP Client

Now that we have tested our server with netcat, its time to make a client and use it instead of netcat.
The program flow is like

socket() -> sendto()/recvfrom()

Here is a quick example

/*
	Simple udp client
*/
#include<stdio.h>	//printf
#include<string.h> //memset
#include<stdlib.h> //exit(0);
#include<arpa/inet.h>
#include<sys/socket.h>

#define SERVER "127.0.0.1"
#define BUFLEN 512	//Max length of buffer
#define PORT 8888	//The port on which to send data

void die(char *s)
{
	perror(s);
	exit(1);
}

int main(void)
{
	struct sockaddr_in si_other;
	int s, i, slen=sizeof(si_other);
	char buf[BUFLEN];
	char message[BUFLEN];

	if ( (s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
	{
		die("socket");
	}

	memset((char *) &si_other, 0, sizeof(si_other));
	si_other.sin_family = AF_INET;
	si_other.sin_port = htons(PORT);
	
	if (inet_aton(SERVER , &si_other.sin_addr) == 0) 
	{
		fprintf(stderr, "inet_aton() failed\n");
		exit(1);
	}

	while(1)
	{
		printf("Enter message : ");
		gets(message);
		
		//send the message
		if (sendto(s, message, strlen(message) , 0 , (struct sockaddr *) &si_other, slen)==-1)
		{
			die("sendto()");
		}
		
		//receive a reply and print it
		//clear the buffer by filling null, it might have previously received data
		memset(buf,'
/*
	Simple udp client
*/
#include<stdio.h>	//printf
#include<string.h> //memset
#include<stdlib.h> //exit(0);
#include<arpa/inet.h>
#include<sys/socket.h>

#define SERVER "127.0.0.1"
#define BUFLEN 512	//Max length of buffer
#define PORT 8888	//The port on which to send data

void die(char *s)
{
	perror(s);
	exit(1);
}

int main(void)
{
	struct sockaddr_in si_other;
	int s, i, slen=sizeof(si_other);
	char buf[BUFLEN];
	char message[BUFLEN];

	if ( (s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
	{
		die("socket");
	}

	memset((char *) &si_other, 0, sizeof(si_other));
	si_other.sin_family = AF_INET;
	si_other.sin_port = htons(PORT);
	
	if (inet_aton(SERVER , &si_other.sin_addr) == 0) 
	{
		fprintf(stderr, "inet_aton() failed\n");
		exit(1);
	}

	while(1)
	{
		printf("Enter message : ");
		gets(message);
		
		//send the message
		if (sendto(s, message, strlen(message) , 0 , (struct sockaddr *) &si_other, slen)==-1)
		{
			die("sendto()");
		}
		
		//receive a reply and print it
		//clear the buffer by filling null, it might have previously received data
		memset(buf,'\0', BUFLEN);
		//try to receive some data, this is a blocking call
		if (recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen) == -1)
		{
			die("recvfrom()");
		}
		
		puts(buf);
	}

	close(s);
	return 0;
}
', BUFLEN); //try to receive some data, this is a blocking call if (recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen) == -1) { die("recvfrom()"); } puts(buf); } close(s); return 0; }

Run the above program and it will ask for some message

$ gcc client.c -o client && ./client
Enter message : happy
happy

Whatever message the client sends to server, the same comes back as it is and is echoed.

Conclusion

UDP sockets are used by protocols like DNS etc. The main idea behind using UDP is to transfer small amounts of data and where reliability is not a very important issue. UDP is also used in broadcasting/multicasting.

When a file transfer is being done or large amount of data is being transferred in parts the transfer has to be much more reliable for the task to complete. Then the TCP sockets are used.

About Silver Moon

A Tech Enthusiast, Blogger, Linux Fan and a Software Developer. Writes about Computer hardware, Linux and Open Source software and coding in Python, Php and Javascript. He can be reached at [email protected].

16 Comments

Programming UDP sockets in C on Linux – Client and Server example
  1. Nathan

    Hey Silver Moon,

    I’m a not a programmer but are very interested in electronics and making things automated. This was very helpful example of udp socket communications. What I did find though was the program doesn’t do anything else whilst it waits for data. How would you suggest to say send this server “Blink Led 1” and continue to listen for commands to turn on, blink or turn off leds.
    I can for example blink the led no problem in one project, I can get you code also running on a pi and responding to commands I send it now but I would like to be able to continue doing things in the background.

    Thanks for you time.

    1. Silver Moon Post author

      i haven’t done sockets for a long time. right now i can think of using multiple threads do things in parallel.
      so the main thread could do its background work, and an extra thread could listen to the udp port for incoming messages.
      or the other way round.
      but i am not sure if that is the best approach. there might be better alternatives.

    2. Big man

      Silver Moon’s approach will work but the best way would be to listen for socket connections asynchronously using epoll() and using TCP not UDP, UDP is unreliable so some of your commands might not make it to the server as intended. That’s what is done in most modern socket servers. Try googling how to use epoll() (Linux system call so should work on Raspberry Pi). It will allow you to have an efficient and scalable socket server. (I am actually almost done developing an IoT socket communication system myself using raspberry pi as main server and epoll() with TCP is the best approach for this kind of stuff as far as I know.

    1. Bryan Kelly

      And ncat used option -vv which on my Ubuntu system means verbose. The captured text does not have the verbose output. My system had five lines of information for each line of typed in data.
      Still, I am new to Linux and Ubuntu and this is an unexpected cool way to test the server app.
      Thank you.

  2. jenny

    Hi, I am new to socket programminga and linux , can you tell me … can we turn a system into a server ? and do communication using above programming? can we establish communication on the microcontroller using above programs?

  3. Mefew

    Excelent example, thanks very much!
    I’ve found that it needs only a tiny addition.
    To clean the buffer on the server also. So just adding on Server:

    //keep listening for data
    while(1)
    {
    printf(“Waiting for data…”);
    fflush(stdout);
    memset(buf,’\0′, BUFLEN); //Add this line

  4. dot

    if that sent character, how about send some file, example a picture, how to change in the script character sent to picture sent..?

  5. nishant

    What if the data sent from client side having some different Server address, SERVER 192.168.16.30 (this IP is pingable)

  6. neal

    if ((recv_len = recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen)) == -1)

    here &slen should be (socklen_t*)&slen . and work perfectly. Thank u

Leave a Reply

Your email address will not be published. Required fields are marked *