在C语言中,多进程和多线程是实现服务器并发处理的两种常见方式,以下是对这两种方式的详细解析:
1、多进程服务器
概念:多进程服务器是指在操作系统中同时运行多个独立的进程来处理客户端请求,每个进程都有自己独立的地址空间和资源。
优点
稳定性高:由于每个进程都有独立的地址空间,一个进程的崩溃不会影响其他进程,从而提高了服务器的稳定性。
充分利用多核处理器:可以充分利用多核处理器的优势,提高系统的整体性能。
缺点
开销大:进程间的切换会引入较大的开销,并且需要较高的内存开销。
通信复杂:进程间通信相对复杂,需要使用操作系统提供的进程间通信机制,如管道、消息队列等。
实现步骤
创建监听套接字:使用socket函数创建一个监听套接字,用于接收客户端的连接请求。
绑定端口:使用bind函数将监听套接字与指定的端口绑定。
监听端口:使用listen函数开始监听端口,等待客户端的连接请求。
接受连接请求:使用accept函数从已连接的客户端队列中取出一个文件描述符,该文件描述符对应于一个客户端连接。
创建子进程:对于每个客户端连接,父进程通过fork函数创建一个子进程,子进程负责与客户端进行通信,父进程则继续等待下一个客户端连接请求。
子进程处理客户端请求:子进程使用accept返回的文件描述符与客户端进行数据读写操作,处理客户端的请求。
关闭文件描述符:子进程完成任务后,关闭与客户端连接的文件描述符,并退出。
示例代码
#include <sys/socket.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <signal.h> #include <sys/wait.h> void waitchild(int signo) { pid_t wpid; while ((wpid = waitpid(-1, NULL, WNOHANG)) > 0) { printf("child exit, wpid==[%d] ", wpid); } } int main() { int sfd, cfd; struct sockaddr_in server_addr, client_addr; socklen_t clilen; char buff[64]; sfd = socket(AF_INET, SOCK_STREAM, 0); if (sfd < 0) { perror("socket error"); exit(1); } memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(9999); server_addr.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(sfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { perror("bind error"); exit(1); } listen(sfd, 128); signal(SIGCHLD, waitchild); while (1) { clilen = sizeof(client_addr); cfd = accept(sfd, (struct sockaddr *)&client_addr, &clilen); if (cfd < 0) { perror("accept error"); continue; } pid_t pid = fork(); if (pid == 0) { // child process close(sfd); while (1) { memset(buff, 0, sizeof(buff)); int n = read(cfd, buff, sizeof(buff)); if (n <= 0) { break; } printf("child [%d] recv data: %s ", getpid(), buff); for (int i = 0; i < n; i++) { buff[i] = toupper(buff[i]); } n = write(cfd, buff, n); if (n <= 0) { perror("write error"); break; } } close(cfd); exit(0); } else if (pid > 0) { // parent process close(cfd); } else { // fork error perror("fork error"); exit(1); } } }
2、多线程服务器
概念:多线程服务器是指主线程创建子线程,用子线程和客户端通信,每个子线程共享主线程的地址空间和资源。
优点
资源共享:多个线程共享同一个地址空间,可以方便地共享数据和资源,减少了内存开销。
响应速度快:线程间的切换开销相对较小,能够更快地响应客户端的请求。
编程相对简单:与多进程相比,多线程编程相对简单,不需要频繁地进行进程间通信。
缺点
安全性问题:由于多个线程共享同一个地址空间,可能会出现数据竞争和竞态条件等问题,需要使用互斥锁等机制来保证线程的安全性。
稳定性差:一个线程的崩溃可能会导致整个服务器的崩溃,因为所有线程共享同一个地址空间。
受硬件限制:线程的数量受到硬件的限制,过多的线程可能会导致系统性能下降。
实现步骤
创建监听套接字:与多进程服务器类似,首先使用socket函数创建一个监听套接字。
绑定端口:使用bind函数将监听套接字与指定的端口绑定。
监听端口:使用listen函数开始监听端口,等待客户端的连接请求。
接受连接请求:使用accept函数从已连接的客户端队列中取出一个文件描述符,该文件描述符对应于一个客户端连接。
创建子线程:对于每个客户端连接,主线程使用pthread_create函数创建一个子线程,将文件描述符传递给子线程,子线程负责与客户端进行通信。
子线程处理客户端请求:子线程使用传递过来的文件描述符与客户端进行数据读写操作,处理客户端的请求。
回收子线程资源:子线程完成任务后,可以使用pthread_exit函数退出,主线程可以使用pthread_join函数等待子线程结束,回收子线程的资源。
示例代码
#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> void *threadFunc(void *arg) { int cfd = *((int *)arg); free(arg); char buff[64]; while (1) { memset(buff, 0, sizeof(buff)); int n = read(cfd, buff, sizeof(buff)); if (n <= 0) { break; } printf("thread [%ld] recv data: %s ", pthread_self(), buff); for (int i = 0; i < n; i++) { buff[i] = toupper(buff[i]); } n = write(cfd, buff, n); if (n <= 0) { break; } } close(cfd); return NULL; } int main() { int sfd, cfd; struct sockaddr_in server_addr, client_addr; socklen_t clilen; pthread_t tid; sfd = socket(AF_INET, SOCK_STREAM, 0); if (sfd < 0) { perror("socket error"); exit(1); } memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(9999); server_addr.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(sfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { perror("bind error"); exit(1); } listen(sfd, 128); while (1) { clilen = sizeof(client_addr); cfd = accept(sfd, (struct sockaddr *)&client_addr, &clilen); if (cfd < 0) { perror("accept error"); continue; } int *pclient = malloc(sizeof(int)); *pclient = cfd; pthread_create(&tid, NULL, threadFunc, pclient); pthread_detach(tid); } }
3、对比归纳
资源占用方面:多进程服务器每个进程有独立的地址空间,资源占用相对较多;多线程服务器多个线程共享同一个地址空间,资源占用相对较少。
性能方面:多进程服务器在处理大量并发连接时,由于进程间的切换开销较大,性能可能会受到影响;多线程服务器线程间的切换开销较小,响应速度可能更快,但受限于硬件的线程数量。
编程复杂度方面:多进程服务器需要使用进程间通信机制,编程相对复杂;多线程服务器共享数据空间,编程相对简单,但需要注意线程的同步和互斥问题。
稳定性方面:多进程服务器一个进程的崩溃不会影响其他进程,稳定性较高;多线程服务器一个线程的崩溃可能导致整个服务器的崩溃,稳定性较差。
原创文章,作者:未希,如若转载,请注明出处:https://www.kdun.com/ask/1565481.html
本网站发布或转载的文章及图片均来自网络,其原创性以及文中表达的观点和判断不代表本网站。如有问题,请联系客服处理。
发表回复