Linux 网络编程——多进程,多线程模型
上一章节"Linux Socket编程基础"已经介绍了单客户端-单服务器的一对一模型,但在实际应用中,服务器要同时处理成千上万个客户端的请求,一对一模型没法对其他客户端响应。
这一章节,我们介绍服务器模型的多进程模型(多线程回收子进程)和多进程模型
多进程模型:
多进程模型服务器demo
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> // socket套接字 #include <unistd.h> #include <arpa/inet.h> // 主机字节序和网络字节序转化 #include <signal.h> #include <pthread.h> #include <sys/wait.h> #define _SERVER_PORT 8080 #define BACK_LOG 128 #define IP_SIZE 16 #define BUFFER_SIZE 1600 void sig_wait(int n) { pid_t zpid; int status; while((zpid = waitpid(-1, &status, WNOHANG)) > 0) { if(WIFEXITED(status)) printf("thread 0x%x wait success, zpid %d exitcode or reval %d\n", (unsigned int)pthread_self(), zpid, WEXITSTATUS(status)); if(WIFSIGNALED(status)) printf("thread 0x%x wait success, zpid %d signal NO.%d\n", (unsigned int)pthread_self(), zpid, WTERMSIG(status)); } } void *thread_wait(void *arg) { // 回收线程 // 被创建后屏蔽了SIGCHLD信号 pthread_detach(pthread_self()); // 设置分离态线程 struct sigaction act, oact; act.sa_handler = sig_wait; act.sa_flags = 0; // 如果信号行为置为handler,flags置0 sigemptyset(&act.sa_mask); sigaction(SIGCHLD, &act, &oact); sigprocmask(SIG_SETMASK, &act.sa_mask, NULL); printf("wait thread 0x%x waiting ...\n", (unsigned int)pthread_self()); while(1) sleep(1); // 睡眠等待信号 } int server_net_init(void) { struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(_SERVER_PORT); server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //设置本机任意IP int server_fd = socket(AF_INET, SOCK_STREAM, 0); if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { perror("bind error"); exit(-1); } listen(server_fd, BACK_LOG); // 将默认主动的socket变为被动类型的 printf("TCP Server Waiting for Connect ...\n"); return server_fd; } int server_recv_response(int sockfd) { char recv_buffer[BUFFER_SIZE]; bzero(recv_buffer, BUFFER_SIZE); ssize_t recvlen; while ((recvlen = recv(sockfd, recv_buffer, sizeof(recv_buffer), 0)) > 0) { printf("%s", recv_buffer); send(sockfd, recv_buffer, recvlen, 0);// 回传 bzero(recv_buffer, BUFFER_SIZE); } if (recvlen == 0) { printf("client exit. child %d exiting..\n", getpid()); exit(0); // return表示函数返回,exit()代表程序退出 } if (recvlen == -1) { printf("recv error"); exit(-1); } return 0; } int server_accepting(int sockfd) { // 阻塞模型,阻塞等待客户端连接 struct sockaddr_in client_addr; socklen_t addrlen = sizeof(client_addr); int client_fd; pid_t pid; char client_ip[IP_SIZE]; bzero(client_ip, IP_SIZE); if ((client_fd = accept(sockfd, (struct sockaddr *)&client_addr, &addrlen)) > 0) { // accept 阻塞函数 pid = fork(); if (pid > 0) { printf("TCP Server Accept Success:client ip[%s] client prot[%d] \n", inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, client_ip, IP_SIZE),ntohs(client_addr.sin_port)); } else if (pid == 0) { server_recv_response(client_fd); } else { perror("Process fail"); exit(-1); } } else { perror("Accept Call Failed"); exit(-1); } return client_fd; } int main() { int sfd; int cfd; // 主线程设置屏蔽 --> 继承给普通线程 sigset_t set, oset; sigemptyset(&set); sigaddset(&set, SIGCHLD); sigprocmask(SIG_SETMASK, &set, &oset); // 回收线程创建 pthread_t tid; pthread_create(&tid, NULL, thread_wait, NULL); sfd = server_net_init(); while(1) { cfd = server_accepting(sfd); } close(cfd); close(sfd); return 0; }
注意!!!!!
return和recv的区别:
1.return 是关键字,exit(0)和_exit(0)是函数。 2.return表示函数返回,而exit()和_exit()代表程序的退出。 return和exit在main函数里是一样的,退出程序并返回值给操作系统。 在普通函数里,exit会退出程序返回到操作系统,return则返回值给上层调用函数。
多线程模型:
多线程模型服务器demo
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> // socket套接字 #include <unistd.h> #include <arpa/inet.h> // 主机字节序和网络字节序转化 #include <pthread.h> #include <sys/wait.h> #define _SERVER_PORT 8080 #define BACK_LOG 128 #define IP_SIZE 16 #define BUFFER_SIZE 1600 int server_recv_response(int sockfd); void *thread_jobs(void *arg) { pthread_detach(pthread_self()); printf("thread 0x%x ... \n", (unsigned int)pthread_self()); // 线程内部使用void *arg参数时, 最好不要直接使用地址 server_recv_response(*(int *)arg); while(1) sleep(1); } int server_net_init(void) { struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(_SERVER_PORT); server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //设置本机任意IP int server_fd = socket(AF_INET, SOCK_STREAM, 0); if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { perror("bind error"); exit(-1); } listen(server_fd, BACK_LOG); // 将默认主动的socket变为被动类型的 printf("TCP Server Waiting for Connect ...\n"); return server_fd; } int server_recv_response(int sockfd) { char recv_buffer[BUFFER_SIZE]; bzero(recv_buffer, BUFFER_SIZE); ssize_t recvlen; while ((recvlen = recv(sockfd, recv_buffer, sizeof(recv_buffer), 0)) > 0) { printf("%s", recv_buffer); send(sockfd, recv_buffer, recvlen, 0);// 回传 bzero(recv_buffer, BUFFER_SIZE); } if (recvlen == 0) { printf("client exit. thread 0x%x exiting..\n", (unsigned int)pthread_self()); } return 0; } int server_accepting(int sockfd) { // 阻塞模型,阻塞等待客户端连接 struct sockaddr_in client_addr; socklen_t addrlen = sizeof(client_addr); int client_fd; pthread_t tid; char client_ip[IP_SIZE]; bzero(client_ip, IP_SIZE); if ((client_fd = accept(sockfd, (struct sockaddr *)&client_addr, &addrlen)) > 0) { // accept 阻塞函数 printf("TCP Server Main thread 0x%x Accept Success:client ip[%s] client prot[%d] \n", (unsigned int)pthread_self(), inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, client_ip, IP_SIZE),ntohs(client_addr.sin_port)); pthread_create(&tid, NULL, thread_jobs, (void *)&client_fd); } else { perror("Accept Call Failed"); exit(-1); } return client_fd; } int main() { int sfd; int cfd; sfd = server_net_init(); while(1) { cfd = server_accepting(sfd); } close(cfd); close(sfd); return 0; }
多进程/多线程 模型的问题:
- 多进程和多线程这类模型都能在一定程度上帮助我们解决一定的并发问题,但,进程/线程是根据客户端的连接动态创建的,服务器的并发量取决于进程/线程数量(数量有限)。
- 频繁的创建或销毁进程/线程,系统开销较大,进程/线程可用性不高(一对一绑定关系)
- 进程/线程缺乏管理和控制
下一章节,我们将介绍IO多路复用,使单进程服务器实现高并发
posted on 2022-04-29 23:41 SocialistYouth 阅读(140) 评论(0) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人