The client-server socket programming is very essential to enable the communication between various processes on a network in networked applications. We may need to create connections, exchange data, and create interactive applications using sockets.
Let us discuss the fundamentals by implementing a simple client-server socket program in the C language. Let us also discuss about the essential steps such as socket creation, binding, listening, and accepting connections. We will learn more about socket programming with the C programming language by understanding this article.
Definition of a Socket
A socket is a key concept in computer networking that enables communication between processes that operate on various machines across a network. It functions as an endpoint for data transmission and reception between the client and server.
Let us consider a socket as a virtual communication channel that allows us with a connection and information exchange between two processes. Data packets can enter and exit through it, serving as a door to ensure a reliable and efficient communication.
The TCP (Transmission Control Protocol) sockets, referred to as stream sockets, offer a dependable and well-organized stream of data transmission. They ensure that the supplied information from one end reaches the other end undamaged and in the proper manner.
The UDP (User Datagram Protocol) sockets, also known as datagram sockets, allow the exchange of single packets, or datagrams, without the guarantee of stability. Datagram sockets are often used when real-time communication or low-latency transmission is required.
The sockets are identified by an IP address and a port number’s combination. The IP address describes the machine’s location in the network, while the port number identifies the specific application or process that runs on that machine. This combination enables the sockets to establish a connection and ensure that the data reaches our target.
Let us see the following diagram that explains the client-server architecture:
Programming Example 1: Server-Side Program
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define PORT 7070
#define BUFFER_SIZE 1024
int main()
{
int serverSocket, newSocket, valRead;
struct sockaddr_in serverAddress, clientAddress;
int opt = 1;
int addressLength = sizeof(serverAddress);
char buffer[BUFFER_SIZE] = {0};
char message[BUFFER_SIZE] = {0};
// Create server socket
if ((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) == 0)
{
perror ("Socket creation failed");
exit (EXIT_FAILURE);
}
printf ("Socket successfully created.\n");
// Set socket options
if (setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)))
{
perror("setsockopt failed");
exit(EXIT_FAILURE);
}
serverAddress.sin_family = AF_INET;
serverAddress.sin_addr.s_addr = INADDR_ANY;
serverAddress.sin_port = htons(PORT);
// Bind
if (bind(serverSocket, (struct sockaddr *)&serverAddress, sizeof(serverAddress)) < 0)
{
perror("Bind failed");
exit(EXIT_FAILURE);
}
printf("Socket successfully binded.\n");
// Listen for connections
if (listen(serverSocket, 3) < 0)
{
perror("Listen failed");
exit(EXIT_FAILURE);
}
printf("Server listening...\n");
// Accept incoming connection
if ((newSocket = accept(serverSocket, (struct sockaddr *)&clientAddress, (socklen_t *)&addressLength)) < 0)
{
perror("Accept failed");
exit(EXIT_FAILURE);
}
printf("Server accepted the client...\n");
// Start chat
while (1)
{
// Read message from client
valRead = read(newSocket, buffer, BUFFER_SIZE);
printf("Client: %s\n", buffer);
// Check for client exit command
if (strcmp(buffer, "exit") == 0)
break;
// Clear the buffer and message
memset (buffer, 0, BUFFER_SIZE);
memset (message, 0, BUFFER_SIZE);
// Get server message
printf("Server: ");
fgets(message, BUFFER_SIZE, stdin);
// Send server message to client
send(newSocket, message, strlen(message), 0);
// Check for server exit command
if (strcmp(message, "exit\n") == 0)
break;
}
// Close sockets
close (newSocket);
close (serverSocket);
return 0;
}
Programming Example 2: Client-Side Program
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define PORT 7070
#define BUFFER_SIZE 1024
int main()
{
int clientSocket, valRead;
struct sockaddr_in serverAddress;
char buffer[BUFFER_SIZE] = {0};
char message[BUFFER_SIZE] = {0};
// Create client socket
if ((clientSocket = socket(AF_INET, SOCK_STREAM, 0)) == 0)
{
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
printf("Socket successfully created.\n");
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(PORT);
if (inet_pton(AF_INET, "127.0.0.1", &(serverAddress.sin_addr)) <= 0)
{
perror("Invalid address/ Address not supported");
exit(EXIT_FAILURE);
}
// Connect to the server
if (connect(clientSocket, (struct sockaddr *)&serverAddress, sizeof(serverAddress)) < 0)
{
perror("Connection failed");
exit(EXIT_FAILURE);
}
printf("Connected to the server.\n");
// Start chat
while (1)
{
// Clear the buffer and message
memset(buffer, 0, BUFFER_SIZE);
memset(message, 0, BUFFER_SIZE);
// Get client message
printf("Client: ");
fgets (message, BUFFER_SIZE, stdin);
// Send client message to the server
send(clientSocket, message, strlen(message), 0);
// Check for client exit command
if (strcmp(message, "exit\n") == 0)
break;
// Read message from server
valRead = read (clientSocket, buffer, BUFFER_SIZE);
printf ("Server: %s\n", buffer);
// Check for server exit command
if (strcmp(buffer, "exit") == 0)
break;
}
// Close socket
close (clientSocket);
return 0;
}
Output:
$ gcc soc.c -o soc
$ ./soc
Client: good morning, sir!
Server: follow your task in your email
Client: okay, sure!
**Client program compilation**
$ gcc cli.c -o cli
$ ./cli
Socket successfully created.
Connected to the server.
Client: good morning, sir!
Server: follow your task in your email
Client: okay, sure!
Explanation:
In these programming examples, the codes implement a simple chat application on the port 7070 where the client connects to the server, writes messages, and receives replies up until an “exit” command is received. The server waits for and accepts the client connections, and reads and reacts to the client messages.
Conclusion
This article shows how a simple client-server socket program is implemented in the C language. These programming examples provide a starting point to create more complex networked applications using the sockets in the C programming language by establishing connections, transferring messages, and managing a client-server communication.