【C++】socket套接字编程
IP 地址的意义就是标识公网内唯一一台主机。
- 端口号是一个 2 字节,16 比特位的整数。
- 一台主机中,一个端口号只能被一个进程所占用。
传输层协议(TCP 和 UDP)的数据段中也有两个端口号, 分别叫做源端口号和目的端口号.,它们描述 “数据是那个进程发送的, 要发给另外那个进程”。
Socket 网络通信
socket 通信的本质就是跨网络的进程间通信
TCP(传输控制协议)
TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议,具有以下特点:
- 传输层协议:TCP工作在OSI模型的传输层,负责在网络中的两个主机之间提供可靠的数据传输服务。
- 有连接:在数据传输开始之前,TCP需要建立一个连接,通过三次握手过程来确保双方准备好进行数据传输。
- 可靠传输:TCP通过确认和重传机制确保数据正确无误地从源传输到目的地。
- 面向字节流:TCP将数据视为字节流,不保留数据边界,这意味着发送的数据被看作是一连串的字节,TCP协议负责将这些字节顺序地、可靠地传输到目的地。
UDP(用户数据报协议)
UDP是一种无连接的、不可靠的、基于数据报的传输层通信协议,具有以下特点:
- 传输层协议:UDP同样工作在OSI模型的传输层,但它提供的是一种简单的消息传递服务。
- 无连接:UDP在数据传输前不需要建立连接,它直接发送数据,不保证数据的到达。
- 不可靠传输:UDP不保证数据的可靠传输,它不进行错误检测和修正,如果数据在传输过程中丢失或出错,UDP不会重传。
- 面向数据报:UDP将数据视为一系列独立的数据报,每个数据报都包含完整的信息,包括源端口号、目的端口号和数据负载。UDP发送和接收的数据单位是数据报。
数据报和字节流
- 数据报:是一种独立的、包含完整信息的数据单元。在UDP中,每个数据报都是一个独立的网络消息,包含源和目的端口信息,以及数据负载。数据报的传输是无序的,且不保证到达。
- 字节流:是一种连续的、无结构的字节序列。在TCP中,数据被看作是一连串的字节,不保留任何数据边界。TCP负责按顺序、可靠地将这些字节从发送方传输到接收方,确保数据的完整性和顺序。
网络字节序(常考)
我们知道,内存中的数据权值排列相对于内存地址的大小有大端和小端之分:
是以低地址的高位字节(大端)低位字节(小端)决定的
- 大端存储:高权值数字存到内存的低地址位置上,低权值存到高地址上。
- 小端存储:高权值数字存到内存的高地址位置上,低权值存到高地址上
如果发送端和接收端主机的存储字节序不同,则会造成发送的数据和识别出来的数据不一致的问题,如下图所示:
TCP/IP 协议规定,网络数据流应采用大端字节序,即低地址高权值:
如果发送主机是小端机, 就需要先进行数据转换;否则忽略,直接发送即可。
如果接受主机是小端机,则拿到数据后需要进行转换;否则忽略,直接读取即可。
Socket 编程
UDP 服务器代码(udp_server.cpp)
#include <iostream>
#include <cstring>
#include <arpa/inet.h>
#define BUFSIZE 1024
class UdpServer {
public:
UdpServer(uint16_t port) : port_(port), sockfd_(-1) {
if ((sockfd_ = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
std::cerr << "Socket creation error" << std::endl;
exit(EXIT_FAILURE);
}
sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port_);
server_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sockfd_, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
std::cerr << "Bind error" << std::endl;
exit(EXIT_FAILURE);
}
}
void serve() {
char buffer[BUFSIZE];
sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
while (true) {
memset(buffer, 0, BUFSIZE);
int bytes_received = recvfrom(sockfd_, buffer, BUFSIZE, 0,
(struct sockaddr*)&client_addr, &client_len);
if (bytes_received < 0) {
std::cerr << "Recvfrom error" << std::endl;
continue;
}
std::cout << "Received message: " << buffer << std::endl;
std::string response = "Echo: " + std::string(buffer);
sendto(sockfd_, response.c_str(), response.size(), 0,
(struct sockaddr*)&client_addr, client_len);
}
}
~UdpServer() {
if (sockfd_ != -1) {
close(sockfd_);
}
}
private:
uint16_t port_;
int sockfd_;
};
int main(int argc, char* argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <port>" << std::endl;
return 1;
}
uint16_t port = static_cast<uint16_t>(std::atoi(argv[1]));
UdpServer server(port);
server.serve();
return 0;
}
UDP 客户端代码(udp_client.cpp)
#include <iostream>
#include <cstring>
#include <arpa/inet.h>
#define BUFSIZE 1024
class UdpClient {
public:
UdpClient(const std::string& server_ip, uint16_t server_port)
: server_ip_(server_ip), server_port_(server_port), sockfd_(-1) {
if ((sockfd_ = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
std::cerr << "Socket creation error" << std::endl;
exit(EXIT_FAILURE);
}
}
void send_receive() {
sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(server_port_);
server_addr.sin_addr.s_addr = inet_addr(server_ip_.c_str());
std::string message = "Hello from UDP client";
sendto(sockfd_, message.c_str(), message.size(), 0,
(struct sockaddr*)&server_addr, sizeof(server_addr));
char buffer[BUFSIZE];
socklen_t server_len = sizeof(server_addr);
int bytes_received = recvfrom(sockfd_, buffer, BUFSIZE, 0,
(struct sockaddr*)&server_addr, &server_len);
if (bytes_received < 0) {
std::cerr << "Recvfrom error" << std::endl;
return;
}
std::cout << "Received message: " << buffer << std::endl;
}
~UdpClient() {
if (sockfd_ != -1) {
close(sockfd_);
}
}
private:
std::string server_ip_;
uint16_t server_port_;
int sockfd_;
};
int main(int argc, char* argv[]) {
if (argc != 4) {
std::cerr << "Usage: " << argv[0] << " <server_ip> <server_port>" << std::endl;
return 1;
}
std::string server_ip = argv[1];
uint16_t server_port = static_cast<uint16_t>(std::atoi(argv[2]));
UdpClient client(server_ip, server_port);
client.send_receive();
return 0;
}
TCP 客户端服务器代码
TCP 服务器代码(tcp_server.cpp)
#include <iostream>
#include <cstring>
#include <arpa/inet.h>
#include <unistd.h>
#define BUFSIZE 1024
class TcpServer {
public:
TcpServer(uint16_t port) : port_(port), listenfd_(-1) {
if ((listenfd_ = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
std::cerr << "Socket creation error" << std::endl;
exit(EXIT_FAILURE);
}
sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port_);
server_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(listenfd_, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
std::cerr << "Bind error" << std::endl;
exit(EXIT_FAILURE);
}
if (listen(listenfd_, 5) < 0) {
std::cerr << "Listen error" << std::endl;
exit(EXIT_FAILURE);
}
}
void serve() {
sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
while (true) {
int connfd = accept(listenfd_, (struct sockaddr*)&client_addr, &client_len);
if (connfd < 0) {
std::cerr << "Accept error" << std::endl;
continue;
}
std::cout << "Connection established" << std::endl;
char buffer[BUFSIZE];
while (true) {
memset(buffer, 0, BUFSIZE);
int bytes_received = read(connfd, buffer, BUFSIZE);
if (bytes_received < 0) {
std::cerr << "Read error" << std::endl;
break;
}
if (bytes_received == 0) {
std::cout << "Client disconnected" << std::endl;
break;
}
std::cout << "Received message: " << buffer << std::endl;
write(connfd, buffer, bytes_received);
}
close(connfd);
}
}
~TcpServer() {
if (listenfd_ != -1) {
close(listenfd_);
}
}
private:
uint16_t port_;
int listenfd_;
};
int main(int argc, char* argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <port>" << std::endl;
return 1;
}
uint16_t port = static_cast<uint16_t>(std::atoi(argv[1]));
TcpServer server(port);
server.serve();
return 0;
}
TCP 客户端代码(tcp_client.cpp)
#include <iostream>
#include <cstring>
#include <arpa/inet.h>
#define BUFSIZE 1024
class TcpClient {
public:
TcpClient(const std::string& server_ip, uint16_t server_port)
: server_ip_(server_ip), server_port_(server_port), sockfd_(-1) {
if ((sockfd_ = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
std::cerr << "Socket creation error" << std::endl;
exit(EXIT_FAILURE);
}
}
void connect_and_communicate() {
sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(server_port_);
server_addr.sin_addr.s_addr = inet_addr(server_ip_.c_str());
if (connect(sockfd_, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
std::cerr << "Connect error" << std::endl;
exit(EXIT_FAILURE);
}
std::cout << "Connected to the server" << std::endl;
char buffer[BUFSIZE];
while (true) {
std::cout << "Enter message: ";
std::cin.getline(buffer, BUFSIZE);
if (write(sockfd_, buffer, std::strlen(buffer)) < 0) {
std::cerr << "Write error" << std::endl;
break;
}
memset(buffer, 0, BUFSIZE);
if (read(sockfd_, buffer, BUFSIZE) <
多线程TCP服务器代码(threaded_tcp_server.cpp)
#include <iostream>
#include <cstring>
#include <arpa/inet.h>
#include <unistd.h>
#include <thread>
#include <vector>
#define BUFSIZE 1024
// 处理每个客户端的函数
void handle_client(int client_fd, sockaddr_in client_addr) {
char buffer[BUFSIZE];
std::vector<char> reply(BUFSIZE);
while (true) {
memset(buffer, 0, BUFSIZE);
int bytes_received = read(client_fd, buffer, BUFSIZE);
if (bytes_received <= 0) {
std::cout << "Client disconnected or read error" << std::endl;
break;
}
std::cout << "Received message from " << inet_ntoa(client_addr.sin_addr) << ":" << ntohs(client_addr.sin_port) << ": " << buffer << std::endl;
// Echo the message back to the client
std::strcpy(reply.data(), buffer);
write(client_fd, reply.data(), std::strlen(buffer));
}
close(client_fd);
}
// 线程函数,接受新的客户端连接
void accept_clients(int listenfd) {
sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
while (true) {
int client_fd = accept(listenfd, (struct sockaddr*)&client_addr, &client_len);
if (client_fd < 0) {
std::cerr << "Accept error" << std::endl;
continue;
}
std::cout << "New client connected: " << inet_ntoa(client_addr.sin_addr) << ":" << ntohs(client_addr.sin_port) << std::endl;
// 创建新线程处理客户端
std::thread client_thread(handle_client, client_fd, client_addr);
client_thread.detach(); // 分离线程,让它独立运行
}
}
class ThreadedTcpServer {
public:
ThreadedTcpServer(uint16_t port) : port_(port), listenfd_(-1) {
if ((listenfd_ = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
std::cerr << "Socket creation error" << std::endl;
exit(EXIT_FAILURE);
}
sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port_);
server_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(listenfd_, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
std::cerr << "Bind error" << std::endl;
exit(EXIT_FAILURE);
}
if (listen(listenfd_, 5) < 0) {
std::cerr << "Listen error" << std::endl;
exit(EXIT_FAILURE);
}
}
void serve() {
accept_clients(listenfd_);
}
~ThreadedTcpServer() {
if (listenfd_ != -1) {
close(listenfd_);
}
}
private:
uint16_t port_;
int listenfd_;
};
int main(int argc, char* argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <port>" << std::endl;
return 1;
}
uint16_t port = static_cast<uint16_t>(std::atoi(argv[1]));
ThreadedTcpServer server(port);
server.serve();
return 0;
}
原文地址:https://blog.csdn.net/lcadna/article/details/143558647
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!