标签:
最近在学习UNP,特此记录。
1. TCP回射服务器程序
#include "unp.h" void str_echo(int sockfd) { ssize_t n; char buf[MAXLINE]; again: while ( (n = read(sockfd, buf, MAXLINE)) > 0) Writen(sockfd, buf, n); if (n < 0 && errno == EINTR) goto again; else if (n < 0) err_sys("str_echo: read error"); } int main(int argc, char **argv) { int listenfd, connfd; pid_t childpid; socklen_t clilen; struct sockaddr_in cliaddr, servaddr; listenfd = Socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT); Bind(listenfd, (SA *)&servaddr, sizeof(servaddr)); Listen(listenfd, LISTENQ); for (;;) { clilen = sizeof(cliaddr); connfd = Accept(listenfd, (SA *)&cliaddr, &clilen); if ( (childpid = Fork()) == 0) { Close(listenfd); str_echo(connfd); exit(0); } Close(connfd); } }
2. TCP回射客户程序
#include "unp.h" void str_cli(FILE *fp, int sockfd) { char sendline[MAXLINE], recvline[MAXLINE]; while (Fgets(sendline, MAXLINE, fp) != NULL) { Writen(sockfd, sendline, strlen(sendline)); if (Readline(sockfd, recvline, MAXLINE) == 0) err_quit("str_cli: server terminated prematurely"); Fputs(recvline, stdout); } } int main(int argc, char **argv) { int sockfd; struct sockaddr_in servaddr; if(argc != 2) err_quit("usage: cli <ipaddress>"); sockfd = Socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(SERV_PORT); Inet_pton(AF_INET, argv[1], &servaddr.sin_addr); Connect(sockfd, (SA *)&servaddr, sizeof(servaddr)); str_cli(stdin, sockfd); exit(0); }
此时,一个简单的客户/服务器程序就写好了。运行服务器程序和客户程序,回射功能正常。
但是,此时程序仍然存在问题。
首先是服务器程序。
服务器子进程终止时,会给父进程发送一个SIGCHLD信号,但是我们没有在代码中捕获该信号,而该信号的默认行为是被忽略。这样,子进程最终进入僵死状态。
即使我们编写了SIGCHLD的信号处理函数,服务器程序仍然存在问题。
因为当SIGCHLD信号递交给父进程时,父进程正阻塞于慢系统调用accept。于是内核就会使accept返回一个EINTR错误。而父进程不处理该错误,于是终止。(有些系统自动重启被中断的系统调用)
因此,我们必须对慢系统调用返回EINTR有所准备。
3. 修正的服务器程序
#include "unp.h" void str_echo(int sockfd) { ssize_t n; char buf[MAXLINE]; again: while ( (n = read(sockfd, buf, MAXLINE)) > 0) Writen(sockfd, buf, n); if (n < 0 && errno == EINTR) goto again; else if (n < 0) err_sys("str_echo: read error"); } void sig_chld(int signo) { pid_t pid; int stat; while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0) printf("child %d terminated\n", pid); return; } int main(int argc, char **argv) { int listenfd, connfd; pid_t childpid; socklen_t clilen; struct sockaddr_in cliaddr, servaddr; listenfd = Socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT); Bind(listenfd, (SA *)&servaddr, sizeof(servaddr)); Listen(listenfd, LISTENQ); Signal(SIGCHLD, sig_chld); /* must call waitpid */ for (;;) { clilen = sizeof(cliaddr); if ( (connfd = accept(listenfd, (SA *)&cliaddr, &clilen)) < 0) { if(errno = EINTR) continue; /* back to for */ else err_sys("accept error"); } if ( (childpid = Fork()) == 0) { Close(listenfd); str_echo(connfd); exit(0); } Close(connfd); } }
服务器程序已经正确,但是客户程序还有些问题。
考虑这样一种情形:当客户与服务器建立TCP连接并成功执行一次回射操作后,使用kill命令杀死子进程。子进程的终止会导致向客户发送一个FIN,但是,客户TCP接收到FIN只是表示服务器进程已关闭了连接的服务器端,从而不再往其中发送任何数据而已。FIN的接收并没有告知客户TCP服务器进程已经终止。我们需要修改代码( 使用select或poll函数),使得服务器进程的终止一旦发生,客户就能检测到。
另外值得一提的是,在本例中,客户实际上在应对两个描述符——套接字和用户输入。它不能单纯阻塞在这两个源中某个特定源的输入上,而是应该阻塞在其中任何一个源的输入上。
下面的代码可以修正上述问题。
void str_cli(FILE *fp, int sockfd) { int maxfdp1, stdineof; fd_set rset; char buf[MAXLINE]; int n; stdineof = 0; FD_ZERO(&rset); for (;;) { if(stdineof == 0) FD_SET(fileno(fp), &rset); FD_SET(sockfd, &rset); maxfdp1 = max(fileno(fp), sockfd) + 1; Select(maxfdp1, &rset, NULL, NULL, NULL); if (FD_ISSET(sockfd, &rset)) /* socket is readable */ { if( (n = Read(sockfd, buf, MAXLINE)) == 0) { if(stdineof == 1) return; else err_quit("str_cli: server terminated prematurely"); } Write(fileno(stdout), buf, n); } if (FD_ISSET(fileno(fp), &rset)) /* input is readable */ { if ( (n = Read(fileno(fp), buf, MAXLINE)) == 0) { stdineof = 1; Shutdown(sockfd, SHUT_WR); FD_CLR(fileno(fp), &rset); continue; } Write(sockfd, buf, n); } } }
标签:
原文地址:http://www.cnblogs.com/gattaca/p/4587175.html