当前位置: 首页 > news >正文

C++后端开发--网络编程基础

目录

一、网络编程基础概念

1.1 网络协议

1.2 IP地址和端口号

1.3 Socket

1.4 TCP协议的三次握手和四次挥手

TCP的三次握手

TCP的四次挥手

整个流程更通俗易懂

TCP 三次握手流程图

TCP 四次挥手流程图

1.5 详细介绍一下http协议

HTTP协议的主要特点

HTTP请求

HTTP请求方法

HTTP响应

常见的HTTP状态码

HTTP/1.1 vs HTTP/2 vs HTTP/3

HTTP/1.1

HTTP/2

HTTP/3

HTTP安全

二、Socket编程

2.1 创建Socket

2.2 绑定地址和端口

2.3 监听和接受连接

2.4 发送和接收数据

2.5 关闭Socket

三、简单的服务器示例

四、结语

五、深入探讨 select 和 epoll,以及多客户端编程

5.1 select 的工作原理及使用

5.1.1 select 基本概念

5.1.2 select 函数原型

5.1.3 使用 select 的步骤

5.1.4 select 示例

5.2 epoll 的工作原理及使用

5.2.1 epoll 基本概念

5.2.2 epoll 函数

5.2.3 使用 epoll 的步骤

5.2.4 epoll 示例

5.3、select 和 epoll 的比较

5.3.1 select 的优缺点

5.3.2 epoll 的优缺点

5.4、结语


网络编程是后端开发中不可或缺的一部分,尤其是在构建需要与其他系统或设备通信的应用程序时。本文将从基础概念开始,逐步深入网络编程的各个方面,帮助读者建立全面的网络编程知识体系。

一、网络编程基础概念

1.1 网络协议

网络协议是计算机网络中进行数据交换的规则。常见的网络协议包括:

  • TCP/IP:传输控制协议/互联网协议,是互联网的核心协议。TCP提供可靠的、面向连接的通信,而IP负责数据包的路由和传输。
  • UDP:用户数据报协议,是一种无连接的协议,适用于实时应用,如视频流和在线游戏。
  • HTTP/HTTPS:超文本传输协议/安全超文本传输协议,用于浏览器和服务器之间的通信。
  • FTP:文件传输协议,用于文件的上传和下载。

1.2 IP地址和端口号

  • IP地址:标识网络中的每个设备,如192.168.1.1
  • 端口号:用于区分同一设备上的不同服务,范围为065535

1.3 Socket

Socket是网络编程的基石,提供了应用层与TCP/IP协议栈通信的接口。主要类型有:

  • 流式套接字(Stream Socket):基于TCP,提供可靠的数据传输。
  • 数据报套接字(Datagram Socket):基于UDP,适用于无连接的数据传输。

1.4 TCP协议的三次握手和四次挥手

TCP的三次握手

三次握手(Three-way Handshake)是TCP建立连接的过程,确保双方能够正确接收和发送数据。三次握手的步骤如下:

  1. 第一次握手(SYN)

    • 客户端向服务器发送一个SYN(Synchronize)报文,表示请求建立连接。
    • 报文头中的SYN标志位被置为1,同时生成一个初始序列号(Sequence Number),假设为x。
    客户端 -> 服务器:SYN=1, Seq=x
  2. 第二次握手(SYN-ACK)

    • 服务器收到客户端的SYN报文后,确认连接请求,并向客户端发送一个SYN-ACK(Synchronize-Acknowledgment)报文。
    • 报文头中的SYN和ACK标志位都被置为1,ACK号为x+1(确认已收到客户端的SYN),并生成一个自己的初始序列号(假设为y)。
    服务器 -> 客户端:SYN=1, ACK=1, Seq=y, Ack=x+1
  3. 第三次握手(ACK)

    • 客户端收到服务器的SYN-ACK报文后,向服务器发送一个ACK(Acknowledgment)报文,表示确认连接建立。
    • 报文头中的ACK标志位被置为1,ACK号为y+1(确认已收到服务器的SYN),序列号为x+1。
    客户端 -> 服务器:ACK=1, Seq=x+1, Ack=y+1

完成三次握手后,客户端和服务器之间的TCP连接正式建立,可以开始数据传输。

TCP的四次挥手

四次挥手(Four-way Handshake)是TCP断开连接的过程,确保双方都能正常关闭连接。四次挥手的步骤如下:

  1. 第一次挥手(FIN)

    • 一方(通常是客户端)向另一方发送一个FIN(Finish)报文,表示希望关闭连接。
    • 报文头中的FIN标志位被置为1,序列号为u。
    客户端 -> 服务器:FIN=1, Seq=u
  2. 第二次挥手(ACK)

    • 另一方(通常是服务器)收到FIN报文后,向发送方发送一个ACK报文,确认已经收到关闭连接的请求。
    • 报文头中的ACK标志位被置为1,ACK号为u+1。
    服务器 -> 客户端:ACK=1, Seq=v, Ack=u+1
  3. 第三次挥手(FIN)

    • 服务器也向客户端发送一个FIN报文,表示同意关闭连接。
    • 报文头中的FIN标志位被置为1,序列号为w。
    服务器 -> 客户端:FIN=1, Seq=w
  4. 第四次挥手(ACK)

    • 客户端收到服务器的FIN报文后,向服务器发送一个ACK报文,确认已经收到关闭连接的请求。
    • 报文头中的ACK标志位被置为1,ACK号为w+1。
    客户端 -> 服务器:ACK=1, Seq=u+1, Ack=w+1

完成四次挥手后,客户端和服务器之间的TCP连接正式关闭。

通过三次握手和四次挥手机制,TCP协议能够确保可靠地建立和关闭连接,使得数据传输变得可靠和有序。这是网络编程中非常重要的部分,理解和掌握这些概念对于实现高效和稳定的网络通信至关重要。

整个流程更通俗易懂
TCP 三次握手流程图
客户端                              服务器|                                    || --------- SYN, Seq=x ------------> ||                                    || <------ SYN, ACK, Seq=y, Ack=x+1 --||                                    || --------- ACK, Seq=x+1, Ack=y+1 -->||                                    |
  • 解释:
  1. 第一次握手:客户端发送SYN

    • 客户端向服务器发送一个SYN(同步)包,表明客户端想要建立连接,并且包含一个初始序列号(Seq=x)。
  2. 第二次握手:服务器回应SYN-ACK

    • 服务器收到SYN包后,回应一个SYN-ACK包,表示同意连接。这个包包含服务器的初始序列号(Seq=y)和对客户端SYN包的确认(Ack=x+1)。
  3. 第三次握手:客户端发送ACK

    • 客户端收到SYN-ACK包后,回应一个ACK包,确认服务器的SYN包(Ack=y+1),此时连接建立,双方可以开始传输数据。
TCP 四次挥手流程图
客户端                              服务器|                                    || --------- FIN, Seq=u ------------> ||                                    || <--------- ACK, Seq=v, Ack=u+1 ----||                                    || <--------- FIN, Seq=w ------------ ||                                    || --------- ACK, Seq=u+1, Ack=w+1 -->||                                    |
  • 解释:
  1. 第一次挥手:客户端发送FIN

    • 客户端发送一个FIN(终止)包,表示客户端要关闭连接,并且包含当前序列号(Seq=u)。
  2. 第二次挥手:服务器回应ACK

    • 服务器收到FIN包后,回应一个ACK包,确认客户端的FIN包(Ack=u+1),此时客户端到服务器的连接关闭,但服务器到客户端的连接仍然存在。
  3. 第三次挥手:服务器发送FIN

    • 服务器也发送一个FIN包,表示服务器也要关闭连接,并且包含当前序列号(Seq=w)。
  4. 第四次挥手:客户端回应ACK

    • 客户端收到服务器的FIN包后,回应一个ACK包,确认服务器的FIN包(Ack=w+1),此时整个连接正式关闭。

这些流程图展示了TCP协议在建立和关闭连接时的具体步骤,帮助理解TCP三次握手和四次挥手的机制。这样,你可以更直观地看到每一步是如何进行的,以及每一步的作用。

1.5 详细介绍一下http协议

HTTP(HyperText Transfer Protocol,超文本传输协议)是用于分布式、协作和超媒体信息系统的应用层协议,是万维网数据通信的基础。HTTP起初由蒂姆·伯纳斯-李(Tim Berners-Lee)为万维网设计,现由互联网工程任务组(IETF)和万维网联盟(W3C)共同维护。HTTP协议定义了浏览器(客户端)与Web服务器之间的通信规则。

HTTP协议的主要特点
  1. 简单快速

    • 客户端向服务器请求服务时,只需传送请求方法和路径。
    • 由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
  2. 灵活

    • HTTP允许传输任意类型的数据对象。通过Content-Type头,可以表示具体的数据类型。
  3. 无连接

    • 无连接的意思是限制每次连接只处理一个请求。服务器处理完客户端的请求并收到客户端的应答后,就断开连接。但这种方式能节省传输时间。
  4. 无状态

    • HTTP协议是无状态协议,即对事务处理没有记忆能力。每次请求都是独立的,服务器不保留任何会话信息。
HTTP请求

一个HTTP请求由以下部分组成:

  1. 请求行:包括请求方法、请求URL和HTTP版本。

    • 示例:GET /index.html HTTP/1.1
  2. 请求头:包括各种头部信息,用于客户端向服务器传递附加信息。

    • 示例:
      Host: www.example.com
      User-Agent: Mozilla/5.0
      Accept: text/html
  3. 空行:用于分隔请求头和请求体。

  4. 请求体:包含客户端发送给服务器的数据(仅在POST、PUT等请求方法中使用)。

HTTP请求方法

常见的HTTP请求方法包括:

  • GET:请求获取指定资源。常用于请求数据。
  • POST:向指定资源提交数据。常用于提交表单或上传文件。
  • PUT:上传指定资源的最新内容。
  • DELETE:删除指定资源。
  • HEAD:类似于GET请求,只不过返回的响应中没有具体的内容,用于获取报头。
  • OPTIONS:返回服务器支持的HTTP请求方法。
  • PATCH:对资源进行部分修改。
HTTP响应

一个HTTP响应由以下部分组成:

  1. 状态行:包括HTTP版本、状态码和状态描述。

    • 示例:HTTP/1.1 200 OK
  2. 响应头:包括各种头部信息,用于服务器向客户端传递附加信息。

    • 示例:
      Content-Type: text/html
      Content-Length: 1234
  3. 空行:用于分隔响应头和响应体。

  4. 响应体:包含服务器返回给客户端的数据。

常见的HTTP状态码
  • 1xx(信息性状态码):表示请求已被接收,继续处理。

    • 100 Continue
    • 101 Switching Protocols
  • 2xx(成功状态码):表示请求已成功被服务器接收、理解并接受。

    • 200 OK
    • 201 Created
  • 3xx(重定向状态码):表示需要客户端采取进一步的操作以完成请求。

    • 301 Moved Permanently
    • 302 Found
    • 304 Not Modified
  • 4xx(客户端错误状态码):表示请求包含错误或无法完成。

    • 400 Bad Request
    • 401 Unauthorized
    • 403 Forbidden
    • 404 Not Found
  • 5xx(服务器错误状态码):表示服务器在处理请求的过程中发生了错误。

    • 500 Internal Server Error
    • 502 Bad Gateway
    • 503 Service Unavailable
HTTP/1.1 vs HTTP/2 vs HTTP/3
HTTP/1.1
  • 长连接:默认使用长连接,允许多个请求/响应在同一连接上进行,减少了连接的建立和关闭次数,提高了传输效率。
  • 管道化:允许客户端在收到HTTP响应之前发送多个HTTP请求,但服务器仍然会按顺序响应。
HTTP/2
  • 二进制分帧:使用二进制格式传输数据,更高效和更容易解析。
  • 多路复用:允许同时通过单一的HTTP/2连接发送多个请求和响应。
  • 头部压缩:使用HPACK算法对头部信息进行压缩,减少带宽占用。
  • 服务器推送:服务器可以主动向客户端推送资源,而不需要客户端明确请求。
HTTP/3
  • 基于QUIC:HTTP/3基于QUIC协议,使用UDP传输,减少了连接建立的延迟。
  • 更快的连接建立:由于QUIC在单个数据包内完成握手,大大减少了连接建立时间。
  • 改进的多路复用:避免了HTTP/2中的队头阻塞问题,更高效。
HTTP安全

HTTP本身是明文传输的,容易被窃听和篡改。为了提高安全性,通常使用HTTPS(HTTP Secure),即HTTP over TLS(或SSL),加密HTTP通信,保证数据的机密性和完整性。

  • HTTPS:通过使用TLS(传输层安全性)来加密数据传输,确保数据在传输过程中不被窃听和篡改。
  • SSL/TLS:安全套接字层/传输层安全性,是一种加密协议,用于保护互联网通信安全。

总结来说,HTTP协议是万维网的基石,通过明确的请求和响应机制,客户端和服务器可以高效地进行通信。随着技术的发展,HTTP协议也在不断演进,以提高性能、安全性和用户体验。

二、Socket编程

2.1 创建Socket

在C++中,可以使用socket()函数创建Socket。语法如下:

int socket(int domain, int type, int protocol);
  • domain:地址族,如AF_INET(IPv4)或AF_INET6(IPv6)。
  • type:Socket类型,如SOCK_STREAM(TCP)或SOCK_DGRAM(UDP)。
  • protocol:协议,一般设为0,由系统自动选择。

示例:

int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {perror("socket creation failed");exit(EXIT_FAILURE);
}

2.2 绑定地址和端口

使用bind()函数将Socket绑定到特定的IP地址和端口。语法如下:

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

示例:

struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = INADDR_ANY;
servaddr.sin_port = htons(8080);if (bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) != 0) {perror("socket bind failed");exit(EXIT_FAILURE);
}

2.3 监听和接受连接

使用listen()函数使Socket进入监听状态,并使用accept()函数接受客户端连接。

int listen(int sockfd, int backlog);
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

示例:

if (listen(sockfd, 5) != 0) {perror("Listen failed");exit(EXIT_FAILURE);
}int connfd = accept(sockfd, (struct sockaddr *)&cli, &len);
if (connfd < 0) {perror("server accept failed");exit(EXIT_FAILURE);
}

2.4 发送和接收数据

使用send()recv()函数在服务器和客户端之间传输数据。

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);

示例:

char buffer[1024] = {0};
recv(connfd, buffer, sizeof(buffer), 0);
send(connfd, "Hello from server", strlen("Hello from server"), 0);

2.5 关闭Socket

使用close()函数关闭Socket。

close(sockfd);

三、简单的服务器示例

以下是一个简单的C++服务器示例,使用TCP协议,监听8080端口,并向每个连接的客户端发送一条欢迎消息。

#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>int main() {int sockfd, connfd;struct sockaddr_in servaddr, cli;// 创建Socketsockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd == -1) {std::cerr << "Socket creation failed\n";exit(EXIT_FAILURE);}// 绑定IP和端口bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(8080);if (bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) != 0) {std::cerr << "Socket bind failed\n";close(sockfd);exit(EXIT_FAILURE);}// 监听连接if (listen(sockfd, 5) != 0) {std::cerr << "Listen failed\n";close(sockfd);exit(EXIT_FAILURE);}socklen_t len = sizeof(cli);connfd = accept(sockfd, (struct sockaddr *)&cli, &len);if (connfd < 0) {std::cerr << "Server accept failed\n";close(sockfd);exit(EXIT_FAILURE);}// 发送欢迎消息const char *message = "Hello from server";send(connfd, message, strlen(message), 0);// 关闭连接close(connfd);close(sockfd);return 0;
}

四、结语

网络编程是后端开发中的重要技能,掌握基础概念和Socket编程方法是深入学习和实践的第一步。本文介绍了网络协议、IP地址、端口号以及Socket编程的基本操作。希望通过这篇文章,读者能对网络编程有一个初步的了解,并能够编写简单的网络应用程序。

在后续的文章中,我们将深入探讨高级网络编程技术,包括多线程服务器、异步I/O、网络安全等内容,敬请期待。

五、深入探讨 selectepoll,以及多客户端编程

在网络编程中,处理多个客户端连接是一项常见且重要的任务。为了有效地管理多个连接,操作系统提供了多种I/O多路复用机制,如selectpollepoll。本文将详细讲解selectepoll,并展示如何使用它们实现多客户端编程。

5.1 select 的工作原理及使用

5.1.1 select 基本概念

select 是一种I/O多路复用机制,用于监视多个文件描述符(如Socket)上的事件,如数据可读、可写或异常状态。当任何一个文件描述符上的事件发生时,select 返回,程序可以对这些事件进行处理。

5.1.2 select 函数原型
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
  • nfds:所有文件描述符中最大值加一。
  • readfds:可读事件的文件描述符集合。
  • writefds:可写事件的文件描述符集合。
  • exceptfds:异常事件的文件描述符集合。
  • timeout:超时时间,NULL表示永不超时。
5.1.3 使用 select 的步骤
  1. 初始化文件描述符集合。
  2. 将需要监视的文件描述符添加到集合中。
  3. 调用 select 函数,等待事件发生。
  4. 遍历文件描述符集合,处理已发生的事件。
5.1.4 select 示例

下面是一个使用 select 实现的简单多客户端服务器示例:

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#include <cstring>#define PORT 8080
#define MAX_CLIENTS 30int main() {int server_fd, new_socket, client_socket[MAX_CLIENTS], max_clients = MAX_CLIENTS, activity, i, valread, sd;int max_sd;struct sockaddr_in address;char buffer[1025];fd_set readfds;// 初始化所有客户端socket为0for (i = 0; i < max_clients; i++) {client_socket[i] = 0;}// 创建服务器socketserver_fd = socket(AF_INET, SOCK_STREAM, 0);if (server_fd == 0) {perror("socket failed");exit(EXIT_FAILURE);}// 绑定地址和端口address.sin_family = AF_INET;address.sin_addr.s_addr = INADDR_ANY;address.sin_port = htons(PORT);if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {perror("bind failed");close(server_fd);exit(EXIT_FAILURE);}// 监听连接if (listen(server_fd, 3) < 0) {perror("listen");close(server_fd);exit(EXIT_FAILURE);}int addrlen = sizeof(address);std::cout << "Listening on port " << PORT << std::endl;while (true) {// 清空文件描述符集合FD_ZERO(&readfds);// 添加服务器socket到集合FD_SET(server_fd, &readfds);max_sd = server_fd;// 添加客户端socket到集合for (i = 0; i < max_clients; i++) {sd = client_socket[i];if (sd > 0)FD_SET(sd, &readfds);if (sd > max_sd)max_sd = sd;}// 等待活动事件activity = select(max_sd + 1, &readfds, NULL, NULL, NULL);if ((activity < 0) && (errno != EINTR)) {std::cerr << "select error" << std::endl;}// 处理新连接if (FD_ISSET(server_fd, &readfds)) {if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {perror("accept");exit(EXIT_FAILURE);}std::cout << "New connection, socket fd is " << new_socket << ", ip is: " << inet_ntoa(address.sin_addr) << ", port: " << ntohs(address.sin_port) << std::endl;// 添加新socket到客户端数组for (i = 0; i < max_clients; i++) {if (client_socket[i] == 0) {client_socket[i] = new_socket;std::cout << "Adding to list of sockets as " << i << std::endl;break;}}}// 处理已连接的客户端的IO操作for (i = 0; i < max_clients; i++) {sd = client_socket[i];if (FD_ISSET(sd, &readfds)) {if ((valread = read(sd, buffer, 1024)) == 0) {// 某客户端断开连接getpeername(sd, (struct sockaddr*)&address, (socklen_t*)&addrlen);std::cout << "Host disconnected, ip: " << inet_ntoa(address.sin_addr) << ", port: " << ntohs(address.sin_port) << std::endl;close(sd);client_socket[i] = 0;} else {buffer[valread] = '\0';std::cout << "Received message: " << buffer << std::endl;send(sd, buffer, strlen(buffer), 0);}}}}return 0;
}

5.2 epoll 的工作原理及使用

5.2.1 epoll 基本概念

epoll 是Linux特有的一种I/O多路复用机制,提供比 selectpoll 更高效的事件通知。epoll 使用事件驱动模型,通过内核维护一个事件表,减少用户空间和内核空间之间的拷贝开销。

5.2.2 epoll 函数

epoll 主要包含以下三个函数:

  1. epoll_create1:创建一个 epoll 实例。
  2. epoll_ctl:控制 epoll 实例,添加、删除或修改事件。
  3. epoll_wait:等待事件发生,并返回已触发的事件。
5.2.3 使用 epoll 的步骤
  1. 创建 epoll 实例。
  2. 使用 epoll_ctl 函数将文件描述符添加到 epoll 实例中。
  3. 调用 epoll_wait 函数等待事件发生。
  4. 处理已触发的事件。
5.2.4 epoll 示例

下面是一个使用 epoll 实现的简单多客户端服务器示例:

#include <iostream>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#include <cstring>#define PORT 8080
#define MAX_EVENTS 10
#define MAX_CLIENTS 30int main() {int server_fd, new_socket, epoll_fd, event_count, i, valread;struct sockaddr_in address;struct epoll_event ev, events[MAX_EVENTS];char buffer[1025];// 创建服务器socketserver_fd = socket(AF_INET, SOCK_STREAM, 0);if (server_fd == 0) {perror("socket failed");exit(EXIT_FAILURE);}// 设置socket为非阻塞fcntl(server_fd, F_SETFL, O_NONBLOCK);// 绑定地址和端口address.sin_family = AF_INET;address.sin_addr.s_addr = INADDR_ANY;address.sin_port = htons(PORT);if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {perror("bind failed");close(server_fd);exit(EXIT_FAILURE);}// 监听连接if (listen(server_fd, 3) < 0) {perror("listen");close(server_fd);exit(EXIT_FAILURE);}// 创建epoll实例epoll_fd = epoll_create1(0);if (epoll_fd == -1) {perror("epoll_create1");close(server_fd);exit(EXIT_FAILURE);}// 添加服务器socket到epoll实例ev.events = EPOLLIN;ev.data.fd = server_fd;if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev) == -1) {perror("epoll_ctl: server_fd");close(server_fd);close(epoll_fd);exit(EXIT_FAILURE);}std::cout << "Listening on port " << PORT << std::endl;while (true) {// 等待事件发生event_count = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);if(event_count == -1) {perror("epoll_wait");close(server_fd);close(epoll_fd);exit(EXIT_FAILURE);}for (i = 0; i < event_count; i++) {if (events[i].data.fd == server_fd) {// 处理新连接new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen);if (new_socket == -1) {perror("accept");continue;}std::cout << "New connection, socket fd is " << new_socket << ", ip is: " << inet_ntoa(address.sin_addr) << ", port: " << ntohs(address.sin_port) << std::endl;// 设置新socket为非阻塞fcntl(new_socket, F_SETFL, O_NONBLOCK);// 添加新socket到epoll实例ev.events = EPOLLIN;ev.data.fd = new_socket;if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, new_socket, &ev) == -1) {perror("epoll_ctl: new_socket");close(new_socket);}} else {// 处理客户端IO操作valread = read(events[i].data.fd, buffer, 1024);if (valread == -1) {perror("read");close(events[i].data.fd);epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, NULL);} else if (valread == 0) {// 客户端断开连接getpeername(events[i].data.fd, (struct sockaddr*)&address, (socklen_t*)&addrlen);std::cout << "Host disconnected, ip: " << inet_ntoa(address.sin_addr) << ", port: " << ntohs(address.sin_port) << std::endl;close(events[i].data.fd);epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, NULL);} else {buffer[valread] = '\0';std::cout << "Received message: " << buffer << std::endl;send(events[i].data.fd, buffer, strlen(buffer), 0);}}}}close(server_fd);close(epoll_fd);return 0;
}

5.3、selectepoll 的比较

5.3.1 select 的优缺点

优点:

  • 跨平台,几乎在所有操作系统上都支持。
  • 简单易用,适合小规模应用。

缺点:

  • 每次调用 select 都需要重新设置文件描述符集合,效率较低。
  • 支持的文件描述符数量有限(一般为1024)。
5.3.2 epoll 的优缺点

优点:

  • 高效,支持大量并发连接,适用于高并发场景。
  • 事件驱动模型,减少了无效的系统调用。

缺点:

  • 仅支持Linux,跨平台性较差。
  • 相对复杂,需要更多的代码来管理事件。

5.4、结语

上面详细介绍了 selectepoll 的工作原理及其使用方法,并通过示例展示了如何实现多客户端编程。对于小规模的网络应用,可以选择简单易用的 select;而对于高并发、高性能的应用,epoll 则是更好的选择。

通过学习和实践这些I/O多路复用技术,读者可以更好地理解和掌握网络编程中的并发处理,为构建高效稳定的网络应用奠定坚实的基础。

相关文章:

C++后端开发--网络编程基础

目录 一、网络编程基础概念 1.1 网络协议 1.2 IP地址和端口号 1.3 Socket 1.4 TCP协议的三次握手和四次挥手 TCP的三次握手 TCP的四次挥手 整个流程更通俗易懂 TCP 三次握手流程图 TCP 四次挥手流程图 1.5 详细介绍一下http协议 HTTP协议的主要特点 HTTP请求 HTT…...

如何将资源前端通过 Docker 部署到远程服务器

作为一个程序员&#xff0c;在开发过程中&#xff0c;经常会遇到项目部署的问题&#xff0c;在现在本就不稳定的大环境下&#xff0c;前端开发也需要掌握部署技能&#xff0c;来提高自己的生存力&#xff0c;今天就详细说一下如何把一个前端资源放到远程服务器上面通过docker部…...

@react-google-maps/api实现谷歌地图嵌入React项目中,并且做到点击地图任意一处,获得它的经纬度

1.第一步要加入项目package.json中或者直接yarn install它都可以 "react-google-maps/api": "^2.19.3",2.加入项目中 import AMapLoader from amap/amap-jsapi-loader;import React, { PureComponent } from react; import { GoogleMap, LoadScript, Mar…...

【MySQL】2.库的操作

库的操作 一.创建数据库1.数据库的编码集 二.查看数据库三.修改数据库四.删除数据库五.数据库的备份和恢复 一.创建数据库 create database [if not exists] db_name [charsetutf8] [collateutf8_general_ci] //创建一个名为db_name的数据库&#xff0c;本质就是在/var/lib/my…...

深入Laravel服务容器:构建灵活应用的秘诀

标题&#xff1a;深入Laravel服务容器&#xff1a;构建灵活应用的秘诀 Laravel框架的服务容器是一个强大的工具&#xff0c;它负责管理类的依赖关系和执行依赖注入&#xff08;DI&#xff09;。服务容器是Laravel依赖注入系统的核心&#xff0c;使得应用组件之间的耦合度降低&…...

3.js - 模板渲染 - 金属切面效果

md&#xff0c;狗不学&#xff0c;我学 源码 // ts-nocheck// 引入three.js import * as THREE from three// 导入轨道控制器 import { OrbitControls } from three/examples/jsm/controls/OrbitControls// 导入lil.gui import { GUI } from three/examples/jsm/libs/lil-gui.m…...

【测试】系统压力测试报告模板(Word原件)

系统压力测试&#xff0c;简而言之&#xff0c;是在模拟高负载、高并发的环境下&#xff0c;对系统进行全面测试的过程。它旨在评估系统在面对极端使用条件时的性能表现&#xff0c;包括处理能力、响应时间、资源消耗及稳定性等关键指标。通过压力测试&#xff0c;开发团队能够…...

图片预加载和懒加载

图片预加载 图片预加载是指在页面展示之前提前加载即将使用到的图片资源&#xff0c;以便当用户需要查看时&#xff0c;能够直接从本地缓存中快速渲染&#xff0c;从而提高页面加载速度和用户体验。 原理 图片预加载通过提前将图片下载到浏览器缓存中&#xff0c;当用户实际…...

Java中的数据可视化与图表库选择

Java中的数据可视化与图表库选择 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 在现代软件开发中&#xff0c;数据可视化是将抽象数据转化为易于理解的图形化…...

STM32-TIM定时器

本内容基于江协科技STM32视频内容&#xff0c;整理而得。 文章目录 1. TIM1.1 TIM定时器1.2 定时器类型1.3 基本定时器1.4 通用定时器1.4 高级定时器1.5 定时中断基本结构1.6 预分频器时序1.7 计数器时序1.8 计数器无预装时序1.9 计数器有预装时序1.10 RCC时钟树 2. TIM库函数…...

Python OpenCV与霍夫变换:检测符合特定斜率范围的直线

在计算机视觉和图像处理领域&#xff0c;检测图像中的直线是一项常见且重要的任务。OpenCV 提供了许多强大的工具来进行图像处理&#xff0c;其中霍夫变换&#xff08;Hough Transform&#xff09;就是用于检测直线的经典方法。本文将介绍如何使用 OpenCV 和霍夫变换来检测图像…...

ubuntu22.04+pytorch2.3安装PyG图神经网络库

ubuntu下安装torch-geometric库&#xff0c;图神经网络 开发环境 ubuntu22.04 conda 24.5.0 python 3.9 pytorch 2.0.1 cuda 11.8 pyg的安装网上教程流传着许多安装方式&#xff0c;这些安装方式主要是&#xff1a;预先安装好pyg的依赖库&#xff0c;这些依赖库需要对应上pyth…...

新型开发语言的试用感受-仓颉语言发布之际

经常听一些媒体介绍一些新型的开发语言&#xff0c;所以最近心血来潮&#xff0c;安装了几种感受了一些。 先看名气&#xff0c;如ziglang&#xff0c;网址为&#xff1a;Home ⚡Zig Programming Language 号称是可以取代C语言的一门新语言&#xff0c;其实我主要是受下面这篇…...

基于字典学习的地震数据降噪(MATLAB R2021B)

稀疏表示基于研究者们提出了许多变换基函数的方法逐渐成型&#xff0c;比如小波域&#xff0c;曲波域&#xff0c;dreamlet 域等&#xff0c;其原理是利用地震信号在变换域内的稀疏性和可分离性以去除噪声。继 Donoho发表非线性去噪方法-小波阈值萎缩方法&#xff0c;在后续的研…...

【Web】

1、配仓库 [rootlocalhost yum.repos.d]# vi rpm.repo ##本地仓库标准写法 [baseos] namemiaoshubaseos baseurl/mnt/BaseOS gpgcheck0 [appstream] namemiaoshuappstream baseurlfile:///mnt/AppStream gpgcheck0 2、挂载 [rootlocalhost ~]mount /dev/sr0 /mnt mount: /m…...

kafka-3

Kafka 消费组 consumer-offsets-N 稀疏索引 Kafka集群 集群搭建 集群启动和验证 Topic的意义 Topic和Partition 分区 副本 集群操作指令 多分区&多副本 多分区消费组 Rebalance机制 Rebalance机制处理流程 Rebalance机制-Range Rebalance机制-RoudRobin Rebalance机制-St…...

MySQL性能优化 二、表结构设计优化

1.设计中间表 设计中间表&#xff0c;一般针对于统计分析功能&#xff0c;或者实时性不高的需求。 2.设计冗余字段 为减少关联查询&#xff0c;创建合理的冗余字段&#xff08;创建冗余字段还需要注意数据一致性问题&#xff09; 3.折表 对于字段太多的大表&#xff0c;考…...

用HttpURLConnection复现http响应码405

目录 使用GET方法&#xff0c;访问GET接口&#xff0c;服务端返回405使用GET方法&#xff0c;访问POST接口&#xff0c;服务端返回405使用POST方法&#xff0c;访问GET接口&#xff0c;服务端返回405 使用GET方法&#xff0c;访问GET接口&#xff0c;服务端返回405 发生场景&a…...

2-27 基于matlab的一种混凝土骨料三维随机投放模型

基于matlab的一种混凝土骨料三维随机投放模型&#xff0c;为混凝土细观力学研究提供一种快捷的三维建模源代码。可设置骨料数量&#xff0c;边界距离、骨料大小等参数。程序已调通&#xff0c;可直接运行。 2-27 matlab 混凝土骨料三维随机投放模型 - 小红书 (xiaohongshu.com)…...

ISA95-Part4-业务流程的解析与设计思路

MES/MOM系统实现ISA-95标准的业务流程通常遵循以下思路,并包含一系列内容。 一、功能模块: 1. 需求分析与规划: - 确定业务流程需求,包括订单管理、生产调度、库存控制等,并规划如何将这些流程与MES/MOM系统集成。 2. 系统集成架构设计: - 设计一个系统集成架构,确保M…...

【Spring Cloud】一个例程快速了解网关Gateway的使用

Spring Cloud Gateway提供了一个在Spring生态系统之上构建的API网关&#xff0c;包括&#xff1a;Spring 5&#xff0c;Spring Boot 2和Project Reactor。Spring Cloud Gateway旨在提供一种简单而有效的路由方式&#xff0c;并为它们提供一些网关基本功能&#xff0c;例如&…...

仿哔哩哔哩视频app小程序模板源码

仿哔哩哔哩视频app小程序模板源码 粉色的哔哩哔哩手机视频网页&#xff0c;多媒体视频类微信小程序ui前端模板下载。包含&#xff1a;视频主页和播放详情页。 仿哔哩哔哩视频app小程序模板源码...

数据库存储引擎

MySQL体系结构 存储引擎 -- 查询建表语句 show create table account; -- 查询引擎 show engines; InnoDB 特点 DML操作遵循ACID模型,支持事务 行级锁&#xff0c;提高并发访问性能支持外键约束 文件 xxx.ibd&#xff1a;xxx代表的是表名,innoDB引擎的每张表都会对应这样…...

【单片机毕业设计选题24049】-基于STM32单片机的智能手表设计

系统功能: 显示时间&#xff0c;温湿度&#xff0c;体温信息&#xff0c;播放音乐及控制红外小夜灯&#xff0c;通过蓝牙模块连接手机APP。 系统上电后OLED显示“欢迎使用智能手表系统请稍后”&#xff0c;两秒后进入正常页面显示 第一行显示获取到的当前时间 第二行显示获…...

利用面向AWS的Thales Sovereign解决方案保护AI之旅

亚马逊网络服务(AWS)是全球最大的云服务提供商。众所周知&#xff0c;他们致力于提供工具、解决方案和最佳实践&#xff0c;使其客户能够安全地利用AWS上的生成式人工智能 (GenAI) 工作负载。组织正在迅速使用GenAI为企业带来更高的生产力和创造力。在GenAI的几乎所有用途中&am…...

学习笔记——交通安全分析13

目录 前言 当天学习笔记整理 5城市主干道交通安全分析 结束语 前言 #随着上一轮SPSS学习完成之后&#xff0c;本人又开始了新教材《交通安全分析》的学习 #整理过程不易&#xff0c;喜欢UP就点个免费的关注趴 #本期内容接上一期12笔记 当天学习笔记整理 5城市主干道交…...

PHP-实例-文件上传

1 需求 2 basename 在 PHP 中&#xff0c;basename() 函数用于返回路径中的文件名部分。如果路径中包含了文件扩展名&#xff0c;则该函数也会返回它。如果路径的结尾有斜杠&#xff08;/&#xff09;或反斜杠&#xff08;\&#xff09;&#xff0c;则 basename() 函数会返回空…...

LeetCode刷题之HOT100之完全平方数

2024 7/7 转眼间就到周日啦&#xff01;昨天下午开组会&#xff0c;开了三个半小时。如坐针毡&#xff0c;会后跑了个步、洗了个澡、洗了衣服、躺床上看了会《罪与罚》&#xff0c;睡着了。早上起来&#xff0c;去拿我昨晚充电的车&#xff0c;当我看到车没有停在昨天的位置&am…...

【SpringCloud应用框架】Nacos集群架构说明

第六章 Spring Cloud Alibaba Nacos之集群架构说明 文章目录 前言一、Nacos支持三种部署模式二、集群部署说明三、预备环境 前言 到目前为止&#xff0c;已经完成了对Nacos的一些基本使用和配置&#xff0c;接下来还需要了解一个非常重要的点&#xff0c;就是Nacos的集群相关的…...

JS进阶-作用域

学习目标&#xff1a; 掌握作用域 学习内容&#xff1a; 作用域局部作用域全局作用域作用域链JS垃圾回收机制拓展-JS垃圾回收机制-算法说明闭包变量提升 作用域&#xff1a; 作用域规定了变量能够被访问的"范围"&#xff0c;离开了这个"范围"变量便不能被…...