C++ NETWORK PROGRAMMING
1 — Introduction to Network Programming:
What is network programming?
Network programming is the practice of creating software applications that communicate with each other over a network. This communication can occur within a local network (LAN) or across the internet, enabling devices and systems to exchange data and share resources. Network programming plays a crucial role in enabling various modern technologies and services, from web applications to IoT devices.
Why is it important?
Understanding network programming is essential for developers who want to build scalable, distributed, and interconnected systems. It enables the creation of applications that can leverage the power of the internet and networked environments to provide real-time communication, data sharing, and collaboration. Whether developing web servers, chat applications, or enterprise systems, knowledge of network programming principles is indispensable.
Overview of different network protocols (TCP/IP, UDP, etc.)
Introduction
Network protocols are sets of rules and conventions that govern how data is transmitted and received over a network. They define the format of data packets, error-handling mechanisms, and other aspects of communication. Understanding different network protocols is crucial for developing networked applications that can effectively communicate and interact with other devices and systems.
TCP/IP Protocol Suite
The TCP/IP (Transmission Control Protocol/Internet Protocol) protocol suite is the foundation of the modern internet. It consists of a set of protocols that facilitate communication between devices connected to the internet. The key protocols in the TCP/IP suite include:
Other Network Protocols
In addition to TCP/IP, there are various other network protocols used for specific purposes or in specialized environments. Some notable examples include:
2 — Socket Programming Basics:[ just another file manipulation in Linux ]
Introduction to sockets in C++:
sockets provide a means for communication between processes running on different devices over a network. In C++, sockets are typically manipulated using system calls provided by the operating system’s networking API, such as the Berkeley sockets API on Unix-like systems or the Winsock API on Windows.
Creating and using sockets in C++ involves several steps, including socket creation, binding to a specific address and port, listening for incoming connections (for server sockets), establishing connections (for client sockets), and sending/receiving data.
One of the key advantages of using sockets in C++ is the flexibility they offer. Developers have fine-grained control over various aspects of network communication, such as the choice of transport protocol (e.g., TCP or UDP), socket options, and error handling. This flexibility allows for the implementation of a wide range of networked applications, from simple client-server applications to complex distributed systems.
C++ provides several libraries and frameworks for working with sockets, including:
Regardless of the specific library or framework used, understanding the fundamentals of socket programming in C++ is essential for developing robust and efficient networked applications. In the following sections, we will explore the basics of socket programming in C++, including socket creation, binding, connection establishment, and data transmission.
Creating sockets (socket(), bind(), listen(), etc.):
1. Socket Creation:
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
2. Binding:
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY; // Bind to any available interface[0.0.0.0]
addr.sin_port = htons(4000); // Bind to port 4000
if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
perror("bind");
exit(EXIT_FAILURE);
}
3. Listening (for Server Sockets):
if (listen(sockfd, 5) == -1) { // Allow up to 5 pending connections
perror("listen");
exit(EXIT_FAILURE);
}
4. Accepting Connections (for Server Sockets):
领英推荐
sockaddr_in client_addr;
socklen_t client_addrlen = sizeof(client_addr);
int client_sockfd = accept(sockfd, (struct sockaddr *)&client_addr, &client_addrlen);
if (client_sockfd == -1) {
perror("accept");
exit(EXIT_FAILURE);
}
5. Connecting (for Client Sockets):
sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // Server's IP address
server_addr.sin_port = htons(4000); // Server's port
if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("connect");
exit(EXIT_FAILURE);
}
3 — Simple tcp client-server :
1-Server :
#include <sys/socket.h>
#include <iostream>
#include <cstring>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
std::cout << "Starting the server ...." << std::endl;
int server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket == -1)
{
std::cerr << "Error creating the socket !" << std::endl;
return 1;
}
sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = INADDR_ANY;
serverAddr.sin_port = htons(4000);
if (bind(server_socket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == -1)
{
std::cerr << "Error binding the socket !" << std::endl;
close(server_socket);
return 1;
}
listen (server_socket, 1);
int client_socket = accept(server_socket, NULL, NULL);
char buff[1024];
while (true)
{
memset(buff, 0, sizeof(buff));
recv(client_socket, buff, sizeof(buff), 0);
std::cout << "Message from client: " << buff;
if (strcmp(buff, "exit\n") == 0)
break ;
}
close(server_socket);
return (0);
}
2-Client :
#include <iostream>
#include <cstring>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
std::cout << "Starting the Client ..." << std::endl;
int client_socket = socket(AF_INET,SOCK_STREAM, 0);
if (client_socket == -1)
{
std::cerr << "Error creating the socket ..." << std::endl;
}
sockaddr_in clientAddr;
clientAddr.sin_family = AF_INET;
clientAddr.sin_port = htons(4000);
clientAddr.sin_addr.s_addr = INADDR_ANY;
connect(client_socket, (struct sockaddr*)&clientAddr, sizeof(clientAddr));
char mess[1024];
while (true)
{
std::cout << "Enter the message to send : ";
fgets(mess, sizeof(mess), stdin);
send(client_socket, &mess, strlen(mess), 0);
if (strcmp(mess, "exit\n") == 0)
break ;
memset(mess, 0, sizeof(mess));
}
close(client_socket);
return (0);
}
3-Run it :
now to run the server-client application just compile it using these commands
g++ server.cpp -o server
./server
g++ client.cpp -o client
./client
4 — blocking vs non-blocking sockets:
Blocking and non-blocking sockets are two different approaches to handling I/O operations in network programming. Understanding the differences between them is crucial for designing efficient and responsive networked applications.
1 — Blocking Sockets:
In blocking sockets, I/O operations (such as reading from or writing to a socket) block the calling thread until the operation completes or an error occurs. This means that if there is no data available to read, a recv() call will block until data arrives, and if the send buffer is full, a send() call will block until space becomes available in the buffer.
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
int main() {
int server_socket = socket(AF_INET, SOCK_STREAM, 0);
// Socket creation, binding, and listening...
int client_socket = accept(server_socket, NULL, NULL);
char buffer[1024];
ssize_t bytes_received = recv(client_socket, buffer, sizeof(buffer), 0);
if (bytes_received == -1) {
perror("recv");
close(client_socket);
close(server_socket);
return 1;
}
std::cout << "Received message: " << buffer << std::endl;
close(client_socket);
close(server_socket);
return 0;
}
2 — Non-blocking Sockets:
In non-blocking sockets, I/O operations return immediately, even if they cannot be completed right away. This allows the calling thread to continue executing other tasks without waiting for the I/O operation to complete. Non-blocking sockets are often used with polling mechanisms like select() or poll() to determine when I/O operations can be performed without blocking.
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
int main() {
int server_socket = socket(AF_INET, SOCK_STREAM, 0);
// Socket creation, binding, and listening...
int client_socket = accept(server_socket, NULL, NULL);
// Set client socket to non-blocking
fcntl(client_socket, F_SETFL, O_NONBLOCK);
char buffer[1024];
ssize_t bytes_received = recv(client_socket, buffer, sizeof(buffer), 0);
if (bytes_received == -1) {
if (errno == EWOULDBLOCK) {
std::cerr << "No data available to read" << std::endl;
} else {
perror("recv");
}
} else {
std::cout << "Received message: " << buffer << std::endl;
}
close(client_socket);
close(server_socket);
return 0;
}
5 — Select and Poll:
1 — Select:
Overview: Select is a system call that monitors multiple file descriptors for I/O readiness. It allows the process to wait until one or more file descriptors become ready for reading, writing, or exceptional conditions.
Prototype: int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
How it Works:
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(server_socket, &read_fds);
int max_fd = server_socket + 1;
struct timeval timeout = {10, 0}; // 10 seconds timeout
int activity = select(max_fd, &read_fds, NULL, NULL, &timeout);
if (activity == -1) {
perror("select");
exit(EXIT_FAILURE);
}
if (FD_ISSET(server_socket, &read_fds)) {
// Server socket is ready for new connection
int client_socket = accept(server_socket, NULL, NULL);
// Handle new connection...
}
2 — Poll:
Overview: Poll is a system call that performs similar functionality to select() but with some differences, such as a more efficient design and lack of limitations on the maximum number of file descriptors.
Prototype: int poll(struct pollfd *fds, nfds_t nfds, int timeout);
How it Works:
struct pollfd fds[1];
fds[0].fd = server_socket;
fds[0].events = POLLIN;
int timeout = 10000; // 10 seconds timeout
int activity = poll(fds, 1, timeout);
if (activity == -1) {
perror("poll");
exit(EXIT_FAILURE);
}
if (fds[0].revents & POLLIN) {
// Server socket is ready for new connection
int client_socket = accept(server_socket, NULL, NULL);
// Handle new connection...
}