这两天“弃暗投明”先放下《Unix环境高级编程》,拿起了不到200页的《TCP/IP 网络变成技术基础》教科书。觉得这个档位正好适合自己目前的水平,等看完这个,再回过头去学习前者。
原来我一直口述的 web server 应该叫面向连接服务器,下面就是把书上的例子简化,对之前的循环服务器增加了多进程,使地其具备支持并发的能力。
这篇笔记先实现一个不固定进程数的并发模型。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <signal.h> #include <string.h> #define SERV_PORT 8031 int main(void) { int lfd, cfd; struct sockaddr_in serv_addr,clin_addr; socklen_t clin_len; pid_t pid; char buf[1024]; int len; if((lfd = socket(AF_INET,SOCK_STREAM,0)) == -1){ perror("create socket failed"); exit(1); } int opt = 1; setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(SERV_PORT); if(bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) { perror("bind error"); exit(1); } if(listen(lfd, 128) == -1) { perror("listen error"); exit(1); } clin_len = sizeof(clin_addr); signal(SIGCLD,SIG_IGN); while(1) { if((cfd = accept(lfd, (struct sockaddr *)&clin_addr, &clin_len)) == -1) { perror("accept error"); exit(1); } pid = fork(); if (pid > 0) { // 在父进程中关闭连接的套接字描述符,只是把 cfd 的引用数减少1,在子进程中还在使用 cfd close(cfd); } else if (pid == 0) { // 子进程关闭 lfd 处理任务,使其回到 TIME_WAIT 状态值 close(lfd); len = read(cfd,buf,sizeof(buf)); char web_result[] = "HTTP/1.1 200 OK\r\nContent-Type:text/html\r\nContent-Length: 11\r\nServer: mengkang\r\n\r\nhello world"; write(cfd,web_result,sizeof(web_result)); close(cfd); exit(0); } else { perror("fork error"); exit(1); } } close(lfd); return 0; }
小插曲:
之前忘记了在子进程处理完任务之后退出了(少写了76行的
exit(0)
),这样子进程又会while
循环执行accept
,而lfd
已经在子进程中关闭了,所以出现了服务器这边查看会有accept error: Bad file descriptor
的错误信息,我wget
了下,发现一次请求就一次报错,但是内容却是请求到了的情况。在71行代码是注释掉的情况下,ab压测是无法执行的,报错
apr_socket_recv: connection reset by peer (104)
,详细说明
这里还没有对子进程个数做限定,压测的时候,如果请求多,并发大,则会创建太多子进程而退出的情况,所以还需要对上面的代码的子进程做线程池管理。可以参考http://mengkang.net/576.html#server02
👇 下面是我的公众号,高质量的博文我会第一时间同步到公众号,给个关注吧!