UNIX网络编程:并发服务器(TCP)
注意,此时listenfd和connfd这两个描述符都在父进程和子进程之间共享(被复制),再下一步是由父进程关闭已连接套接字,由子进程关闭监听套接字。如下图: 在编写TCP并发服务器的时可能会遇到三种情况: 当fork子进程时,必须捕获SIGCHLD信号; 当捕获信号时,必须处理被中断的慢系统调用; SIGCHLD的信号处理函数必须正确编写,应使用waitpid函数,以免留下僵死进程。 我们用术语慢系统调用描述accept,该术语也适用于那些可能永远阻塞的系统调用。 适用于慢系统调用的基本规则是:当阻塞于某个慢系统调用的一个进程捕获某个信号且相应信号处理函数返回时,该系统调用可能返回一个EINTR错误。有些内核自动重启某些被中断的系统调用。不过为了便于移植,当我们编写捕获信号的程序时(多数并发服务器捕获SIGCHLD),我们必须对慢系统调用返回EINTR有所准备。 服务器程序serv.c: #include<stdio.h> #include<sys/types.h> #include<sys/socket.h> #include<unistd.h> #include<stdlib.h> #include<errno.h> #include<arpa/inet.h> #include<netinet/in.h> #include<string.h> #include<signal.h> #define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); } while (0) void do_service(int); int main(void) { signal(SIGCHLD, SIG_IGN); int listenfd; //被动套接字(文件描述符),即只可以accept, 监听套接字 if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) // listenfd = socket(AF_INET, SOCK_STREAM, 0) ERR_EXIT("socket error"); struct sockaddr_in servaddr; memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(5188); servaddr.sin_addr.s_addr = htonl(INADDR_ANY); /* servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); */ /* inet_aton("127.0.0.1", &servaddr.sin_addr); */ int on = 1; if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) ERR_EXIT("setsockopt error"); if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) ERR_EXIT("bind error"); if (listen(listenfd, SOMAXCONN) < 0) //listen应在socket和bind之后,而在accept之前 ERR_EXIT("listen error"); struct sockaddr_in peeraddr; //传出参数 socklen_t peerlen = sizeof(peeraddr); //传入传出参数,必须有初始值 int conn; // 已连接套接字(变为主动套接字,即可以主动connect) pid_t pid; while (1) { if ((conn = accept(listenfd, (struct sockaddr *)&peeraddr, &peerlen)) < 0) //3次握手完成的序列 { if( errno == EINTR ) ///////////////////////////////////////////////////////////////////必须处理被中断的系统调用 continue; else ERR_EXIT("accept error"); } printf("recv connect ip=%s port=%dn", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)); pid = fork(); if (pid == -1) ERR_EXIT("fork error"); if (pid == 0) { // 子进程 close(listenfd); do_service(conn); exit(EXIT_SUCCESS); } else close(conn); //父进程 } return 0; } void do_service(int conn) { char recvbuf[1024]; while (1) { memset(recvbuf, 0, sizeof(recvbuf)); int ret = read(conn, recvbuf, sizeof(recvbuf)); if (ret == 0) //客户端关闭了 { printf("client closen"); break; } else if (ret == -1) ERR_EXIT("read error"); fputs(recvbuf, stdout); write(conn, recvbuf, ret); } } 查看本栏目更多精彩内容:http://www.bianceng.cn/OS/unix/ (编辑:云计算网_泰州站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |