互联网络程序设计第3章.ppt
第三章、迭代服务器与客户端,TCP套接字编程基本步骤套接字编程基本函数迭代echo服务器迭代echo客户端测试用例,1.TCP套接字编程基本步骤,TCP套接字基本步骤分为服务器端和客户端两部分:服务器端创建套接字绑定套接字设置套接字为监听模式,进入被动接受连接状态接受请求,建立连接读写数据终止连接客户端步骤创建套接字与远程服务器建立连接读/写数据终止连接,TCP套接字编程(cont.),socket(),bind(),listen(),accept(),read(),write(),close(),socket(),connect(),write(),read(),close(),阻塞直到接收到客户连接请求,TCP服务器端,TCP客户端,2.套接字编程基本函数,bind函数#include int bind(int sockfd,struct sockaddr*addr,socklen_len len)返回:0成功;-1出错该函数用于给传输层的socket分配地址,其深层含义是在分用Packet的时候,协议栈通过检查inpcb中的地址,确定packet应该传送给哪个socket一般而言,服务器调用此函数,而客户则很少调用它。绑定地址时,可以指定地址和端口号,也可以指定其中之一,甚至一个也不指定。通配地址:INADDR_ANY,bind函数(续),另外,需要注意以下几点:参数addr中的相关字段在初始化时,必须是网络字节序;如果由内核来选择IP地址和临时端口号,函数并不返回所选择的值。为了获得这些值,进程必须调用getsockname函数函数bind返回的一个常见错误是:EADDRINUSE,我们可以通过设置套接口选项SO_REUSEADDR。,bind函数的用法,struct sockaddr_in addr;int port=1234;addr.sin_family=AF_INET;=htonl(INADDR_ANY);addr.sin_port=htons(port);if(bind(fd,(struct sockaddr*)&addr,sizeof(addr)=-1)/*错误处理*/,基本套接字函数listen,#include int listen(int sockfd,int backlog)返回:0成功;-1出错;函数listen仅被服务器调用,它完成两件事情:函数listen将未连接的套接字转化成被动套接字,指示内核应接受指向此套接字的连接请求;函数的第二个参数规定了内核为此套接字排队的最大连接个数;对于给定的监听套接字,内核要维护两个队列未完成连接队列已完成连接队列两个队列之和不超过backlog;,listen函数(续),三路握手完成,两队列之和不能超过backlog,已完成连接队列(ESTABLISHED状态),未完成连接队列(SYN_RCVD状态),新到达的SYN分节,服务器,accept,TCP为监听套接口维护的两个队列,Client,Server,SocketConnect(blocks)(active open),Socket,bind,listen(passive open),SYN J,SYN K,ack J+1,ack K+1,Accept returnRead(blocks),Connect return,连接完成,进入已建连队列,RTT,listen函数(续),另外几点说明:不同的实现对backlog有不同的解释,如源自Berkeley的实现将backlog增加一个模糊因子,把它乘以1.5,再作为两个队列之和;不要把backlog定义为0,因为有些实现允许1个连接排队,而有些实现不允许连接排队;当一个客户SYN到达时,若两个队列都是满的,tcp就忽略此分节,且不发送RST。这是因为,这种情况是暂时的,客户tcp将重发SYN,期望不久的将来就能在队列中找到空闲条目。如果发送RST,将会出现?,基本套接字函数accept,#include int accept(int sockfd,struct sockaddr*cliaddr,socklen_t*addrlen);返回:非负描述字OK;-1出错;accept函数由TCP服务器调用;从已完成连接队列头返回下一个已完成连接;如果该队列空,则进程进入睡眠状态。函数返回的套接字为连接套接字,应与监听套接字区分开来该函数最多返回三个值:一个既可能是新套接字也可能是错误指示的整数,一个客户进程的协议地址(由cliaddr所指),以及该地址的大小(这后两个参数是值结果参数);也就是说,服务器可以通过参数cliaddr来得到请求连接并获得成功的客户的地址和端口号;,accept函数示例,struct sockaddr_in servaddr,cliaddr;socklen_tlen;int listenfd,connfd;connfd=accept(listenfd,(struct sockaddr*),基本套接字函数connect,#include int connect(int sockfd,const struct sockaddr*addr,socklen_t addrlen);返回:0成功;-1出错;函数connect激发TCP的三路握手过程;仅在成功或出错返回;错误有以下几种情况:如果客户没有收到SYN分节的响应(总共75秒,这之间需要可能需要重发若干次SYN),则返回ETIMEDOUT。如果对客户的SYN的响应是RST,则表明该服务器主机在该端口上没有进程在等待。函数返回错误ECONNREFUSED;如果客户发出的SYN在中间路由器上引发一个目的地不可达ICMP错误,则如第一种情况,连续发送SYN,直到规定时间,返回EHOSTUNREACH或ENETUNREACH。,connect函数(续),客户在调用connect前不必非得调用bind函数,此时,内核会选择一个合适的IP地址和临时端口号;如果函数connect失败,则套接字不可再用,必须关闭。不能再对此套接字再调用函数connect。注意这一点!,3.echo服务器,本课程全程采用echo程序作为讲解的例子采用这个例子的原因操作简单,关注于网络编程本身便于比较各种网络编程技术的性能,Hello,world!,Hello,world!,Hello,world!,Hello,world!,echo服务器,int main(int argc,char*argv)int fd1,fd2;socklen_t length;struct sockaddr_in addr1,addr2;char bufBUFSIZE;/create a socket/if(fd1=socket(AF_INET,SOCK_STREAM,0)=-1)cout strerror(errno)endl;return-1;,/bind the socket/bzero(,/listen the socket/if(listen(fd1,BACKLOG)0)cout strerror(errno)endl;return-1;/serve the client/while(true)/accept the requirement of some client/,length=sizeof(addr2);bzero(,/close the listening socket/close(fd1);return 0;,4.echo客户端程序,int main(int argc,char*argv)int ret,fd;struct sockaddr_in addr;char bufBUFFERSIZE;/get the servers addr/if(argc!=2)printf(Usage:iterateClient addrn);return-1;,bzero(,/connect to the server/RETRY:if(connect(fd,(struct sockaddr*),/get the serve/return tcpclient(fd,buf,BUFFERSIZE);tcpclient()函数中使用read、write函数向标准输入、标准输出读写数据,为什么没有用fgets?,5.测试用例,1.基本功能打开服务器netstat antp打开客户端netstat antpps a输入字符串Ctrl-Dnetstat-antp,2.观察报文sudo tcpdump tcp port 1234 Xu i lo分析TCP报文结构,3.多客户端测试首先以一个客户端连接,获取服务;然后再打开一个终端,连接服务器,观察现象4.BackLog测试教材(第3版)中指出的BackLog的实现为0时,队列长度为3,修改代码中的Backlog值,测试Linux 2.6中的情况BackLog=1?,5.压力测试修改代码,加入时间测量函数./iterateClient 127.0.0.1 ubuntu.bak检查传输速率,并记录下来,作业,采用迭代的方式实现echo客户端与服务器,要求进行如下实验:验证程序能够正常工作检查多用户的情况Backlog=0和1时,Ubuntu的Tcp接收队列有多长采用一个大文件(12GB),对echo程序做压力测试思考题:安装两台Linux主机,分别运行echo客户端与服务器,模拟如下边界条件:当网线断掉的情况当echo服务器被杀死,采用C+对Tcp的套接字进行封装,