中小企业网站查询,响应式旅行社展业网站开发调研报告,如何做自己的网站表白,最近刚发生的新闻目录
一、网络编程基础概念
1.1 网络协议
1.2 IP地址和端口号
1.3 Socket
1.4 TCP协议的三次握手和四次挥手
TCP的三次握手
TCP的四次挥手
整个流程更通俗易懂
TCP 三次握手流程图
TCP 四次挥手流程图
1.5 详细介绍一下http协议
HTTP协议的主要特点
HTTP请求
HTT…目录
一、网络编程基础概念
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。端口号用于区分同一设备上的不同服务范围为0到65535。
1.3 Socket
Socket是网络编程的基石提供了应用层与TCP/IP协议栈通信的接口。主要类型有
流式套接字Stream Socket基于TCP提供可靠的数据传输。数据报套接字Datagram Socket基于UDP适用于无连接的数据传输。
1.4 TCP协议的三次握手和四次挥手
TCP的三次握手
三次握手Three-way Handshake是TCP建立连接的过程确保双方能够正确接收和发送数据。三次握手的步骤如下 第一次握手SYN 客户端向服务器发送一个SYNSynchronize报文表示请求建立连接。报文头中的SYN标志位被置为1同时生成一个初始序列号Sequence Number假设为x。客户端 - 服务器SYN1, Seqx 第二次握手SYN-ACK 服务器收到客户端的SYN报文后确认连接请求并向客户端发送一个SYN-ACKSynchronize-Acknowledgment报文。报文头中的SYN和ACK标志位都被置为1ACK号为x1确认已收到客户端的SYN并生成一个自己的初始序列号假设为y。服务器 - 客户端SYN1, ACK1, Seqy, Ackx1 第三次握手ACK 客户端收到服务器的SYN-ACK报文后向服务器发送一个ACKAcknowledgment报文表示确认连接建立。报文头中的ACK标志位被置为1ACK号为y1确认已收到服务器的SYN序列号为x1。客户端 - 服务器ACK1, Seqx1, Acky1
完成三次握手后客户端和服务器之间的TCP连接正式建立可以开始数据传输。
TCP的四次挥手
四次挥手Four-way Handshake是TCP断开连接的过程确保双方都能正常关闭连接。四次挥手的步骤如下 第一次挥手FIN 一方通常是客户端向另一方发送一个FINFinish报文表示希望关闭连接。报文头中的FIN标志位被置为1序列号为u。客户端 - 服务器FIN1, Sequ 第二次挥手ACK 另一方通常是服务器收到FIN报文后向发送方发送一个ACK报文确认已经收到关闭连接的请求。报文头中的ACK标志位被置为1ACK号为u1。服务器 - 客户端ACK1, Seqv, Acku1 第三次挥手FIN 服务器也向客户端发送一个FIN报文表示同意关闭连接。报文头中的FIN标志位被置为1序列号为w。服务器 - 客户端FIN1, Seqw 第四次挥手ACK 客户端收到服务器的FIN报文后向服务器发送一个ACK报文确认已经收到关闭连接的请求。报文头中的ACK标志位被置为1ACK号为w1。客户端 - 服务器ACK1, Sequ1, Ackw1
完成四次挥手后客户端和服务器之间的TCP连接正式关闭。
通过三次握手和四次挥手机制TCP协议能够确保可靠地建立和关闭连接使得数据传输变得可靠和有序。这是网络编程中非常重要的部分理解和掌握这些概念对于实现高效和稳定的网络通信至关重要。
整个流程更通俗易懂
TCP 三次握手流程图
客户端 服务器| || --------- SYN, Seqx ------------ || || ------ SYN, ACK, Seqy, Ackx1 --|| || --------- ACK, Seqx1, Acky1 --|| |
解释 第一次握手客户端发送SYN 客户端向服务器发送一个SYN同步包表明客户端想要建立连接并且包含一个初始序列号Seqx。 第二次握手服务器回应SYN-ACK 服务器收到SYN包后回应一个SYN-ACK包表示同意连接。这个包包含服务器的初始序列号Seqy和对客户端SYN包的确认Ackx1。 第三次握手客户端发送ACK 客户端收到SYN-ACK包后回应一个ACK包确认服务器的SYN包Acky1此时连接建立双方可以开始传输数据。
TCP 四次挥手流程图
客户端 服务器| || --------- FIN, Sequ ------------ || || --------- ACK, Seqv, Acku1 ----|| || --------- FIN, Seqw ------------ || || --------- ACK, Sequ1, Ackw1 --|| |
解释 第一次挥手客户端发送FIN 客户端发送一个FIN终止包表示客户端要关闭连接并且包含当前序列号Sequ。 第二次挥手服务器回应ACK 服务器收到FIN包后回应一个ACK包确认客户端的FIN包Acku1此时客户端到服务器的连接关闭但服务器到客户端的连接仍然存在。 第三次挥手服务器发送FIN 服务器也发送一个FIN包表示服务器也要关闭连接并且包含当前序列号Seqw。 第四次挥手客户端回应ACK 客户端收到服务器的FIN包后回应一个ACK包确认服务器的FIN包Ackw1此时整个连接正式关闭。
这些流程图展示了TCP协议在建立和关闭连接时的具体步骤帮助理解TCP三次握手和四次挥手的机制。这样你可以更直观地看到每一步是如何进行的以及每一步的作用。
1.5 详细介绍一下http协议
HTTPHyperText Transfer Protocol超文本传输协议是用于分布式、协作和超媒体信息系统的应用层协议是万维网数据通信的基础。HTTP起初由蒂姆·伯纳斯-李Tim Berners-Lee为万维网设计现由互联网工程任务组IETF和万维网联盟W3C共同维护。HTTP协议定义了浏览器客户端与Web服务器之间的通信规则。
HTTP协议的主要特点 简单快速 客户端向服务器请求服务时只需传送请求方法和路径。由于HTTP协议简单使得HTTP服务器的程序规模小因而通信速度很快。 灵活 HTTP允许传输任意类型的数据对象。通过Content-Type头可以表示具体的数据类型。 无连接 无连接的意思是限制每次连接只处理一个请求。服务器处理完客户端的请求并收到客户端的应答后就断开连接。但这种方式能节省传输时间。 无状态 HTTP协议是无状态协议即对事务处理没有记忆能力。每次请求都是独立的服务器不保留任何会话信息。
HTTP请求
一个HTTP请求由以下部分组成 请求行包括请求方法、请求URL和HTTP版本。 示例GET /index.html HTTP/1.1 请求头包括各种头部信息用于客户端向服务器传递附加信息。 示例 Host: www.example.com
User-Agent: Mozilla/5.0
Accept: text/html 空行用于分隔请求头和请求体。 请求体包含客户端发送给服务器的数据仅在POST、PUT等请求方法中使用。
HTTP请求方法
常见的HTTP请求方法包括
GET请求获取指定资源。常用于请求数据。POST向指定资源提交数据。常用于提交表单或上传文件。PUT上传指定资源的最新内容。DELETE删除指定资源。HEAD类似于GET请求只不过返回的响应中没有具体的内容用于获取报头。OPTIONS返回服务器支持的HTTP请求方法。PATCH对资源进行部分修改。
HTTP响应
一个HTTP响应由以下部分组成 状态行包括HTTP版本、状态码和状态描述。 示例HTTP/1.1 200 OK 响应头包括各种头部信息用于服务器向客户端传递附加信息。 示例 Content-Type: text/html
Content-Length: 1234 空行用于分隔响应头和响应体。 响应体包含服务器返回给客户端的数据。
常见的HTTP状态码 1xx信息性状态码表示请求已被接收继续处理。 100 Continue101 Switching Protocols 2xx成功状态码表示请求已成功被服务器接收、理解并接受。 200 OK201 Created 3xx重定向状态码表示需要客户端采取进一步的操作以完成请求。 301 Moved Permanently302 Found304 Not Modified 4xx客户端错误状态码表示请求包含错误或无法完成。 400 Bad Request401 Unauthorized403 Forbidden404 Not Found 5xx服务器错误状态码表示服务器在处理请求的过程中发生了错误。 500 Internal Server Error502 Bad Gateway503 Service Unavailable
HTTP/1.1 vs HTTP/2 vs HTTP/3
HTTP/1.1
长连接默认使用长连接允许多个请求/响应在同一连接上进行减少了连接的建立和关闭次数提高了传输效率。管道化允许客户端在收到HTTP响应之前发送多个HTTP请求但服务器仍然会按顺序响应。
HTTP/2
二进制分帧使用二进制格式传输数据更高效和更容易解析。多路复用允许同时通过单一的HTTP/2连接发送多个请求和响应。头部压缩使用HPACK算法对头部信息进行压缩减少带宽占用。服务器推送服务器可以主动向客户端推送资源而不需要客户端明确请求。
HTTP/3
基于QUICHTTP/3基于QUIC协议使用UDP传输减少了连接建立的延迟。更快的连接建立由于QUIC在单个数据包内完成握手大大减少了连接建立时间。改进的多路复用避免了HTTP/2中的队头阻塞问题更高效。
HTTP安全
HTTP本身是明文传输的容易被窃听和篡改。为了提高安全性通常使用HTTPSHTTP 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_INETIPv4或AF_INET6IPv6。typeSocket类型如SOCK_STREAMTCP或SOCK_DGRAMUDP。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.hint 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、网络安全等内容敬请期待。 五、深入探讨 select 和 epoll以及多客户端编程
在网络编程中处理多个客户端连接是一项常见且重要的任务。为了有效地管理多个连接操作系统提供了多种I/O多路复用机制如select、poll和epoll。本文将详细讲解select和epoll并展示如何使用它们实现多客户端编程。
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 的步骤
初始化文件描述符集合。将需要监视的文件描述符添加到集合中。调用 select 函数等待事件发生。遍历文件描述符集合处理已发生的事件。
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多路复用机制提供比 select 和 poll 更高效的事件通知。epoll 使用事件驱动模型通过内核维护一个事件表减少用户空间和内核空间之间的拷贝开销。
5.2.2 epoll 函数
epoll 主要包含以下三个函数
epoll_create1创建一个 epoll 实例。epoll_ctl控制 epoll 实例添加、删除或修改事件。epoll_wait等待事件发生并返回已触发的事件。
5.2.3 使用 epoll 的步骤
创建 epoll 实例。使用 epoll_ctl 函数将文件描述符添加到 epoll 实例中。调用 epoll_wait 函数等待事件发生。处理已触发的事件。
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、select 和 epoll 的比较
5.3.1 select 的优缺点
优点
跨平台几乎在所有操作系统上都支持。简单易用适合小规模应用。
缺点
每次调用 select 都需要重新设置文件描述符集合效率较低。支持的文件描述符数量有限一般为1024。
5.3.2 epoll 的优缺点
优点
高效支持大量并发连接适用于高并发场景。事件驱动模型减少了无效的系统调用。
缺点
仅支持Linux跨平台性较差。相对复杂需要更多的代码来管理事件。
5.4、结语
上面详细介绍了 select 和 epoll 的工作原理及其使用方法并通过示例展示了如何实现多客户端编程。对于小规模的网络应用可以选择简单易用的 select而对于高并发、高性能的应用epoll 则是更好的选择。
通过学习和实践这些I/O多路复用技术读者可以更好地理解和掌握网络编程中的并发处理为构建高效稳定的网络应用奠定坚实的基础。