最近看unp,其中提到了这么几个事,写下备忘。
服务器多进程需注意的地方
一个典型的服务器多进程和客户端的框架大概是这样。
- int main(int argc, char* argv[])
- {
- int connfd;
- pid_t childpid;
- struct sockaddr_in cliaddr, servaddr;
- int clien;
- int n = 0;
- char buf[1024] = "";
- int listenfd = socket(AF_INET, SOCK_STREAM, 0);
- bzero(&servaddr, sizeof(servaddr));
- servaddr.sin_family = AF_INET;
- servaddr.sin_port = htons(8888);
- servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
- bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
- listen(listenfd, 200);
- signal(SIGCHLD, sig_chld);
- while (1)
- {
- clien = sizeof(cliaddr);
- if (connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &clien)<0)
- {
- if (errno == EINTR)
- continue;
- else
- exit(1);
- }
- if ((childpid = fork() == 0))
- {
- close(listenfd);
- str_echo(connfd);
- exit(0);
- }
- close(connfd);
- }
- return 0;
- }
程序中没有对任何错误进行检测。
1建立套接字
2套接字清0,并填充套接字
3与本地端口进行邦定
4监听
5while循环,调用accept,accept参数用来标识客户端信息,不关心可直接写null
6主进程fork
7子进程执行相应的数据处理
8主进程关闭监听接口(子进程创建会拷贝父进程的资源,为了减少资源占用应该将主进程套接口关闭。
- #include <unistd.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <stdio.h>
- #define MAXLINE 1024
- void str_cli(FILE* fp, int sockfd)
- {
- char sendline[MAXLINE], recvline[MAXLINE];
- while (fgets(sendline, MAXLINE, fp) != NULL)
- {
- write(sockfd, sendline, strlen(sendline));
- if (read(sockfd, recvline, MAXLINE) == 0)
- {
- exit (1);
- }
- fputs(recvline, stdout);
- }
- }
- int main(int argc, char* argv[])
- {
- int fd;
- struct sockaddr_in servaddr;
- int n;
- fd = socket(AF_INET, SOCK_STREAM, 0);
- bzero(&servaddr, sizeof(servaddr));
- servaddr.sin_family = AF_INET;
- servaddr.sin_port = htons(8888);
- inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
- connect(fd, (struct sockaddr*)&servaddr, sizeof(servaddr));
- str_cli(stdin, fd);
- exit (0);
- }
客户端:
1建立套接字
2填充套接字
3connect与服务端建立连接
4发送数据
多进程需要注意的问题:
fork函数:由于子进程会复制父进程的所有资源,所以在父进程中必须关闭套接口,否则套接口资源会全部占用完
僵尸进程:当服务端和客户端正常启动时,服务端会有两个进程,一个为父进程在循环监听,另一个为客户端连到服务端所建立的子进程。此时的子进程已经处于ESTABLISHED状态。但此时如果客户端终止,那么服务端的子进程会变成僵尸进程。所以服务端的程序需要对SIGCHLD信号进行处理。
信号处理:为了避免僵局进程的产生,需要在SIGCHLD信号产生时,父进程执行waitpid函数。
while((pid = waitpid(-1, &stat, WNOHANG))>0)循环调用是因为这里需要取得所有子进程的状态,指定WNOHANG是为了让此函数能在有未终止的子进程结束前不要阻塞。
处理被中断的系统调用:父进程是在accep阻塞时捕获了SIGCHLD信号,此时有可能会发生系统调用产生一个EINTR的错误,为了让程序继续运行,需要处理EINTER的错误。在accept()调用后加入if (errno == EINTR)
continue
所以今后要注意:
fork子进程时,一定要释放父进程的资源
派生子进程时,要捕获SIGCHLD信号
捕获信号时,要处理被中断的系统调用
正常编写SIGCHLD信号处理程序.
阅读(1551) | 评论(0) | 转发(0) |