C多进程服务器是一种基于C语言实现的、利用多进程技术来处理并发客户端连接的服务器模型,在高并发场景下,它能够有效地提高服务器的处理能力和资源利用率,下面将详细介绍C多进程服务器的相关内容。
多进程服务器是指在操作系统中同时运行多个独立的进程,每个进程都有自己独立的地址空间和资源,服务器使用fork
系统调用创建子进程来和客户端进行通信,父进程则负责取出连接请求,这种模型可以充分利用多核处理器的优势,提高系统的整体性能。
1、创建套接字:使用socket()
函数创建服务器套接字。
2、绑定地址和端口:使用bind()
函数将服务器地址和端口与套接字绑定在一起。
3、监听连接:使用listen()
函数使套接字进入监听状态,等待客户端连接。
4、接受连接并创建子进程:当有新的客户端连接时,使用accept()
函数接受连接,并通过fork()
函数创建一个子进程来处理该连接。
5、子进程处理客户端请求:在子进程中,通过读写操作与客户端进行通信,完成请求的处理后关闭连接并终止子进程。
6、父进程继续等待新的连接:父进程关闭与客户端的连接描述符,并继续等待新的连接请求。
以下是一个使用C语言实现的多进程服务器的示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.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() {
// 阻塞SIGCHLD信号
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &mask, NULL);
int sigbol = 1;
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if (sfd < 0) {
perror("ERROR opening socket");
exit(1);
}
// 设置端口复用
int opt = 1;
setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));
struct sockaddr_in soaddr;
bzero(&soaddr, sizeof(soaddr));
soaddr.sin_family = AF_INET;
soaddr.sin_port = htons(9999);
soaddr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sfd, (struct sockaddr *)&soaddr, sizeof(soaddr)) < 0) {
perror("ERROR on binding");
close(sfd);
exit(1);
}
listen(sfd, 128);
struct sockaddr_in clientsocket;
socklen_t clilen;
char sIP[16];
while (1) {
clilen = sizeof(clientsocket);
bzero(&clientsocket, clilen);
int cfd = accept(sfd, (struct sockaddr *)&clientsocket, &clilen);
if (cfd < 0) {
perror("ERROR on accept");
continue;
}
int pid = fork();
if (pid == 0) { // 子进程
close(sfd);
char buff[64];
printf("current pid is [%d],father is [%d]
", getpid(), getppid());
while (1) {
memset(buff, 0x00, sizeof(buff));
int n = read(cfd, buff, sizeof(buff));
if (n == 0) {
return 0;
} else if (n < 0) {
perror("child read error");
return -1;
}
printf("child [%d] recv data from [%s:%d]:[%s]
", getpid(), inet_ntop(AF_INET, &clientsocket.sin_addr.s_addr, sIP, sizeof(sIP)), ntohs(clientsocket.sin_port), buff);
for (int i = 0; i < n; i++) {
buff[i] = toupper(buff[i]);
}
n = write(cfd, buff, n);
if (n <= 0) {
perror("child write error");
return -1;
}
}
} else if (pid > 0) { // 父进程
close(cfd);
if (sigbol == 1) {
sigbol = 0;
// 注册SIGCHLD信号处理函数
}
} else {
perror("fork failed");
exit(1);
}
}
close(sfd);
return 0;
}
1、优点:稳定性高,一个子进程的崩溃或错误不会影响其他子进程的执行,能够有效地隔离错误,提高服务器的可靠性,适用于对稳定性要求较高的服务器,如Web服务器、文件服务器等。
2、缺点:创建和管理多个进程需要消耗更多的系统资源,包括内存和CPU时间,进程间的切换会引入较大的开销,并且需要特殊的机制来在不同进程间传递数据和同步操作。
1、问:多进程服务器中如何处理僵尸进程?
答:僵尸进程是已经完成但尚未被其父进程回收的进程,在多进程服务器中,可以使用wait
或waitpid
函数来回收子进程的资源,避免僵尸进程的产生,也可以使用信号处理函数来自动回收僵尸进程。
2、问:多进程服务器的性能瓶颈在哪里?
答:多进程服务器的性能瓶颈主要在于进程的创建和管理开销,以及进程间通信的效率,为了提高性能,可以优化进程的创建方式,减少不必要的进程创建,并采用高效的进程间通信机制,如管道、共享内存等。