TCP状态测试(CS模型改进3)
来源:互联网 发布:淘宝直通车效果怎么样 编辑:程序博客网 时间:2024/06/05 02:50
本文测试TCP的11种状态,更深入理解CS模型。
尽管我们TCP程序很小,然而对于我们弄清客户和服务器如何启动,如何终止,更为重要的是当发生某些错误(例如客户主机崩溃,客户进程崩溃,网络连接断开,等等)时将会发生什么,本例十分重要。只有弄清这些边界条件以及TCP/IP协议的相互作用,我们才能写出能够处理这些情况的健壮的客户和服务器程序。
(1) 启动服务器
服务器启动后,它调用socket,bind,listen和accept,并阻塞于accept调用。(我们还没有启动客户端)
我们检查服务器监听套接字的状态。
处于LISTEN状态
(2)启动客户端
客户调用socket和connect,后者引起TCP的三次握手过程。当三次握手完成后,客户connect和服务器accept均返回,连接建立。接下来发生的步骤:
(1)客户调用str_cli函数,该函数将阻塞fgets调用,因为我们还未曾键入过一行文本。
(2)当服务器中accept返回时,服务器调用fork,再由子进程调用str_echo。该函数调用readline,readline调用read,而read在等待客户送入一行文本期间阻塞。
(3)另一方面,服务器父进程再次调用accept并阻塞,等待下一个客户连接。
目前,我们有三个进程都在休眠:客户进程,服务器父进程,服务器子进程。
此时处于连接状态。
(3)正常终止
此时连接建立,不论我们在客户端的便准输入中输入什么,都会回射到它的标准输出中。
接着输入ctrl+d
客户端处于TIME_WAIT状态。服务器端LISTEN
我们可以总结出正常终止客户和服务器的步骤:
(1)当我们键入EOF字符(ctrl+d),fgets返回一个空指针,于是srt_cli函数返回。
(2)当str_cli函数返回到客户的main函数,main调用exit终止。
(3)进程终止处理的部分工作是关闭所有打开的描述符,因此客户打开的套接字由内核关闭。这导致客户TCP发送一个FIN给服务器,服务器TCP则以ACK相应,这就是TCP连接终止序列的前半部分。至此,服务器套接字处于CLOSE_WAIT状态,客户套接字处于FIN_WAIT_2状态。
(4)当服务器TCP接收FIN时,服务器子进程阻塞于readline调用,返回0,导致str_echo函数返回服务器子进程的main函数。
(5)服务器子进程通过调用exit来终止。
(6)服务器子进程中打开的所有描述符随之关闭。由子进程来关闭已连接套接字会引发TCP连接终止序列的最后两个分节:一个服务器到客户的FIN和一个客户到服务器的ACK。之此,连接完全终止,客户套接字进入TIME_WAIT状态。
(7)进程终止处理的另一部分内容:在服务器子进程终止时,给父进程发送SIGCHLD信号。但是我们并没有捕获该信号,而该信号的默认行为是被忽略。既然父进程未加处理,子进程于是进入僵死状态。
在下一篇博客讲解信号处理。
本篇代码:
服务器端
#include<unistd.h>#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>#include<time.h>#include<stdio.h>#include<stdlib.h>#include<errno.h>#include<string.h>#define ERR_EXIT(m) \ do \ { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0)ssize_t writen(int fd,void *buf,size_t count){ size_t nleft=count;//剩余发送字节数 ssize_t nwritten;//已经发送字节数 char *bufp=(char *)buf; while(nleft>0)//一般而言,write缓冲区大于发送数据缓冲区,不阻塞 { if((nwritten=write(fd,bufp,nleft))<0) { if(errno==EINTR)//被中断 continue; return -1; } else if(nwritten==0)//对等方关闭 continue;//读到EOF,对方关闭 bufp+=nwritten; nleft-=nwritten; } return count;}void str_echo(int sockfd){ char recvbuf[1024]; while(1){ memset(recvbuf,0,sizeof(recvbuf));//初始化recvbuf int ret=read(sockfd,recvbuf,1024);//一行一行接收数据。 if(ret==-1) { ERR_EXIT("readine"); } if(ret==0) { printf("client close\n"); break; } fputs(recvbuf,stdout);//读取一行数据,就将其输出到标准输出 writen(sockfd,recvbuf,strlen(recvbuf));//buf中数据被复制到了TCP发送缓冲区 }}int main(){ int listenfd,connfd; /*if((listenfd=socket(AF_INET,SOCK_STEAM,IPPOTO_TCP))<0) */ if((listenfd=socket(AF_INET,SOCK_STREAM,0))<0) ERR_EXIT("socket"); //IPV4地址结构 struct sockaddr_in servaddr,cliaddr; memset(&servaddr,0,sizeof(servaddr)); servaddr.sin_family=AF_INET;//地址家族 servaddr.sin_port=htons(5188);//端口,主机转网络 /*servaddr.sin_addr.s_addr=inet_addr("127.0.0.1");*/ inet_pton(AF_INET,"127.0.0.1",&servaddr.sin_addr); if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0) ERR_EXIT("bind"); if(listen(listenfd,SOMAXCONN)<0) ERR_EXIT("listen"); pid_t pid; socklen_t clilen =sizeof(cliaddr); while(1){ if((connfd=accept(listenfd,(struct sockaddr*)&cliaddr,&clilen))<0) ERR_EXIT("accept"); pid=fork(); if(pid==-1) ERR_EXIT("fork"); if(pid==0) { close(listenfd); str_echo(connfd); exit(EXIT_SUCCESS);//将子进程退出,要不它会fork() } else close(connfd); } return 0;}
客户端
#include<unistd.h>#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>#include<stdio.h>#include<stdlib.h>#include<errno.h>#include<string.h>#define ERR_EXIT(m) \ do \ { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0)ssize_t writen(int fd,void *buf,size_t count){ size_t nleft=count;//剩余发送字节数 ssize_t nwritten;//已经发送字节数 char *bufp=(char *)buf; while(nleft>0)//一般而言,write缓冲区大于发送数据缓冲区,不阻塞 { if((nwritten=write(fd,bufp,nleft))<0) { if(errno==EINTR)//被中断 continue; return -1; } else if(nwritten==0)//对等方关闭 continue;//读到EOF,对方关闭 bufp+=nwritten; nleft-=nwritten; } return count;}ssize_t readline(int sockfd,void *buf,size_t maxline){ ssize_t n,rc; char c,*ptr; ptr=buf; for(n=1;n<maxline;n++){ again: if((rc=read(sockfd,&c,1))==1){ *ptr++=c; if(c=='\n') break; }else if(rc==0){ *ptr=0; return(n-1); }else{ if(errno==EINTR) goto again; return -1; } } *ptr=0; return n;}void str_cli(FILE *fp,int sockfd){ char sendline[1024]={0}; char recvline[1024]={0}; while(fgets(sendline,sizeof(sendline),fp)!=NULL){ writen(sockfd,&sendline,strlen(sendline)); if(readline(sockfd,recvline,sizeof(recvline))==0) ERR_EXIT("readline"); fputs(recvline,stdout); memset(sendline,0,sizeof(sendline)); memset(recvline,0,sizeof(recvline)); }}int main(){ int sockfd; /*if((listenfd=socket(AF_INET,SOCK_STEAM,IPPOTO_TCP))<0) */ if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0) ERR_EXIT("socket"); //IPV4地址结构 struct sockaddr_in servaddr; memset(&servaddr,0,sizeof(servaddr)); servaddr.sin_family=AF_INET;//地址家族 servaddr.sin_port=htons(5188);//端口,主机转网络 servaddr.sin_addr.s_addr=inet_addr("127.0.0.1"); /*inet_aton("127.0.0.1",&servaddr.sin_addr);*/ if(connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0) ERR_EXIT("connect"); str_cli(stdin,sockfd); exit(0); return 0;}
- TCP状态测试(CS模型改进3)
- 处理SIGCHLD信号(CS模型改进4)
- socket编程之并发服务器(CS模型改进2)
- 网络编程中最简单的TCP通信(CS模型)
- Splash.cs的改进
- CS/CSS架构应用的软件性能测试模型分析
- CS/CSS架构应用的软件性能测试模型分析
- CS/CSS架构应用的软件性能测试模型分析
- CS/CSS架构应用的软件性能测试模型分析
- FTP异步模型测试网络状态
- tcp通讯CS端
- Socket TCP CS
- linux下CS模型
- bs模型与cs模型
- 测试协同CS模式下的客户端操作通知时间 采用协议TCP
- 测试流程改进工具 (TPI)
- 测试改进四策(一)
- 4.1.3 TCP/IP模型
- PAT_1070. Mooncake
- 中兴新支点操作系统小教程——用户
- 啊啊
- Java当中的四种引用
- MyEclipse的字体调整
- TCP状态测试(CS模型改进3)
- 修改mysql数据库root密码
- 微信支付服务商APP支付申请H5支付申请
- 使用atof需小心
- Android ViewPager实现轻量级Banner
- 区块链基本加密概念
- python
- rsync参数详解
- Android开发规范