Chinaunix首页 | 论坛 | 博客
  • 博客访问: 37505
  • 博文数量: 7
  • 博客积分: 172
  • 博客等级: 入伍新兵
  • 技术积分: 95
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-10 22:50
文章分类

全部博文(7)

文章存档

2012年(7)

我的朋友

分类: C/C++

2012-02-16 16:49:01

最近看unp,其中提到了这么几个事,写下备忘。

服务器多进程需注意的地方

一个典型的服务器多进程和客户端的框架大概是这样。

  1. int main(int argc, char* argv[])
  2. {
  3.     int connfd;
  4.     pid_t childpid;
  5.     struct sockaddr_in cliaddr, servaddr;
  6.     int clien;
  7.     int n = 0;
  8.     char buf[1024] = "";
  9.     int listenfd = socket(AF_INET, SOCK_STREAM, 0);
  10.     bzero(&servaddr, sizeof(servaddr));
  11.     servaddr.sin_family = AF_INET;
  12.     servaddr.sin_port = htons(8888);
  13.     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
  14.     bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
  15.     listen(listenfd, 200);
  16.     signal(SIGCHLD, sig_chld);
  17.     while (1)
  18.     {
  19.         clien = sizeof(cliaddr);
  20.         if (connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &clien)<0)
  21.         {
  22.             if (errno == EINTR)
  23.                 continue;
  24.             else
  25.                 exit(1);
  26.         }
  27.         if ((childpid = fork() == 0))
  28.         {
  29.             close(listenfd);
  30.             str_echo(connfd);
  31.             exit(0);
  32.         }
  33.         close(connfd);
  34.     }
  35.     return 0;
  36. }

程序中没有对任何错误进行检测。

1建立套接字

2套接字清0,并填充套接字

3与本地端口进行邦定

4监听

5while循环,调用acceptaccept参数用来标识客户端信息,不关心可直接写null

6主进程fork

7子进程执行相应的数据处理

8主进程关闭监听接口(子进程创建会拷贝父进程的资源,为了减少资源占用应该将主进程套接口关闭。

  1. #include <unistd.h>
  2. #include <sys/socket.h>
  3. #include <netinet/in.h>
  4. #include <stdio.h>

  5. #define MAXLINE 1024
  6. void str_cli(FILE* fp, int sockfd)
  7. {
  8.     char sendline[MAXLINE], recvline[MAXLINE];
  9.     while (fgets(sendline, MAXLINE, fp) != NULL)
  10.     {
  11.         write(sockfd, sendline, strlen(sendline));
  12.         if (read(sockfd, recvline, MAXLINE) == 0)
  13.         {
  14.             exit (1);
  15.         }
  16.         fputs(recvline, stdout);
  17.     }
  18. }

  19. int main(int argc, char* argv[])
  20. {
  21.     int fd;
  22.     struct sockaddr_in servaddr;
  23.     int n;
  24.     fd = socket(AF_INET, SOCK_STREAM, 0);
  25.     bzero(&servaddr, sizeof(servaddr));
  26.     servaddr.sin_family = AF_INET;
  27.     servaddr.sin_port = htons(8888);
  28.     inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
  29.     connect(fd, (struct sockaddr*)&servaddr, sizeof(servaddr));
  30.     str_cli(stdin, fd);    
  31.     exit (0);
  32. }

客户端:

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) |
给主人留下些什么吧!~~