版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第6章 TCP/IP网络编程6.1 TCP/IP协议概述6.3 TCP编程6.4 UDP编程6.5 组播编程6.6 WinSock I/O模型小结基于TCP/IP协议的网络程序是当前网络通信的主要方式,TCP/IP协议目前也处于鼎盛时期。随着TCP/IP由IPv4向IPv6过渡,可以预见,这种通信协议还会使用很长时间。因此,基于TCP/IP协议的编程方法也是本书所介绍的重点内容之一。本章首先简要地介绍TCP/IP协议的基本内容,然后介绍TCP/IP在Windows操作系统下网络编程的重要编程接口WinSock,基于WinSock进行TCP/IP传输层两种通信方式(即TCP和UDP)的编程,接着
2、介绍基于WinSock的TCP/IP组播编程。为了使网络编程更加具有灵活性,本章介绍了三种I/O控制方法。6.1.1 基本概念TCP/IP(Transmission Control Protocol/Internet Protocol,传输控制协议/网际协议)是一系列协议,或者说是一个协议族,它定义了数据传输如何通过因特网进行交换。TCP/IP起源于20世纪60年代末美国政府资助的一个分组交换网络研究项目,到20世纪90年代已发展成为计算机之间最常用的组网协议。6.1 TCP/IP协议概述TCP/IP允许分布在各地安装着完全不同系统的计算机互相通信,是一个真正的开放系统。TCP/IP是根据它最
3、主要的两个协议命名的,已经实际应用了许多年,并在世界范围内证明了它的有效性。1协议栈结构TCP/IP模型进一步提炼与合并了OSI模型,它取消了OSI模型中的表示层和会话层,并合并了数据链路层和物理层(由于主要与连接有关并依赖于阐述介质,因此TCP/IP参考模型实际上对物理层并没有定义),使得逻辑更加简洁明晰,在此基础上逐步实现了各种子协议。TCP/IP模型与OSI模型的比较如图6-1所示。TCP/IP模型每一层所负责的功能如下:链路层:有时被称做数据链路层或网络接口层,通常包括操作系统中的设备驱动程序和计算机中对应的网络接口卡,它们一起处理与电缆(或其他任何传输媒介)的物理接口细节。网络层:有
4、时也被称为互连网层,负责分组在网络中的活动,包括IP(网际协议)、ICMP(Internet互联网控制报文协议)以及IGMP(Internet组管理协议)。这一层TCP/IP有两个基本组件:一个是IP协议,另一个是路由协议。图6-1 TCP/IP模型与OSI模型的比较传输层:该层主要为两台主机上的应用程序提供端到端的数据通信,它分为两个不同的协议,即TCP(传输控制协议)和UDP(用户数据报协议)。TCP提供端到端的保证质量的数据传输,该层负责数据的分组、质量控制和超时重发等,对于应用层来说,就可以忽略这些工作。UDP则只简单地把数据报从一端发送到另一端,至于数据是否到达或按时到达、数据是否损
5、坏,这都必须由应用层来做。这两种协议各有用途,前者可用于面向连接的应用,而后者则在及时性服务中有着重要的用途,如网络多媒体通信等。应用层:该层负责处理实际的应用程序细节,包括Telnet、HTTP、SMTP、FTP、DNS和SNMP等协议和应用。层与层之间的联系与逻辑分离是利用封装与分用过程分别实现的。2协议封装当应用程序传送数据时,数据按自上而下的方向被送入协议栈中,然后逐个通过每一层直到被当做一串比特流送入网络。其中,每一层对收到的上一层数据都要增加一些首部信息(有时还要增加尾部信息),通过层层包裹完成数据的封装过程,使之适合网络传输,这相当于完成了1.1.2节提及的数字通信中的信道编码。
6、以应用程序通过TCP协议传输数据为例,该过程如图6-2所示。图6-2 应用程序通过TCP协议传输数据封装过程用户数据首先被添加应用首部,传给TCP层;在TCP层,数据被再安装TCP首部后,传给IP层(TCP传给IP的数据单元称作TCP报文段或简称为TCP段);在IP层,数据被再次安装IP首部后,传给链路层(IP传给网络接口层的数据单元称作IP数据报);在链路层,数据再被安装以太网首部,并添加以太网尾部,形成适合网络(光、电信号)传输的数据比特流,该比特流称做帧(Frame)。读者可能对首部和尾部的概念还不太了解。实际上,首部和尾部是一种协议指定的数据结构,按照一定的顺序规则填写数据。关于各种首
7、部中的字段含义、用途将在后面章节进行详细介绍。3协议分用网络接口分别发送和接收IP、ARP和RARP数据。IP层接口负责发送ICMP、IGMP、TCP、UDP数据包,因此必须在以太网的帧首部、IP特定字段中加入某种形式的标识,以指明生成数据的网络协议。例如:以太网的帧首部有一个16bit的帧类型域,当目的主机收到一个以太网数据帧时,数据就开始从协议栈中由底向上升,同时去掉各层协议上加的报文首部。每层协议都要检查报文协议标识,以确定接收数据的上层协议。这个过程称做分用(Demultiplexing),它解决了数据包接收时协议解析的问题,从而保证了各种不同的TCP/IP子协议能够被组合成一个整体(
8、图6-3显示了该过程)。图6-3 TCP/IP协议分用过程6.1.2 常用协议如前所述,TCP/IP协议是一个协议族,下面对常用的协议分别进行介绍。1MAC协议MAC(Media Access Control,媒体访问控制)协议最重要的功能是确定谁占有信道,即信道分配问题,其主要作用是保证信道的公平性和有效的资源共享。MAC的机制分为两类,即基于竞争的信道协议和无竞争的信道协议。基于竞争的信道协议是假定网络中没有中心实体来分配信道资源,每个节点必须通过竞争媒体资源来进行传送,当超过一个节点同时尝试发送时,碰撞就会发生(见7.1.1节所介绍的CSMA/CD协议)。相反,无竞争的信道协议为每个需要
9、通信的节点分配专用的信道资源。无竞争的信道协议能够有效地减少冲突,其代价是突发数据业务的信道利用率可能会比较低。不同的传输介质决定了所使用的MAC标准,如:以太网遵循IEEE 802.3标准,令牌总线遵循IEEE 802.4标准,令牌环网遵循IEEE 802.5标准,等等。本书重点关心的IEEE 802.3标准定义了一种具有七个字段的MAC帧,包括:前导符P、帧起始分界符SFD、目的地址DA、源地址SA、表示数据字段字节数长度的字段LEN、要发送的数据字段、填充字段PAD和帧校验序列FCS等8个字段,这8个字段中除了数据字段和填充字段外,其余的长度都是固定的。图6-4就是以太网帧的结构,其首部
10、由5个字段组成,包括前导符、起始帧分界符、目标地址、源地址、长度/类型构成;尾部由一个字段构成(CRC);上层协议的首部及数据夹在首部与尾部之间的数据字段里。图6-4 以太网帧结构这里使用的地址无论是DA还是SA,都是硬件地址,或称为MAC地址。MAC地址由网卡的生产厂商唯一设定给每一块不同的网卡。一块网卡依据数据帧的包头信息中是否写有它的MAC地址来决定是否接受并上传该帧。查阅本机MAC地址的方法很多,如Windows的ipcongfig命令、NetBIOS的Astatus命令等。需要注意的是,IEEE 802.3标准的MAC帧不提供任何对收到的帧进行确认的机制,其通信确认在高层完成,这表明
11、它是一种不可靠的介质。以太网MAC协议承载了其他TCP/IP上层子协议。2IP协议IP协议负责在TCP/IP主机之间提供数据报服务,进行数据封装,产生协议头。由于在以太网中帧的大小受限制,并且不同的帧可能由不同的网络路径传送,因此IP协议需要将较大的数据报文分割,并在目的主机处按正确顺序组合。另外,IP协议不负责包的校验,它是一种无连接、不可靠的传输。如果发生任何错误,IP协议则丢弃该数据报,然后发送ICMP消息报给信源端。数据报的检测校验是由上层协议如TCP等提供的。无连接数据报并不维护任何关于后续数据报的状态,每个数据报的处理是相互独立的,即IP数据报可以不按发送顺序接收。IP协议还需要负
12、责寻找路由,因此它需要配一个确定的IP地址。在IP报文的包头中包含了源与目的的IP地址。一般来说,不会有应用程序直接访问IP协议。IP数据报是Internet上数据通信的基本单元,这些数据报不超过1000字节长,当人们打开Web页、下载文件或者发送E-mail时,这些数据报就在世界各地来回传输。IP协议包裹的协议有:ARP、RAPRP、ICMP、IGMP、路由协议。网络互联的目的是提供一个无缝的通信系统,为此,互联网协议必须屏蔽物理网络的具体细节,并提供一个虚拟网络的功能,使设计者可以在不考虑物理硬件细节的情况下自由地选择地址。在TCP/IP栈中,编址由IP协议规定,IP标准分配给每台主机一个
13、32位的二进制数作为该主机的IP地址。在2019年6月即将正式投入运行的IPv6中,IP地址升至128位,这样IP资源变得更加丰富。IP协议将每个IP地址分割成前缀和后缀两部分。前缀用于确定计算机从属的物理网络,后缀则用于确定网络上一台单独的计算机。互联网中的每一个物理网络都有一个唯一的值作为网络号(Network Number)。IP地址的层次性设计保证了以下两个重要性质:每台计算机分配一个唯一的地址;网络号分配全球统一,但后缀可本地分配,无需全球统一。IP地址共分五类:A类、B类、C类、D类和E类。其中,A类、B类和C类为基本类;D类用于多播传送;E类属于保留类,现在不用。这种地址分配方法
14、的优点是,通过判断从左到右第一个0出现的位置就可以区分地址类型。它们的格式如表6-1所示(其中,*代表网络号位数,X代表主机号位数)。表6-1 IP地址分类IP地址一般采用点分十进制的方法表示,例如10000001 00110100 00000110 00000000。此外,需要特别注意以下几个特殊的IP地址:(1) 网络地址:IP中主机地址为0,表示网络地址,如。(2) 广播地址:网络号后跟一个所有位全是1的后缀,即直接广播地址。(3) 回送地址:用于测试。(4) 内网地址:B类地址中的55、55,C类地址中的55等三个地址段内的IP习惯上经常作为内部网络地址使用。除了给每个主机分配一个IP
15、地址外,IP协议也规定给每个路由器分配IP地址。事实上,每个路由器会被分配了两个或更多个IP地址。一个路由器连接到多个物理网络,每一个IP地址包含一个特定物理网络的网络号。这个IP地址并不标识一台特定的计算机,而是标识一台计算机和一个网络间的一个连接。现在所有的主机都要求支持子网编址(RFC950,J.Mogul and J.Postel,1985),该功能要求,不仅要把IP地址看成由单纯的一个网络号和一个主机号组成,还要把主机号再分成一个子网号和主机号。这样做是因为A类和B类地址为主机号分配了太多空间,但事实上在一个网络中并不会有这么多主机,因此在NIC(Network Informatio
16、n Center)获得某个IP网络号后,就由系统管理员来决定是否建立子网,以及分配多少位给子网号和主机号。例如,这里有一个B类地址(),在剩下的16位中,8位用于子网号,8位用于主机号,其格式如图6-5所示。这样就允许有254个子网,每个子网可有254台主机。图6-5 B类地址的子网编址举例除了地址类型以外,主机还需要知道地址中分别有多少位用于子网号与主机号。这是在引导过程中由子网掩码所确定的。这个掩码是一个32位的值,其中值为1的位留给网络号和子网号,为0的位留给主机号。在上面的例子中,子网掩码就是。通常规定,具有相同网络号的主机属于网内关系,不同网络号的主机属于网间关系。IP协议包裹或承载
17、了ARP/RARP、ICMP、IGMP、OSPF等TCP/IP子协议。3ARP/RARP协议ARP(Address Resolution Protocol,地址解析协议)和RARP(Reverse Address Resolution Protocol,逆向地址解析协议)是某些网络接口(如以太网和令牌环网)使用的特殊协议,用来转换IP层和MAC层使用的地址。由于IP地址只对TCP/IP有效,MAC地址只对网络访问层有意义,因此分配给主机使用的IP地址和它固有的MAC地址是互不相干的。在物理网络上的数据帧交换依赖于MAC地址,而在网络层层面的IP地址赋予用户设定逻辑地址的权利,要使二者配合工作必
18、须进行正确的转换。ARP实现了从IP地址到MAC地址的映射,而RARP负责根据NIC硬件地址去查询对应的IP地址。ARP要求网络接口有一个硬件地址。在硬件上进行的数据帧交换必须要有正确的接口地址。TCP/IP的地址是32位的IP地址。仅知道主机的IP地址并不能让内核(如以太网驱动程序)发送数据帧给主机,内核必须知道目的端的硬件地址才能发送数据。假设在一个以太网中,客户端要将一个IP报文发送到服务器端,那么客户端就必须把32位的IP地址转换成48位的以太网地址。ARP获取IP的过程可分为以下三个步骤:(1) ARP以广播的方式发送ARP Request数据帧给以太网的每个主机。ARP请求数据帧中
19、包含目的主机的IP地址,意思是“如果你是这个IP地址的拥有者,请回答你的硬件地址”。(2) 目的主机的ARP层收到这份广播报文后,识别出这是发送端在询问它的IP地址,于是发送一个ARP应答。这个ARP应答包含IP地址及对应的硬件地址。(3) 发送端收到ARP应答后,主机间通过使用ARP协议获得的硬件地址进行通信。ARP中规定了两种信息的基本类型:请求(Request)和应答(Response)。在以太网上解析IP地址时,ARP请求和应答分组的格式如附录4中的附表4-6所示(ARP亦可用于解析其他类型网络的IP地址以外的地址,紧跟着帧类型字段的前四个字段决定了最后四个字段的类型和长度)。4ICM
20、P协议ICMP(Internet Control Message Protocol)是Internet控制报文协议。它是TCP/IP协议族的一个子协议,用于在IP主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着至关重要的作用。ICMP协议是一种面向非连接的协议,用于传输出错报告控制信息。它是一个非常重要的协议,对网络安全具有极其重要的意义。它是TCP/IP协议族的一个子协议,属于网络层协议,主要用于在主机与路由器之间传递控制信息,包括报告错误、交换受限控制和状态信息等。当遇到IP数据无
21、法访问目标、IP路由器无法按当前的传输速度转发数据包等情况时,IP路由器会自动发送ICMP消息。ICMP提供一致易懂的出错报告信息。发送的出错报文返回到发送原数据的设备,因为只有发送设备才是出错报文的逻辑接受者。发送设备随后可根据ICMP报文确定发生错误的类型,并确定如何才能更好地重发失败的数据报。但是ICMP唯一的功能是报告问题而不是纠正错误,纠正错误的任务由发送方完成。5IGMP协议Internet 组管理协议(IGMP)是因特网协议家族中的一个组播协议,用于 IP 主机向任一个直接相邻的路由器报告它们的组成员情况。IGMP信息封装在IP报文中,其IP的协议号为2,用来在IP主机和与其直接
22、相邻的组播路由器之间建立、维护组播组成员关系。IGMP不包括组播路由器之间的组成员关系信息的传播与维护,这部分工作由各组播路由协议完成。所有参与组播的主机必须实现IGMP。参与IP组播的主机可以在任意位置、任意时间、成员总数不受限制地加入或退出组播组。组播路由器不需要也不可能保存所有主机的成员关系,它只是通过IGMP协议了解每个接口连接的网段上是否存在某个组播组的接收者,即组成员。而主机方只需要保存自己加入了哪些组播组。IGMP在主机与路由器之间是不对称的:主机需要响应组播路由器的IGMP查询报文,即以IGMP membership report报文响应;路由器周期性发送成员资格查询报文,然后
23、根据收到的响应报文确定某个特定组在自己所在子网上是否有主机加入,并且当收到主机的退出组的报告时,发出特定组的查询报文(IGMP版本2),以确定某个特定组是否已无成员存在。6路由协议当数据跨网传输时需要进行路由,通常路由包括两个基本的动作:确定最佳路径和让信息群(或称为分组)通过网络传输。通过网络传输分组相对较简单,而路径的确定却相对复杂,一般采用的方法就是查询路由表。路由表保存了通过路由器可能到达的目标网络以及如何到达该目标网络的信息,基本项包括目标网络地址、子网掩码以及到达目标网络的下一站路由器的地址,简介记录了网络间的位置关系。路由表由路由协议维护和更新,此外,路由协议还完成发送路由更新信
24、息且基于路由算法决定路由的功能,常见的路由协议有RIP、OSPF、BGP等。RIP协议使用V-D算法在局域网上实现,它将参加者分为主动机和被动机两种。主动机主动地向外广播路径刷新报文,被动机被动地接受路径刷新报文。一般情况下,网关作主动机,主机作被动机。RIP规定一条路径的距离为该路径(从信源机到信宿机)上的网关数。为防止寻径回路的长期存在,RIP规定,长度为16的路径为无限长路径,即不存在路径。所以一条有限的路径长度不得超过15。正是这一规定限制了RIP的使用范围,使RIP局限于小型的局域网中。OSPF(Open Shortest Path First)是一个内部网关协议(Interior
25、Gateway Protocol,IGP),用于在单一自治系统(Autonomous System,AS)内决策路由,一个自治系统的经典定义是在一个管理机构控制之下的一组路由器。与RIP相对,OSPF是链路状态路由协议,而RIP是距离向量路由协议。链路是路由器接口的另一种说法,因此OSPF也称为接口状态路由协议。OSPF通过路由器之间通告网络接口的状态来建立链路状态数据库,生成最短路径树,每个OSPF路由器使用这些最短路径构造路由表。BGP(Border Gateway Protocol)是一种在自治系统之间动态交换路由信息的路由协议,它使用IGP(内部网关协议)和普通度量值向其他自治系统转发
26、报文。BGP中使用自治系统这个术语是为了强调这样一个事实:一个自治系统的管理对于其他自治系统而言是提供一个统一的内部选路计划,它为那些通过它可以到达的网络提供了一个一致的描述。三种协议分别采用不同的下层协议承载自己,即RIP使用UDP、OSPF使用IP、BGP使用TCP。7TCP协议TCP(Transmission Control Protocol,传输控制协议)使用IP作为网络层协议。在网络通信传输机制中,它属于面向连接、可靠传输的类型。面向连接的传输意味着在进行通信以前,需要在两个系统之间建立逻辑连接,在每个数据传输的过程中都需要进行应答以保证数据包的完整。这种方法需要的网络开销较大,可是
27、数据传输的可靠性可以保证。虽然TCP使用不可靠的IP服务,但它却提供了一种可靠的传输层服务。TCP承载的高级应用与路由协议有HTTP、FTP、SMTP、BGP等。8UDP协议UDP(User Datagram Protocol,用户数据报协议)属于面向无连接、不可靠传输的类型。该协议只负责接收和传送由上层协议传递的消息,它本身不做任何检测、修改与应答,上层协议需要自己处理这些事务。UDP的报头格式较简单,主要是地址信息、包的长度和校验信息。与此对应,TCP包的头信息有十多个域。因此UDP的网络开销一般要小于TCP。由于UDP在传送数据过程中没有建立连接,亦不进行检查,因此在良好的网络环境中,其
28、工作效率较TCP要高。由于UDP的这种特点,因此亦是进行网络广播的首选协议。UDP承载的高级应用与路由协议有DNS、SNMP及RIP等。9应用协议应用协议是平时使用最广泛的协议,这层的每个协议都由两部分组成:客户程序和服务程序。程序通过服务器与客户机的交互来工作,如SNMP、FTP、SMTP、POP3等。应用协议被下层协议所承载,提供更加具体的应用服务,详见第12章。更多关于TCP/IP协议的介绍请参考相关书籍。6.1.3 TCP/IP地址函数TCP/IP协议下的WinSock函数与5.2.3节介绍的函数基本一致(实现产生的区别见8.1节),特殊之处在于其地址编码。TCP/IP协议的地址编码遵
29、循IP协议(参见6.1.2节),采用结构sockaddr_in实现。同时,为了解决不同情况下的地址格式转换与查询问题,还分别提供了一组地址转换函数和地址信息查询函数,下面分别进行介绍。1地址结构TCP/IP协议采用一种不同于IPX/SPX和NetBIOS的逻辑地址编码方法,使用IP地址结构体sockaddr。TCP/IP协议WinSock的bind、connect、recvfrom、sendto等都用这个地址结构来指明地址信息,该结构的定义如下:struct sockaddr unsigned short sa_family; /地址协议族char sa_data14; /地址数据字符数组然而
30、,一般在编程中,并不直接使用sockaddr结构,而是使用其等价结构sockaddr_in,定义如下:struct sockaddr_inshort sin_family;/地址协议族unsigned short sin_port;/IP端口struct in_addr sin_addr;/IP地址结构char sin_zero8 ;/填充,使总长度与sockaddr一致其中,in_addr结构体存放的IP地址可以用标准点分式字符串、无符号短整型、无符号长整型等三种不同的方式描述。该结构是一个联合体(union),定义如下:struct in_addr union struct unsigne
31、d char s_b1, s_b2,s_b3, s_b4;/标准点分式字符串 S_un_b; struct unsigned short s_w1, s_w2;/两个无符号短整型 S_un_w; unsigned long S_addr;/一个无符号长整型 S_un;2地址转换在TCP/IP地址的使用中,不仅由于具有不同的字节顺序(网络字节顺序和主机字节顺序)需要转换,再加上地址本身的三种数据类型有时需要进行适当的转换,因此WinSock提供了以下一组转换函数。(1) ntohl()函数:将一个u_long类型数(32位无符号整数)从TCP/IP网络字节顺序转换成主机字节顺序,原型如下:u_l
32、ong WSAAPI ntohl(u_long netlong);/netlong是TCP/IP网络字节顺序表示的32位/无符号整数(2) ntohs()函数:将一个u_short类型数(16位无符号整数)从TCP/IP网络字节顺序转换成主机字节顺序,原型如下:u_short WSAAPI ntohs(u_short netshort);/netshort是TCP/IP网络字节顺序表示的16位/无符号整数(3) htonl()函数:将一个u_long类型数(32位无符号整数)从主机字节顺序转换成TCP/IP网络字节顺序,原型如下:u_long WSAAPI htonl(u_long hostl
33、ong); /hostlong是指主机字节顺序表示的32位无符号整数(4) htons()函数:将一个u_short类型数(16位无符号整数)从主机字节顺序转换成TCP/IP网络字节顺序,原型如下:u_short WSAAPI htons(u_short hostshort);/hostshort是按主机字节顺序表示的16位无符号整数上述函数很容易记忆,h代表主机字节顺序,n代表网络字节顺序,s代表无符号短整型,l代表无符号长整型,to代表转换目标。另外,还有两个重要的函数,即inet_addr()和inet_ntoa(),用于IP地址点分表示法的转换。(1) inet_addr()函数将一个
34、用点分表示法表示的地址的字符串地址转换成网际地址in_addr形式,所有网际地址都以网络字节顺序(字节顺序从左到右)返回,原型如下:unsigned long WSAAPI inet_addr(const char FAR * cp);/cp是含有用点分表示法表示的/地址字符串(2) inet_ntoa()函数将一个网际地址转换成点分十进制表示法表示的字符串。它接收由参数in指定的网际地址结构,返回以点分表示法表示的地址的ASCII字符串,原型如下:char FAR * WSAAPI inet_ntoa(struct in_addr in);/in表示主机网际地址结构3地址查询TCP/IP还提
35、供地址查询函数,可以在编程过程中根据需要获取主机信息。主机信息被放入一个hostent的结构体里。函数如下:struct hostent char FAR * h_name; /PC的官方名 char FAR * FAR * h_aliases; /PC的别名 short h_addrtype; /地址类型 short h_length; /地址长度 char FAR * FAR * h_addr_list;/主机地址列表可以依据主机的地址得到该主机信息,函数如下:struct HOSTENT FAR * gethostbyaddr ( const char FAR * addr, /主机地址
36、 int len,/地址长度 int type);/地址类型可以依据主机名得到该主机信息,函数如下:struct hostent FAR * gethostbyname (const char FAR * name);/指向主机名的指针 可以依据主机名得到扩展gethostbyname,函数如下:HANDLE WSAAsyncGetHostByName (HWND hWnd,/接收异步请求的窗口句柄unsigned int wMsg, /接收该异步请求的消息const char FAR * name, /指向主机名的指针char FAR * buf, /接收信息的缓冲区指针int buflen
37、 );/缓冲区长度获得本地主机名的函数如下:int gethostname ( char FAR * name, /接收主机名的缓存区int namelen);/缓冲区长度上述部分函数需要PSDK(参见2.8.2节)的支持。TCP/IP协议中的面向连接服务是TCP协议提供的,本节对TCP编程进行介绍。在WinSock编程中,TCP协议编程是用流套接字实现的。流套接字的服务进程和客户进程在通信前必须创建各自的套接字并建立连接,然后才能对相应的套接字进行读/写操作,实现数据的传输。6.3 TCP编程6.3.1 TCP程序结构在TCP通信中主要有连接的建立、数据的传输、连接的关闭等三个主要过程(如图
38、6-6所示)。每个过程完成不同的工作,而且TCP包的序列号和确认号在每个过程中的变化都是不同的。TCP建立连接也就是我们常说的三次握手,它需要三步完成。在 TCP 建立连接后,就可以开始传输数据了。TCP工作在全双工模式,它可以同时进行双向数据传输。以服务器向客户端发送数据为例,服务器向客户端发送一个数据包,客户端收到这个数据包后,会向服务器发送一个确认数据包。TCP连接的关闭经历四次挥手的过程。图6-6 流套接字程序时序图下面是从套接字编程角度进行的服务端与客户端的函数过程,TCP通信的三个过程蕴含于其中。 建立套接字。用socket()函数完成。 将指定协议的套接字绑定到它已知的名字上,这
39、个名字就是本地的IP地址端口号。这个过程通过bind()函数完成。 服务进程要处于监听状态,等待任意数量的客户端连接,以便为它们的请求提供服务。此服务进程必须在所绑定的名字上进行监听,所以要把套接字置为监听模式。通过listen()函数来实现。 服务进程调用函数accept()或WSAAccept()准备接收来自客户端的连接,如果一个客户端用connect()函数试图建立连接,服务进程就可以接受连接。 建立连接后,服务器和客户端之间就可以使用send()和recv()函数进行通信。注意,默认情况recv()函数处于阻塞模式,在接收到数据前,程序不向下执行。 通信结束后,调用closescoke
40、t()函数关闭套接字。注意:在此编程过程中,服务器必须首先启动,直到执行完accept()调用,进入等待状态后,方能接收客户请求。如客户在此之前启动,则connect()将返回出错代码,表示连接不成功。6.3.2 TCP服务器端下列程序是一个面向连接的服务器端程序,能够侦听客户端的连接请求。当使客户端发送字符串“list0”到“list5”,服务器端每接收到一个字符串就回应一个echo信号,例如:接收到“list4”,回应“echo list4”。#include stdafx.h#include winsock.h#include windows.h#include stdio.h#prag
41、ma comment(lib,wsock32.lib)#define RECV_PORT 3000SOCKET sock,sock1;sockaddr_in ServerAddr;sockaddr_in ClientAddr;int Addrlen;DWORD StartSock()WSADATA WSAData; if (WSAStartup(MAKEWORD(2,2),&WSAData)!=0) /初始化套接字printf(sock init fail !n);return(-1); return(1);DWORD CreateSocket()sock=socket(AF_INET,SOC
42、K_STREAM,0); /创建套接字if (sock=SOCKET_ERROR)printf(sock create fail !n);WSACleanup();return(-1);ServerAddr.sin_family=AF_INET; /填充服务器地址ServerAddr.sin_addr.s_addr=htonl(INADDR_ANY);ServerAddr.sin_port=htons(RECV_PORT);if (bind(sock,(struct sockaddr FAR*)&ServerAddr,sizeof(ServerAddr)=SOCKET_ERROR) /绑定套接
43、字为创建的sock指定通信对象 printf(bind is the error); return(-1); return (1);DWORD ConnectProcess() char buff80;char buffecho80=echo ;int i,length;Addrlen=sizeof(sockaddr_in);if (listen( sock, 5 ) 0) printf(Listen error); return(-1); else printf(Listening.n);sock1 = accept( sock,(struct sockaddr FAR *) &Client
44、Addr,&Addrlen);printf(connect ok n);printf(wait for receive.n); for (i=0;i6;i+) memset(buff,0,80); if (recv(sock1,buff,80,0)=0) return -1; else printf(recive %sn,buff); strncpy(buffecho+5,buff,5);/依次生成ehco list0、ehco list1、ehco list2、ehco list3、/ ehco list4、ehco list5字符串并发送 length=send(sock1,buffech
45、o,strlen(buffecho),0); if (length=0) printf(send data error !n); closesocket(sock); WSACleanup(); return(-1); return(1);int main(int argc, char* argv)if (StartSock()=-1)return(-1);if (CreateSocket()=-1)return(-1);if (ConnectProcess()=-1)return(-1);closesocket(sock);WSACleanup();return 0;6.3.3 TCP客户端
46、下面程序是6.3.2节程序对应的客户端程序,首先向服务端发出连接请求。连接建立后向服务端发送字符串“list0”到“list5”,之后接收来自服务端的响应。#include stdafx.h#include winsock.h#include windows.h#include stdio.h#include #pragma comment(lib,wsock32.lib)#define SEND_PORT 3000SOCKET sock;sockaddr_in ServerAddr;DWORD CreateSocket() sock=socket(AF_INET,SOCK_STREAM,0)
47、; /创建套接字if (sock=SOCKET_ERROR)printf(sock create fail !n);WSACleanup();return(-1);return (1);DWORD CallServer() CreateSocket();if(connect(sock,(struct sockaddr*) &ServerAddr,sizeof( ServerAddr)=SOCKET_ERROR) printf(Connect fail n); closesocket( sock ); return(-1); return(1);DWORD TCPSend(char data)i
48、nt length;length=send(sock,data,strlen(data),0); if (length=0)printf(send data error !n);closesocket(sock);WSACleanup();return(-1);return(1);DWORD TCPRecv(char data)int length;length=recv(sock,data,80,0); if (length=0)printf(send data error !n);closesocket(sock);WSACleanup();return(-1);return(1);DWO
49、RD StartSock()WSADATA WSAData; if (WSAStartup(MAKEWORD(2,2),&WSAData)!=0)/初始化套接字printf(sock init fail !n);return(-1); ServerAddr.sin_family=AF_INET; /填充服务器地址ServerAddr.sin_addr.s_addr=inet_addr();ServerAddr.sin_port=htons(SEND_PORT);return(1);int main( )char buff80=list;char buffrecv80=;char num; in
50、t i;StartSock();while (CallServer()=-1);printf(connect ok!n); for (i=0;i6;i+) _itoa(i,&num,10); strncpy(buff+4,&num,1); printf(press any key to send %s!,buff); getchar(); TCPSend(buff);/发送list并接收echo ZeroMemory(buffrecv,80); TCPRecv(buffrecv); printf(%sn,buffrecv); closesocket(sock);WSACleanup(); re
51、turn(0);6.3.4 TCP连接与断开TCP连接与断开是其通信的关键,TCP采用三次握手与四次挥手的方式来实现。1三次握手TCP提供的一个可靠连接的方式是通过三次握手(Three-way Handshake)来完成的。三次握手是指通信双方彼此交换三次信息。三次握手是指在存在包丢失、重复和延迟的情况下,确保通信双方信息交换确定性的充分必要条件。三次握手的操作过程如图6-7(a)所示。(1) 请求端(通常称为客户)发送一个TCP报文,并设置了SYN标志,指明客户打算连接的服务器的端口,以及初始序号(ISN)。这个TCP报文为报文段1。(2) 服务器发回一个设置了SYN标志和ACK标志的TCP
52、报文(报文段2)作为应答,并将该报文中确认序号设置为客户的ISN加1,并将ISN设置为服务器端初始序号,用以对客户的SYN报文段进行确认,一个SYN占用一个序号。(3) 客户开始向服务器发送数据,并设置ACK标志,将确认序号设置为服务器的ISN加1,用以对服务器的SYN报文段进行确认(报文段3)。图6-7 三次握手与四次挥手的过程在程序设计中,WinSock的listen、connect、accept配合完成该过程,三次握手在WinSock函数TCP编程中所处的位置如图6-8所示。上述三个过程的依次完成表明建立了TCP连接。图6-8 三次握手在Winsock函数TCP编程中所处的位置2四次挥手
53、建立一个TCP连接需要三次握手,而正常终止一个连接则要经过四次挥手,这是由TCP的半关闭(half-close)特性所造成的。由于TCP是全双工连接,每个方向的连接必须单独关闭,因此当一方完成数据发送任务后必须发送一个FIN标志来终止这个方向的连接。当一端收到一个FIN后,必须通知应用层另一端已经终止了该方向的数据传送。发送FIN通常是应用层关闭连接的结果。TCP连接收到一个FIN标志只意味着对方已不再发送数据,但己方仍能发送数据,这是半关闭型应用。正常关闭过程如图6-7(b)所示。(1) 通常情况下,一方完成主动关闭而另一方完成被动关闭(但也存在双方都执行主动关闭的特例)。首先进行关闭的一方
54、(即发送第一个FIN)执行主动关闭,而另一方(收到这个FIN)执行被动关闭。图6-8中的报文段1发起终止连接,TCP客户端发送一个FIN,用来关闭从客户到服务器的数据传送。(2) 当服务器收到这个FIN,它发回一个ACK,确认序号为收到的ISN加1(报文段2)。(3) 接着,这个服务器程序就关闭它的连接,TCP端发送一个FIN(报文段3),客户必须发回一个确认,并将确认序号设置为收到的ISN加1(报文段4)。四次挥手的WinSock函数在TCP编程中所处的位置如图6-9所示。图6-9 四次挥手的WinSock函数在TCP编程中所处的位置3优雅地关闭连接TCP连接的关闭处于通信程序的末端,一般容
55、易被程序设计者所忽视,认为直接调用shutdown()或closesocket()函数即可,这种做法通常过于武断和直接,会导致四次挥手没有全部完成,从而造成传输数据丢失、关闭不完善等问题。在这种关闭过程中,一方开始关闭通信会话,但另一方仍然可以读取线上或网络堆栈上已挂起的数据。因此必须提倡优雅地关闭TCP连接,这也是MSDN专门强调的问题。建议按照如下的方法关闭TCP连接(C代表客户,S代表服务器)优雅地关闭TCP连接:C:注册(用WSAAsyncSelect)FD_CLOSE事件(套接字起初创建之后就应当注册),调用shutdown,how=SD_SEND(实际上发了一个FIN包);S:注册
56、FD_CLOSE事件(套接字起初创建之后就应当注册),当发现此事件发生后(C的shutdown和全部数据都处理这两个条件均满足才会引起的,参见MSDN中FD_CLOSE的解释),为了使FD_CLOSE可以顺利发生,则要调用recv直到返回值是0或SOCKET_ERROR,调用shutdown,how=SD_SEND(实际上发了一个FIN包),等上一会,调用closesocket;C:当发现FD_CLOSE事件发生后(S的shutdown和全部数据都处理这两个条件均满足才会引起的,参见MSDN中看FD_CLOSE的解释),为了使FD_CLOSE可以顺利发生,则要调用recv直到返回值是0或SOC
57、KET_ERROR,等上一会再调用closesocket。图6-10是利用网络嗅探器软件Wireshark抓到的TCP服务器(IP:00)与客户端(IP:5)之间的三次握手(包13)和四次挥手(包13)过程中的数据包。从图中包列表面板(packet list)的info字段可以清楚地看到SYN、ACK、FIN等标识,以及报文长度等信息,如果想得到一个数据包更加详细的信息,可以在包列表窗口选中该包,在包细节面板(packet details)里看到该数据包的各字段数据,或者在包字节面板(packet byte)里查看该数据包的十六进制数据。图6-10 三次握手与四次挥手过程数据报套接字是无连接的
58、,其编程过程与流套接字编程类似,但是要比流套接字简单。6.4.1 UDP程序结构数据报套接字是无连接的,所使用的基本WinSock函数与流套接字相同,而数据传输函数则使用的是sendto()函数和recvfrom()函数。6.4 UDP编程图6-11 UDP通信时序图具体步骤如下(如图6-11所示): 服务器和客户端都要建立一个数据报套接字。 服务器调用bind()函数给套接字分配一个公认的端口,在开发应用程序时,这个公认端口通常是指定的。客户端同样需要对套接字进行绑定(如果在套接字建立之后直接调用sendto()函数,该步骤可以省略)。 客户端和服务器都可以使用sendto()函数发送数据,
59、使用recvfrom()函数接收数据,从而完成数据报传递。默认情况recvfrom()函数处于阻塞模式,在接收到数据前,程序不向下执行。 通信结束后,调用closescoket()关闭套接字。注意:无连接服务器也必须先启动,否则客户请求传不到服务进程。无连接客户不调用connect(),因此在数据发送之前,客户与服务器之间尚未建立完全相关,但各自通过socket()和bind()建立了半相关。发送数据时,发送方除指定本地套接字号外,还需指定接收方套接字号,从而在数据收发过程中动态地建立了全相关。6.4.2 UDP服务器端本节展示的是用UDP实现点对点会话的一个实例程序。以下程序实现的是一个计算
60、机对计算机的点对点会话。程序循环接收另外一台发送的消息(也可以增加发送功能)。实现原理是程序首先建立一个socket(),然后同一个本地地址与端口100绑定在一起。这样,如果对方计算机向这个地址上的这个端口发送数据,此计算机就可以循环接收数据了。#include #include #pragma comment(lib, wsock32.lib)DWORD StartSock() /略,同6.3.2节int main()printf(初始化.n);StartSock();SOCKET s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); /创建套接字if(
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 浙江师范大学《规范字与书法》2023-2024学年第一学期期末试卷
- 郑州体育职业学院《视频传播实务》2023-2024学年第一学期期末试卷
- 长春师范大学《社会治理》2023-2024学年第一学期期末试卷
- 榆林职业技术学院《户外拓展与定向运动》2023-2024学年第一学期期末试卷
- 食用菌栽培基质废弃物降解
- 硕士职场竞争力
- 秋分节气与粮食安全
- 农业创新路演
- 学校文化建设上墙标语
- 会计辞职报告范文
- 华为经营管理-华为市场营销体系(6版)
- 2023年中国育龄女性生殖健康研究报告
- 钢结构加工厂考察报告
- 发电机检修作业指导书
- 薪酬与福利管理实务-习题答案 第五版
- 废旧物资处置申请表
- GB/T 37234-2018文件鉴定通用规范
- GB/T 31888-2015中小学生校服
- 质量检查考核办法
- 云南省普通初中学生成长记录-基本素质发展初一-初三
- 外科医师手术技能评分标准
评论
0/150
提交评论