一、概述
想要写一个完整的TCP客户-服务器程序例子,有下面功能的回射服务器
1.客户从标准输入读一行文本,写到服务器上;
2.服务器从网络输入读此行,并回射给客户;
3.客户读此回射行并写到标准输出。
此例子需要观察:正常运行时什么情况,客户和服务器都启动时什么情况,客户正常终止时什么情况,
如果服务器在客户之前终止客户什么情况,如果服务器主机崩溃则客户什么情况。
二、TCP回射服务器程序:main函数
1 #include "unp.h" 2 3 int 4 main(int argc, char *argv[]) 5 { 6 int listenfd, connfd; 7 pid_t childpid; 8 socklen_t clilen; 9 struct sockaddr_in cliaddr, servaddr; 10 11 listenfd = Socket(AF_INET, SOCK_STREAM, 0); 12 13 bzero(&servaddr, sizeof(servaddr)); 14 servaddr.sin_family = AF_INET; 15 servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 16 servaddr.sin_port = htons(SERV_PORT); 17 Bind(listenfd, (SA *)&servaddr, sizeof(servaddr)); 18 19 Listen(listenfd, LISTENQ); 20 for(;;) { 21 clilen = sizeof(cliaddr); 22 connfd = Accept(listenfd, (SA *)&cliaddr, &clilen); 23 if((childpid = Fork()) == 0) { /* child process */ 24 Close(listenfd); /* close listening socket */ 25 str_echo(connfd); /* process the request */ 26 exit(0); 27 } 28 Close(connfd); /* parent closes connected socket */ 29 } 30 }
#include "unp.h" void str_echo(int sockfd) { ssize_t n; char line[MAXLINE]; for(;;) { if((n = Readline(sockfd, line, MAXLINE)) == 0) return; /* connection closed by other end */ Writen(sockfd, line, n); } }
三、TCP回射客户程序:main函数
#include "unp.h" int main(int argc, char *argv[]) { int sockfd; struct sockaddr_in servaddr; if(argc !=2) err_quit("usage:tcpcli <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); }
str_cli:
#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); } }
四、正常终止
正常终止客户和服务器的步骤:
1.当我们键入EOF字符时,fgets返回一个空指针,于是str_cli返回。
2.当str_cli返回到客户的函数main时,main通过调用exit终止。
3.进程终止处理的一部分是关闭所有打开的描述字,所以客户套接口由内核关闭。
TCP连接终止的前半部分:客户TCP发送一个FIN给服务器,服务器TCP则ACK响应。
此时,服务器套接口处于CLOSE_WAIT状态,客户套接口处于FIN_WAIT_2状态。
4.当服务器TCP接收FIN时,服务器子进程阻塞于readline调用,于是readline返回0,这导致函数str_echo返回服务器子进程的main
5.服务器子进程通过调用exit来终止。
6.服务器子进程中打开的所有描述字随之关闭。由子进程来关闭已连接套接口引发TCP连接终止序列的最后两个分节:一个从服务器到客户的FIN和一个从客户到服务器的ACK。至此,连接完全终止,客户套接口进入TIME_WAIT状态。
7.进程终止处理的另一部分是在服务器子进程终止时给父进程发一个信号SIGCHLD。