How to implement UDP sockets in C

Share

UDP is a connection-less protocol that, unlike TCP, does not require any handshaking prior to sending or receiving data, which simplifies its implementation.

svg viewer

Server-side

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
int main(void){
int socket_desc;
struct sockaddr_in server_addr, client_addr;
char server_message[2000], client_message[2000];
int client_struct_length = sizeof(client_addr);
// Clean buffers:
memset(server_message, '\0', sizeof(server_message));
memset(client_message, '\0', sizeof(client_message));
// Create UDP socket:
socket_desc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(socket_desc < 0){
printf("Error while creating socket\n");
return -1;
}
printf("Socket created successfully\n");
// Set port and IP:
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(2000);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
// Bind to the set port and IP:
if(bind(socket_desc, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0){
printf("Couldn't bind to the port\n");
return -1;
}
printf("Done with binding\n");
printf("Listening for incoming messages...\n\n");
// Receive client's message:
if (recvfrom(socket_desc, client_message, sizeof(client_message), 0,
(struct sockaddr*)&client_addr, &client_struct_length) < 0){
printf("Couldn't receive\n");
return -1;
}
printf("Received message from IP: %s and port: %i\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
printf("Msg from client: %s\n", client_message);
// Respond to client:
strcpy(server_message, client_message);
if (sendto(socket_desc, server_message, strlen(server_message), 0,
(struct sockaddr*)&client_addr, client_struct_length) < 0){
printf("Can't send\n");
return -1;
}
// Close the socket:
close(socket_desc);
return 0;
}

Explanation

Include the header file <sys/socket.h> and arpa/inet.h:

#include <sys/socket.h>
#include <arpa/inet.h>

Create a socket that returns a socket descriptor. This is used to refer to the socket later on in the code:

int socket_desc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

The server-side code keeps the address information of both the server and the client in the variable server_addr (a struct of type sockaddr_in). Initialize the server address by the port and IP:

struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(2000);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");

Bind socket descriptor to the server address:

bind(socket_desc, (struct sockaddr*)&server_addr, sizeof(server_addr);

Unlike TCP, the server-side does not wait for a client to connect and, therefore, does not receive the client’s address prior to sending and receiving data. Instead, the server receives information about the client when it receives data using the recvfrom() method:

int client_struct_length = sizeof(client_addr);

recvfrom(socket_desc, client_message, sizeof(client_message), 0,
           (struct sockaddr*)&client_addr, &client_struct_length);

The client’s information, stored in the variable client_addr of type sockaddr_in, is then used to send data to the same client:

sendto(socket_desc, server_message, strlen(server_message), 0,
         (struct sockaddr*)&client_addr, client_struct_length);

Close the socket to end the communication:

close(socket_desc);

Client-side

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
int main(void){
int socket_desc;
struct sockaddr_in server_addr;
char server_message[2000], client_message[2000];
int server_struct_length = sizeof(server_addr);
// Clean buffers:
memset(server_message, '\0', sizeof(server_message));
memset(client_message, '\0', sizeof(client_message));
// Create socket:
socket_desc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(socket_desc < 0){
printf("Error while creating socket\n");
return -1;
}
printf("Socket created successfully\n");
// Set port and IP:
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(2000);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
// Get input from the user:
printf("Enter message: ");
gets(client_message);
// Send the message to server:
if(sendto(socket_desc, client_message, strlen(client_message), 0,
(struct sockaddr*)&server_addr, server_struct_length) < 0){
printf("Unable to send message\n");
return -1;
}
// Receive the server's response:
if(recvfrom(socket_desc, server_message, sizeof(server_message), 0,
(struct sockaddr*)&server_addr, &server_struct_length) < 0){
printf("Error while receiving server's msg\n");
return -1;
}
printf("Server's response: %s\n", server_message);
// Close the socket:
close(socket_desc);
return 0;
}

Explanation

Include header files, create a socket, and initialize the server’s address information in a variable of type sockaddr_in (similarly to how it was done at the server-side):

#include <sys/socket.h>
#include <arpa/inet.h>

int socket_desc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(2000);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");

Unlike TCP, when the client sends and receives data using sendto() and recvfrom(), the server’s information has to be given every time:

sendto(socket_desc, client_message, strlen(client_message), 0,
         (struct sockaddr*)&server_addr, server_struct_length);

recvfrom(socket_desc, server_message, sizeof(server_message), 0,
         (struct sockaddr*)&server_addr, &server_struct_length);

A deadlock will occur if both the client and the server are waiting for each other’s message at recvfrom().

Control-flow of a client-server program
Control-flow of a client-server program

Client-server app

The following client-server application enables a client to connect to a server and send only one message. The server replies with the same message converted to uppercase letters​ and the communication terminates.

Instructions

  1. Click the Run button in the widget below and execute the command for the Server. If the socket is created successfully, the message Listening for incoming messages… will be displayed.
  2. Press the + button to open another terminal tab and execute the Client’s command.
  3. Enter a message in the Client tab that is sent to the Server.
  4. The Server’s response will be shown in the Client’s tab.
Server Client
usercode/server usercode/client
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int main(void){
    int socket_desc;
    struct sockaddr_in server_addr, client_addr;
    char server_message[100], client_message[100];
    int client_struct_length = sizeof(client_addr);
    
    // Clean buffers:
    memset(server_message, '\0', sizeof(server_message));
    memset(client_message, '\0', sizeof(client_message));
    
    // Create UDP socket:
    socket_desc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    
    if(socket_desc < 0){
        printf("Error while creating socket\n");
        return -1;
    }
    printf("Socket created successfully\n");
    
    // Set port and IP:
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(2000);
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
    // Bind to the set port and IP:
    if(bind(socket_desc, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0){
        printf("Couldn't bind to the port\n");
        return -1;
    }
    printf("Done with binding\n");
    
    printf("Listening for incoming messages...\n\n");
    
    // Receive client's message:
    if (recvfrom(socket_desc, client_message, sizeof(client_message), 0,
         (struct sockaddr*)&client_addr, &client_struct_length) < 0){
        printf("Couldn't receive\n");
        return -1;
    }
    printf("Received message from IP: %s and port: %i\n",
           inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
    
    printf("Msg from client: %s\n", client_message);
    
    // Change to uppercase:
    for(int i = 0; client_message[i]; ++i)
        client_message[i] = toupper(client_message[i]);
    
    // Respond to client:
    strcpy(server_message, client_message);
    
    if (sendto(socket_desc, server_message, strlen(server_message), 0,
         (struct sockaddr*)&client_addr, client_struct_length) < 0){
        printf("Can't send\n");
        return -1;
    }
    
    // Close the socket:
    close(socket_desc);

}
Copyright ©2024 Educative, Inc. All rights reserved