Here we provide a basic client server communication using both TPC and UDP.
User Datagram Protocol (UDP) is a connectionless protocol unlike connection oriented protocol Transmission Control Protocol (TCP).
//udp_server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUFFER_SIZE 1024
int main(int argc, char *argv[]) {
//1. process parameters
if (argc != 4) {
printf("Syntax: ./server_prog server_ip server_port message_to_send\n");
return -1;
}
char *ip = argv[1];
int port = atoi(argv[2]); //similar to Integer.parseInt(argv[2]) in Java
char *message = argv[3];
//2. create socket
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
perror("Socket creation failed");
return -1;
}
//2. Create sockaddr_in
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr)); //fill with all zeros (i.e., zeroed out)
//now fill with correct values
server_addr.sin_family = AF_INET;
//TCP-IP uses big-endian. Default in networking is big-endian.
//our x86 pc is little-endian. So, any value we receive as big-endian, must be converted to little endian
//Endianess comes to any multibyte data communication
server_addr.sin_port = htons(port); //host to network byte order (i.e., big endian)
// Convert ip address to network binary format and validate IP address
// return 0 means invalid IP, and -1 means error
if (inet_pton(AF_INET, ip, &server_addr.sin_addr) <= 0) {
perror("Invalid address");
close(sock);
return -1;
}
//4. bind socket
// we also pass length of server_addr to provide we read full lenght of socket
if (bind(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("Bind failed");
close(sock);
return -1;
}
printf("Server is listening on %s:%d\n", ip, port);
//5. create buffer to send/receive message
// any two buffer to store receive and responseds
char buffer[BUFFER_SIZE];
char response[BUFFER_SIZE];
struct sockaddr_in client_addr;
socklen_t client_addr_len = sizeof(client_addr);
//6. looper to receive and send
while (1) {
memset(buffer, 0, BUFFER_SIZE);
// in recvfrom function client_addr_len part is a pointer, pointer points to address, therefore, address of the value should be passed. Pass through by reference example
ssize_t received_len = recvfrom(sock, buffer, BUFFER_SIZE - 1, 0, (struct sockaddr *)&client_addr, &client_addr_len);
if (received_len < 0) {
perror("recvfrom failed");
break;
}
buffer[received_len] = '\0'; //null-termination
printf("Received message from client: %s\n", buffer);
// Ensure no overflow during message construction
snprintf(response, BUFFER_SIZE, "%.*s: %.*s", 512, message, 512, buffer);
ssize_t sent_len = sendto(sock, response, strlen(response), 0, (struct sockaddr *)&client_addr, client_addr_len);
if (sent_len < 0) {
perror("sendto failed");
break;
}
printf("Sent response to client: %s\n", response);
}
//7. all done, sock can be closed
close(sock);
return 0;
}
To compile: gcc -o udp_server udp_server.c
To start server: ./udp_server 127.0.0.1 8080 "hello"
To test: echo "hello server" | nc -u 127.0.0.1 8080
!root@amd:/home/upgautam/network_programming_linux# ./udp_server 127.0.0.1 8080 "hello"
Server is listening on 127.0.0.1:8080
Received message from client: hello server
Sent response to client: hello: hello server
# send from client
upgautam@amd:~/network_programming_linux$ echo "hello server" | nc -u 127.0.0.1 8080
hello: hello server
Now, we make udp_client.c program to ping to udp_server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUFFER_SIZE 1024
int main(int argc, char *argv[]) {
// Check for correct number of arguments
if (argc != 4) {
printf("Usage: %s <server_ip> <server_port> <message>\n", argv[0]);
return -1;
}
// Process command-line arguments
char *server_ip = argv[1];
int server_port = atoi(argv[2]);
char *message = argv[3];
// Create UDP socket
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
perror("Socket creation failed");
return -1;
}
// Configure server address
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(server_port);
// Convert server IP to binary format
if (inet_pton(AF_INET, server_ip, &server_addr.sin_addr) <= 0) {
perror("Invalid server IP address");
close(sock);
return -1;
}
// Send message to server
ssize_t sent_len = sendto(sock, message, strlen(message), 0,
(struct sockaddr *)&server_addr, sizeof(server_addr));
if (sent_len < 0) {
perror("Failed to send message");
close(sock);
return -1;
}
printf("Message sent to %s:%d\n", server_ip, server_port);
// Receive server response
char buffer[BUFFER_SIZE];
memset(buffer, 0, BUFFER_SIZE);
socklen_t server_addr_len = sizeof(server_addr);
ssize_t received_len = recvfrom(sock, buffer, BUFFER_SIZE - 1, 0,
(struct sockaddr *)&server_addr, &server_addr_len);
if (received_len < 0) {
perror("Failed to receive response");
close(sock);
return -1;
}
// Print server response
printf("Server response: %s\n", buffer);
// Close the socket
close(sock);
return 0;
}
TCP is connection oriented protocol.
//tcp_server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <signal.h>
#define BUFFER_SIZE 1024
#define BACKLOG 10 // Number of pending connections allowed in the queue
int server_sock; // Global variable to access and close the socket in signal handler
// Signal handler for SIGINT (Ctrl+C)
void handle_sigint(int sig) {
printf("\nCaught signal %d. Shutting down server...\n", sig);
close(server_sock); // Clean up resources
exit(0); // Exit the program
}
int main(int argc, char *argv[]) {
//1. process parameters
if (argc != 4) {
printf("Syntax: ./server_prog server_ip server_port message_to_send\n");
return -1;
}
char *ip = argv[1];
int port = atoi(argv[2]); // Convert port number from string to integer
char *message = argv[3];
// Set up signal handling for SIGINT
signal(SIGINT, handle_sigint);
//2. create socket
server_sock = socket(AF_INET, SOCK_STREAM, 0);
if (server_sock < 0) {
perror("Socket creation failed");
return -1;
}
//3. Create sockaddr_in
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr)); // Zero out the structure
// Set up server address structure
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
if (inet_pton(AF_INET, ip, &server_addr.sin_addr) <= 0) {
perror("Invalid IP address");
close(server_sock);
return -1;
}
//4. bind socket
if (bind(server_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("Bind failed");
close(server_sock);
return -1;
}
//4.1. We need to listen in TCP (this we don't do in UDP)
// Start listening for incoming connections
if (listen(server_sock, BACKLOG) < 0) {
perror("Listen failed");
close(server_sock);
return -1;
}
printf("Server is listening on %s:%d\n", ip, port);
//5. create buffer to send/receive message
struct sockaddr_in client_addr;
socklen_t client_addr_len = sizeof(client_addr);
char buffer[BUFFER_SIZE];
char response[BUFFER_SIZE];
//6. main looper
while (1) {
// 6.1. We also need to accept (this we don't do in UDP)
// Accept a new connection from a client
int client_sock = accept(server_sock, (struct sockaddr *)&client_addr, &client_addr_len);
if (client_sock < 0) {
perror("Accept failed");
continue; // Accept failed, try the next connection
}
printf("Connected to client\n");
// Clear the buffer for receiving data
memset(buffer, 0, BUFFER_SIZE);
// Receive data from the client
ssize_t received_len = recv(client_sock, buffer, BUFFER_SIZE - 1, 0);
if (received_len < 0) {
perror("Receive failed");
close(client_sock);
continue; // Continue with the next client
}
buffer[received_len] = '\0'; // Null-terminate the received string
printf("Received message from client: %s\n", buffer);
// Construct the response message
snprintf(response, BUFFER_SIZE, "%s: %s", message, buffer);
// Send the response back to the client
ssize_t sent_len = send(client_sock, response, strlen(response), 0);
if (sent_len < 0) {
perror("Send failed");
} else {
printf("Sent response to client: %s\n", response);
}
// Close the connection with the current client
close(client_sock);
}
// This will never be reached unless the loop exits, but is retained for clarity
close(server_sock);
return 0;
}
To compile: gcc -o tdp_server tdp_server.c
To start server: ./tdp_server 127.0.0.1 8080 "hello"
To test: echo "hello server" | nc -u 127.0.0.1 8080
!root@amd:/home/upgautam/network_programming_linux# ./tcp_server 127.0.0.1 8080 "hello"
Server is listening on 127.0.0.1:8080
Received message from client: hello server
Sent response to client: hello: hello server
# send from client
upgautam@amd:~/network_programming_linux$ echo "hello server" | nc -u 127.0.0.1 8080
hello: hello server
Now, we make a tcp_client.c program to ping to tcp_server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUFFER_SIZE 1024
int main(int argc, char *argv[]) {
if (argc != 4) {
printf("Syntax: ./tcp_client server_ip server_port message_to_send\n");
return -1;
}
char *ip = argv[1];
int port = atoi(argv[2]); // Convert port number from string to integer
char *message = argv[3];
// Create a TCP socket
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("Socket creation failed");
return -1;
}
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr)); // Zero out the structure
// Set up server address structure
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
// Convert IP address to binary form and validate
if (inet_pton(AF_INET, ip, &server_addr.sin_addr) <= 0) {
perror("Invalid IP address");
close(sock);
return -1;
}
// Connect to the server
if (connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("Connection to the server failed");
close(sock);
return -1;
}
printf("Connected to the server at %s:%d\n", ip, port);
// Send the message to the server
if (send(sock, message, strlen(message), 0) < 0) {
perror("Message sending failed");
close(sock);
return -1;
}
printf("Message sent: %s\n", message);
// Receive the response from the server
char buffer[BUFFER_SIZE];
memset(buffer, 0, BUFFER_SIZE);
ssize_t received_len = recv(sock, buffer, BUFFER_SIZE - 1, 0);
if (received_len < 0) {
perror("Response receiving failed");
} else {
buffer[received_len] = '\0'; // Null-terminate the response
printf("Response from server: %s\n", buffer);
}
// Close the socket
close(sock);
printf("Disconnected from the server\n");
return 0;
}
We have listen and accept functionality in TCP that are used to create connection; that is why connection-oriented.