赞
踩
hello !大家好呀! 欢迎大家来到我的Linux高性能服务器编程系列之高性能服务器框架介绍,在这篇文章中,你将会学习到高效的创建自己的高性能服务器,并且我会给出源码进行剖析,以及手绘UML图来帮助大家来理解,希望能让大家更能了解网络编程技术!!!
希望这篇文章能对你有所帮助,大家要是觉得我写的不错的话,那就点点免费的小爱心吧!(注:这章对于高性能服务器的架构非常重要哟!!!)
目录
C/S模型,即客户端/服务器模型(Client/Server Model),是一种网络计算模型,它将任务和工作负载分配到客户端和服务器两个不同的计算环境中。在这种模型中,客户端负责发送请求,而服务器负责处理请求并返回响应。
如图:
客户端(Client):
服务器(Server):
我们可以使用多线程来进行实现,一个连接的业务处理分配一个线程:
核心代码如下:
线程处理函数:
- // 定义线程函数
- void *handle_client(void *socket_desc) {
- int sock = *(int*)socket_desc;
- char *message;
- int len;
-
- // 接收客户端数据
- while((len = read(sock, message, 1024)) > 0) {
- printf("收到数据:%s\n", message);
- // 发送响应
- write(sock, "Hello, Client!", 14);
- memset(message, 0, 1024);
- }
-
- // 关闭套接字
- close(sock);
- return 0;
- }
主函数:
-
- int main() {
- int sock, newsock, clilen;
- struct sockaddr_in serv_addr, cli_addr;
- int *new_sock;
- pthread_t thread_id;
-
- // 创建套接字
- sock = socket(AF_INET, SOCK_STREAM, 0);
- if (sock == -1) {
- printf("Could not create socket");
- }
-
- // 填充服务器地址结构
- serv_addr.sin_family = AF_INET;
- serv_addr.sin_addr.s_addr = INADDR_ANY;
- serv_addr.sin_port = htons(8080);
-
- // 绑定套接字到地址
- if (bind(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
- perror("bind failed");
- return -1;
- }
-
- // 监听套接字
- listen(sock, 3);
- printf("Listening...\n");
- clilen = sizeof(cli_addr);
-
- // 接受客户端连接
- while((newsock = accept(sock, (struct sockaddr *)&cli_addr, (socklen_t*)&clilen)) > 0) {
- // 创建新线程
- new_sock = malloc(1);
- *new_sock = newsock;
- if (pthread_create(&thread_id, NULL, handle_client, (void*)new_sock) < 0) {
- perror("could not create thread");
- return -1;
- }
- pthread_detach(thread_id);
- }
-
- // 关闭套接字
- if (newsock < 0) {
- perror("accept failed");
- return -1;
- }
-
- return 0;
- }
-
-
在 Linux 环境中,P2P(点对点)模型是一种直接连接两个或多个计算机的网络通信方式,其中没有中心服务器参与数据传输。在 P2P 模型中,每个节点既是客户端也是服务器,可以相互发送和接收数据。
节点间通信:
网络拓扑:
如图:
代码和C/S相似,大家可以去网上自行寻找资料,这里就不再重复了哦!
Reactor 模式是一种事件驱动的网络编程模式,用于处理高并发网络服务。在 Reactor 模式中,一个或多个线程负责监听网络事件,当事件发生时,例如新的连接请求、数据到达等,Reactor 模式会触发相应的处理函数来处理这些事件。
事件循环:
事件处理器:
事件分派器:
事件源:
流程图如下:
使用的是同步I/O模型。
1) 主线程往epol l 内核事件表中注册socket上的读就绪事件。
2) 主线程调用epol l _ wait等待 socket 上有数据可读。
3)当socket 上有数据可读时, epoll _ wait通知主线程。主线程则将socket 可读事件放入请求队列。
4)睡眠在请求队列上的某个工作线程被唤醒,它从 socket 读取数据,并处理客户请求,然后往epol l内核事件表中注册该socket 上的写就绪事件。
5) 主线程调用epoll _ wait等待 socket 可写。
6)当socket 可写时, epoll wait通知主线程。主线程将socket 可写事件放入请求队列。
7)睡眠在请求队列上的某个工作线程被唤醒,它往socket上写入服务器处理客户请求的结果。
Proactor 模式是一种事件驱动的网络编程模式,与 Reactor 模式类似,但它使用异步 I/O 操作来处理网络事件。在 Proactor 模式中,一个或多个线程负责监听网络事件,当事件发生时,例如新的连接请求、数据到达等,Proactor 模式会触发相应的处理函数来处理这些事件。
事件循环:
事件处理器:
事件分派器:
事件源:
异步 I/O 操作:
具体流程图如下:
1)主线程调用aio _ read 函数向内核注册socket 上的读完成事件, 并告诉内核用户读缓冲区的位置,以及读操作完成时如何通知应用程序(这里以信号为例,详情请参考sigevent的man手册)。
2)主线程继续处理其他逻辑。
3)当socket上的数据被读入用户缓冲区后,内核将向应用程序发送一个信号,以通知应用程序数据已经可用。
4)应用程序预先定义好的信号处理函数选择一个工作线程来处理客户请求。工作线程处理完客户请求之后,调用aio _ write函数向内核注册 socket上的写完成事件, 并告诉内核用户写缓冲区的位置,以及写操作完成时如何通知应用程序(仍然以信号为例)。
5)主线程继续处理其他逻辑。
6)当用户缓冲区的数据被写入socket之后,内核将向应用程序发送一个信号,以通知应用程序数据已经发送完毕。
7)应用程序预先定义好的信号处理函数选择一个工作线程来做善后处理,比如决定是否关闭 socket。
这里给出一个代码例子:
事件处理函数:
- // 事件处理函数
- void *handle_connection(void *socket_desc) {
- int sock = *(int*)socket_desc;
- char *message;
- int len;
-
- // 接收客户端数据
- while((len = read(sock, message, 1024)) > 0) {
- printf("收到数据:%s\n", message);
- // 发送响应
- write(sock, "Hello, Client!", 14);
- memset(message, 0, 1024);
- }
-
- // 关闭套接字
- close(sock);
- return 0;
- }
循环逻辑:
- while(1) {
- activity = epoll_wait(epollfd, events, MAX_EVENTS, -1);
- if ((activity < 0) && (errno != EINTR)) {
- printf("epoll_wait error");
- return -1;
- }
-
- for (i = 0; i < activity; i++) {
- if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) || (!(events[i].events & EPOLLIN))) {
- // 处理错误情况
- close(events[i].data.fd);
- continue;
- }
-
- if (events[i].data.fd == sock) {
- // 有新的客户端连接
- newsock = accept(sock, (struct sockaddr *)&cli_addr, (socklen_t*)&clilen);
- printf("新的客户端连接:%s\n", inet_ntoa(cli_addr.sin_addr));
- client_sockets[i] = newsock;
- event.events = EPOLLIN;
- event.data.fd = newsock;
- if (epoll_ctl(epollfd, EPOLL_CTL_ADD, newsock, &event) == -1) {
- perror("epoll_ctl failed");
- return -1;
- }
- } else {
- // 处理客户端数据
- new_sock = malloc(1);
- *new_sock = events[i].data.fd;
- if (pthread_create(&thread_id, NULL, handle_connection, (void*)new_sock) < 0) {
- perror("could not create thread");
- return -1;
- }
- pthread_detach(thread_id);
- }
- }
- }
-
- // 关闭 epoll 实例
- close(epollfd);
-
- return 0;
- }
在这个例子中,服务器端使用 epoll
函数来实现 Proactor 模式,创建一个简单的服务器。服务器使用 pthread
库来创建多线程来处理多个客户端的连接,每个连接都由一个单独的线程处理。服务器收到客户端的请求后,发送一个响应,并关闭与客户端的连接。(不是完整代码哦!)
好啦!到这里这篇文章就结束啦,关于实例代码中我写了很多注释,如果大家还有不懂得,可以评论区或者私信我都可以哦!! 感谢大家的阅读,我还会持续创造网络编程相关内容的,记得点点小爱心和关注哟!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。