Its time for use to understand the sockets and do some socket programming.
What are Sockets❓
At its core, a socket is an abstraction provided by the operating system that allows applications to send or receive data over a network. Think of it as an endpoint in an communication link.
It acts as a connecting point for sending and receiving messages.
A socket is made up of the following components:
- IP Address:
- Identifies the device on the network. It can be an
IPv4
orIPv6
address.
- Identifies the device on the network. It can be an
- Port Number:
- Identifies the specific process or service on the device that the communication is intended for. For example,
HTTP
typically uses port80
, andHTTPS
uses port443
.
- Identifies the specific process or service on the device that the communication is intended for. For example,
- Protocol:
- Specifies the type of communication protocol being used. Common protocols are:
- TCP (Transmission Control Protocol): For reliable, connection-oriented communication.
- UDP (User Datagram Protocol): For faster, connectionless communication.
- Specifies the type of communication protocol being used. Common protocols are:
- Transport Layer Socket Descriptor:
- A unique identifier used by the operating system to manage the socket.
Representation:
In code, a socket is usually represented as a combination of:
<IP Address>:<Port Number>
For example:
192.168.1.1:8080
(IPV4)
[2001:db8::1]:443
(IPv6)
Types of Sockets:
- Stream Sockets (TCP):
- Provides reliable, connection-oriented communication.
- Ensures data integrity, sequencing, and error recovery.
- Example: Web browsing, email.
- Datagram Sockets (UDP):
- Provides connectionless, unreliable communication.
- Faster than TCP but does not guarantee delivery.
- Example: Video streaming, online gaming.
- Raw Sockets:
- Operate at a lower level, giving direct access to IP packets.
- Typically used for custom protocols or network diagnostics.
- Domain Sockets:
- Used for inter-process communication (IPC) on the same host.
- Faster than network sockets as they avoid network stack overhead.
Key Purposes of Sockets
- Facilitating Network Communication
- Sockets provide the mechanism for data transfer between devices connected to a network (e.g., the Internet or a local area network).
- They support a wide variety of use cases, including web browsing, file sharing, and video streaming.
- Endpoint Abstraction
- A socket acts as an endpoint for sending and receiving data.
- Each socket is uniquely identified by:
- IP address
- Port number
- Protocol (TCP/UDP)
- Enabling Client-Server Architecture
- Sockets form the backbone of the client-server model:
- Server: Listens on a specific port and accepts incoming connections.
- Client: Initiates the connection to the server.
- Examples:
- Web browsers connect to web servers (e.g., HTTP over TCP).
- Messaging apps use sockets for real-time communication.
- Sockets form the backbone of the client-server model:
- Cross-platform Communication
- Sockets allow devices running different operating systems to communicate over standard protocols like TCP/IP and UDP.
- Protocol Agnosticism
- Sockets support various protocols, enabling communication that:
- Is reliable (e.g., TCP for guaranteed delivery).
- Is fast but less reliable (e.g., UDP for real-time gaming or streaming).
- Sockets support various protocols, enabling communication that:
- Secure Communication
- While raw sockets are not inherently secure, they can be used in conjunction with encryption libraries like OpenSSL to enable secure communication (e.g., HTTPS).
Real Life Analogy
Imagine Two Friends Talkin on Tin Can Telephones
- A socket is like the end of the tin can that each friends hold and speaks into.
- For the two friends to talk:
- Each friend needs their own tin can (a socket).
- They must be connected by a string (like a network).
- One friend speaks, and the sound travels through the string (data is sent).
- The other friend listens and responds (data is received).
Socket Process
The socket process refers to how a socket facilitates communication between two endpoints (client and server) in a network. Here’s an overview of the socket process:
1️⃣ Socket Creation
This is the first step in both the client and server sides. A socket is created as an endpoint for communication.
System Call:
int socket(int domain, int type, int protocol);
- Domain: Specifies the communication domain.
AF_INET
: For IPv4 communication.AF_INET6
: For IPv6 communication.AF_UNIX
: For local communication (on the same machine).
- Type: Specifies the type of service.
SOCK_STREAM
: For TCP (connection-oriented, reliable).SOCK_DGRAM
: For UDP (connectionless, fast, unreliable).
- Protocol: Specifies the transport protocol.
- Usually set to
0
to use the default protocol for the type (e.g., TCP forSOCK_STREAM
).
- Usually set to
Example:
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
2️⃣ Server-Side Steps
[a] Bind
The server assigns its socket to a specific IP address and port number so that clients can locate it.
System Call:
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- Parameters:
sockfd
: The socket file descriptor.addr
: A pointer to astruct sockaddr
that specifies the address and port.addrlen
: The size of the address structure.
Example:
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY; // Bind to all available interfaces
server_addr.sin_port = htons(8080); // Convert port to network byte order
if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("Bind failed");
exit(EXIT_FAILURE);
}
[b] Listen
The server prepares the socket to accept incoming connections.
System Call:
int listen(int sockfd, int backlog);
- Parameters:
sockfd
: The socket file descriptor.backlog
: The number of pending connections the queue can hold.
Example:
if (listen(sockfd, 5) < 0) {
perror("Listen failed");
exit(EXIT_FAILURE);
}
[c] Accept
The server accepts an incoming connection, creating a new socket for communication with the client.
System Call:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
- Parameters:
sockfd
: The socket file descriptor.addr
: Pointer to astruct sockaddr
for storing the client's address.addrlen
: Pointer to the size of the address structure.
Example:
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int new_sockfd = accept(sockfd, (struct sockaddr *)&client_addr, &client_len);
if (new_sockfd < 0) {
perror("Accept failed");
exit(EXIT_FAILURE);
}
3️⃣ Client-Side Step
[a] Connect
The client connects to the server using its IP address and port number.
System Call:
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- Parameters:
sockfd
: The socket file descriptor.addr
: Pointer to astruct sockaddr
with the server's address and port.addrlen
: Size of the address structure.
Example:
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8080); // Server port
server_addr.sin_addr.s_addr = inet_addr("192.168.1.10"); // Server IP
if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("Connection failed");
exit(EXIT_FAILURE);
}
4️⃣ Data Transmission
Both client and server can now exchange data over the socket.
[a] Send Data
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
- Parameters:
sockfd
: The socket file descriptor.buf
: Pointer to the data buffer to be sent.len
: Length of the data.flags
: Transmission flags (usually set to0
).
Example:
char message[] = "Hello, Server!";
send(sockfd, message, strlen(message), 0);
[b] Receive Data
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
- Parameters:
sockfd
: The socket file descriptor.buf
: Pointer to the buffer for storing received data.len
: Size of the buffer.flags
: Transmission flags (usually set to0
).
Example:
char buffer[1024];
recv(new_sockfd, buffer, sizeof(buffer), 0);
5️⃣ Socket Closure
Both the client and server close their sockets when communication ends.
System Call:
int close(int sockfd);
Example:
int close(int sockfd);
Detailed Flow Diagram
Server Side:
- Create Socket → 2. Bind → 3. Listen → 4. Accept → 5. Communicate (Send/Receive) → 6. Close Socket.
Client Side:
- Create Socket → 2. Connect → 3. Communicate (Send/Receive) → 4. Close Socket.
This process enables reliable communication between a client and a server over a network.
Socket Programming
Socket programming is the foundation for enabling communication between devices in a network. It allows developers to write network applications where data can be exchanged between clients and servers using well-defined protocols such as TCP and UDP.
Steps for Socket Programming
1. Server-Side Steps
- Create a socket
Use thesocket()
function to create a server socket. - Bind the socket
Associate the socket with an IP address and port number using thebind()
function. - Listen for connections
Put the socket in a passive mode using thelisten()
function to accept client requests. - Accept a connection
Use theaccept()
function to accept an incoming client connection, creating a new socket for communication. - Communicate
Exchange data with the client using functions likesend()
andrecv()
. - Close the socket
Release resources after communication is complete using theclose()
function.
Client-Side Steps
- Create a socket
Use thesocket()
function to create a client socket. - Connect to the server
Use theconnect()
function to establish a connection with the server. - Communicate
Exchange data with the server usingsend()
andrecv()
. - Close the socket
Close the client socket using theclose()
function.
Example
Server Code:
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <arpa/inet.h>
#define PORT 8080
#define BUFFER_SIZE 1024
int main() {
int server_fd, client_fd;
struct sockaddr_in server_addr, client_addr;
char buffer[BUFFER_SIZE];
socklen_t client_len = sizeof(client_addr);
// Create a socket
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == -1) {
std::cerr << "Socket creation failed.\n";
exit(EXIT_FAILURE);
}
// Bind the socket to an IP address and port
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
std::cerr << "Binding failed.\n";
close(server_fd);
exit(EXIT_FAILURE);
}
// Listen for incoming connections
if (listen(server_fd, 5) == -1) {
std::cerr << "Listening failed.\n";
close(server_fd);
exit(EXIT_FAILURE);
}
std::cout << "Server listening on port " << PORT << "...\n";
// Accept a client connection
client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len);
if (client_fd == -1) {
std::cerr << "Connection acceptance failed.\n";
close(server_fd);
exit(EXIT_FAILURE);
}
std::cout << "Client connected.\n";
// Communicate with the client
memset(buffer, 0, BUFFER_SIZE);
read(client_fd, buffer, BUFFER_SIZE - 1);
std::cout << "Client says: " << buffer << std::endl;
std::string response = "Hello from server!";
send(client_fd, response.c_str(), response.size(), 0);
// Close the connection
close(client_fd);
close(server_fd);
return 0;
}
Client Code:
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <arpa/inet.h>
#define PORT 8080
#define BUFFER_SIZE 1024
int main() {
int sock;
struct sockaddr_in server_addr;
char buffer[BUFFER_SIZE];
// Create a socket
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1) {
std::cerr << "Socket creation failed.\n";
exit(EXIT_FAILURE);
}
// Define server address
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
// Convert IP address to binary form
if (inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr) <= 0) {
std::cerr << "Invalid address/Address not supported.\n";
close(sock);
exit(EXIT_FAILURE);
}
// Connect to the server
if (connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
std::cerr << "Connection failed.\n";
close(sock);
exit(EXIT_FAILURE);
}
std::cout << "Connected to the server.\n";
// Communicate with the server
std::string message = "Hello from client!";
send(sock, message.c_str(), message.size(), 0);
memset(buffer, 0, BUFFER_SIZE);
read(sock, buffer, BUFFER_SIZE - 1);
std::cout << "Server says: " << buffer << std::endl;
// Close the connection
close(sock);
return 0;
}
Compiling and Running
Compiling the server and client:
g++ server.cpp -o server g++ client.cpp -o client
Run the server:
./server
In a new terminal, run the client:
./client
- You should see the exchange of messages between the server and the client.
Explanation
- Server:
- The server listens on port
8080
. - Accepts a connection from the client.
- Reads a message from the client and sends a response back.
- The server listens on port
- Client:
- The client connects to the server on
127.0.0.1:8080
. - Sends a message to the server and prints the server’s response.
- The client connects to the server on