• 657.50 KB
  • 2022-04-22 11:51:07 发布

网络协议分析习题解答参考思路.doc

  • 42页
  • 当前文档由用户上传发布,收益归属用户
  1. 1、本文档共5页,可阅读全部内容。
  2. 2、本文档内容版权归属内容提供方,所产生的收益全部归内容提供方所有。如果您对本文有版权争议,可选择认领,认领后既往收益都归您。
  3. 3、本文档由用户上传,本站不保证质量和数量令人满意,可能有诸多瑕疵,付费之前,请仔细先通过免费阅读内容等途径辨别内容交易风险。如存在严重挂羊头卖狗肉之情形,可联系本站下载客服投诉处理。
  4. 文档侵权举报电话:19940600175。
'《网络协议分析与实现》习题解答参考思路第1章习题解答参考思路习题1:该题考查对异构网络互联概念、异构网络涉及的问题以及解决方法的理解程度。其中涉及的问题包括地址问题、包格式转换问题、路由问题等,其中1.1.3节还列举了很多其他的问题。习题2:该题可参考教材中所讲述的用户A和用户B的数据转换和传输过程进行解答。习题3:该题主要考查网上查找资料的能力。在http://www.rfc-editor.org/网站上可以查到所有的RFC信息。习题4:TCP/IP模型和OSI参考模型之间的层次对应关系及各层协议参见教材中的图1-5。习题5:该题主要考查动手能力,可以使用Wireshark(曾称为Ethereal)、SnifferPortable等软件进行抓包,然后针对一些具体报文进行分析。注意分析通信中的多路复用和多路分解过程,说出通信双方的物理地址、IP地址和端口地址。习题6:该题主要考查阅读代码的能力,这部分代码是对数据结构课程中队列操作的一个实现。该队列是一个基于优先级排序的队列,主要的数据结构是qinfo:structqinfo{Boolq_valid;intq_type;/*mutextype*/intq_max;intq_count;intq_seen;intq_mutex;42 int*q_key;char**q_elt;};具体操作如下:intenq(intq,void*elt,intkey);/*入队列操作,根据key的大小插到队列中的合适位置*/void*deq(intq);/*出队列操作*/void*headq(intq);/*获取队列头部元素*/void*seeq(intq);/*按顺序取队列元素*/intnewq(unsignedsize,unsignedmtype);/*分配一个新的队列,并返回队列的索引位置*/intfreeq(intq);/*释放队列*/intlenq(intq);/*获取队列长度*/staticintinitq();/*初始化队列*/42 第2章习题解答参考思路习题1:该题主要考查对HDLC概念的理解程度,答案参见2.1节。习题2:该题主要考查对SLIP、PPP之间的关系,PPP对SLIP的改进以及PPPoE的概念的理解程度。SLIP和PPP可以在教材中找到参考资料,PPPoE可以到网上或RFC中查找参考资料。PPPoE全称为PointtoPointProtocoloverEthernet(以太网上的点对点协议),简单地说,就是以太网和PPP结合后的协议,目前广泛应用在ADSL接入方式中。通过PPPoE技术和宽带调制解调器(比如ADSLModem)可以实现高速宽带网的个人身份验证访问,为每个用户创建虚拟拨号连接,这样就可以高速连接入因特网。(1)PPP在SLIP的基础上的改进之处●提供了校验机制,可以对每一帧进行检查;●提供了IP地址的动态协商机制,使通信双方能够得知对方的IP地址;●在一条串行链路上提供了对多协议的支持;●提供了对TCP和IP数据报首部的压缩机制。(2)PPPoE和PPP的关系PPP提供了一种标准的方式在点对点的链路上传输多种网络层协议的数据报。它要求通信双方之间是点到点的关系,由于不适用于广播类型的以太网和另外一些多点访问类型的网络,因此就产生了PPPoE。在实际应用中,PPPoE利用以太网的工作机理,将ADSLModem的10BASE-T接口与内部以太网互联,在ADSLModem中采用RFC1483的桥接封装方式对终端发出的PPP报文进行LLC/SNAP封装后,通过连结两端的PVC在ADSLModem与网络侧的宽带接入服务器之间建立连接,实现PPP的动态接入。PPPoE接入利用在网络侧和ADSLModem之间的一条PVC(永久虚拟电路)就可以完成以太网上多用户的共同接入,实际组网方式简单,实用方便,大大降低了网络的复杂程度。PPPoE的实质就是以太网和拨号网络之间的一个中继协议,它继承了以太网的快速和PPP拨号的简单、用户验证、IP分配等优点。(3)PPPoE的工作流程PPPoE提供了在广播式网络(如以太网)中多台主机连接到远端的访问集中器上的一种标准。在这种网络模型中,不难看出所有用户的主机都需要能独立地初始化自己的PPP协议栈,而且通过PPP本身所具有的一些特点能在广播式网络上对用户进行计费和管理。为了能在广播式网络上建立、维持各主机与访问集中器之间点对点的关系,需要每个主机与访问集中器之间建立唯一的点到点的会话。PPPoE共包括两个阶段,即PPPoE的发现阶段(PPPoEDiscoveryStage)和PPPoE的会话阶段(PPPoESessionStage)。42 ①发现阶段:此阶段用来建立连接,如下图所示,当一个用户主机想开始一个PPPoE会话时,首先必须经过发现阶段以识别PPPoEServer的以太网MAC地址,并建立一个PPPoE会话标识(SessionID)。如上图所示,该阶段的基本工作流程由4个步骤组成。PADI:如果要建立一条PPPoE连接,首先PPPoE客户端要以广播的方式发送一个PADI(PPPoEActiveDiscoveryInitiation)报文,PADI报文包括客户端请求的服务。PADO:在PPPoE服务器(BRAS)收到一个PADI报文之后,它会判断自己是否能够提供服务,如果能够提供服务,就会向客户端发送PADO(PPPoEActiveDiscoveryOffer)报文来进行回应。PADO报文包括PPPoE服务器名称和与PADI报文中相同的服务名;如果PPPoE服务器不能为PADI提供服务,则不允许用PADO报文应答。PADR:由于PADI是以广播的形式发送出去的,PPPoE客户端可能会收到不止一个PADO报文,它将审查所有接收到的PADO报文并根据其中的服务器名或所提供的服务选择一个PPPoE服务器,同时向选中的服务器发送PADR(PPPoEActiveDiscoveryRequest)报文。PADR报文包括客户端所请求的服务。PADS:当PPPoE服务器收到客户端发送的PADR报文时,它准备开始一个PPPoE会话。它为PPPoE会话创建一个唯一的PPPoE会话标识,并向客户端发送PADS(PPPoEActiveDiscoverySession-confirmation)报文作为应答。当发现阶段正常结束后,通信的两端都会获得会话标识和对方的MAC地址,它们一起唯一定义一个PPPoE会话。②会话阶段:PPPoE进入PPP会话阶段后,客户端和服务器将进行标准的PPP协商,PPP协商通过后,数据通过PPP封装发送。PPP报文作为PPPoE帧的净荷被封装在以太网帧内,发送到PPPoE链路的对端。会话标识必须是发现阶段确定的标识,并且在会话过程中保持不变,MAC地址必须是对端的MAC地址。在会话阶段的任意时刻,PPPoE服务器和客户端都可向对方发送PADT(PPPoEActiveDiscoveryTerminate)报文通知对方结束本会话。收到PADT以后,就不再允许使用该会话发送PPP流量了。在发送或接收到PADT报文后,即使是常规的PPP结束报文也不允许发送。一般情况下,PPP通信双方使用PPP报文自身来结束PPPoE会话,但在无法使用PPP时可以使用PADT来结束会话。(4)帧结构及其应用42 版本4位类型4位代码8位会话标识16位长度16位净载荷发现阶段承载一些标记会话阶段承载PPP报文PPPoE作为宽带网接入的一种有效方法,不但可以防止IP被盗用,还有利于开展多服务、速率限制和按时按流量计费等多方面的应用。习题3:该题主要考查对CSMA/CD和CSMA/CA协议,以及无线网络中“隐藏站”问题的理解程度。习题4:XINU操作系统源代码etherlance目录下存放的是AMD7990Lance以太网控制器驱动实现程序源代码,其中ethdemux函数实现了该控制器接收到以太帧后所要进行的多路分解操作。该函数首先进行一些检查,然后进行数据帧的复制,并最终转入ni_in()函数,进行网络接口层的多路分解。/*ethdemux.c-ethdemux*/#include#include#include/*--------------------------------------------------------------------------------------------------*ethmudex-选择一个端口发送一个传入的数据报(ETHER端或者其他)*-------------------------------------------------------------------------------------------------*//*首先检查来自网络的数据帧的正确性,然后申请缓冲区以存储数据帧,最后将数据帧交由ni_in()函数处理*/intethdemux(etptr,bn)structetblk*etptr;/*以太网控制块描述*/intbn;{structbre*pbre;structle_md*pmd;/*消息描述符*/structotblk*otptr;structep*pep;/*以太网的帧结构*/intnoth,readlen,errs,ifnum;42 char*dest;ifnum=etptr->etintf;/*端口号*/pbre=&etptr->etbrr[bn];pmd=pbre->pmd;errs=pmd->lmd_flags&(RMD_FRAM|RMD_OFLO|RMD_CRC|RMD_BUFF);if(errs!=0){if(errs&RMD_FRAM)kprintf("ethread:framingerrorn");if(errs&RMD_OFLO)kprintf("ethread:bufferoverflown");if(errs&RMD_CRC)kprintf("ethread:CRCerrorn");if(errs&RMD_BUFF)kprintf("ethread:don"townnextbuffern");gotodrop;}readlen=pmd->lmd_mcnt;/*消息的长度(实际长度)*/if(readlen>(EP_DLEN+18))/*长度是否合法*/gotodrop;pep=(structep*)nbgetbuf(Net.netpool);/*申请帧缓冲区*/if(pep==0){/*申请失败*/if(ifnum>=0&&ifnumep_eh,pbre->buf,readlen-EP_CRC);/*将网卡缓冲区中的信息复制到帧缓冲区*/pmd->lmd_flags=LMD_OWN;/*将使用权还给以太网卡*/pbre->flags=0;/*标志位清零*/#ifNoth>0/*其他设备端口*/dest=(char*)pep->ep_dst;for(noth=0;nothetoth[noth];if(otptr==0||!otptr->ot_valid)continue;if(blkequ(dest,otptr->ot_paddr,EP_ALEN)||blkequ(dest,otptr->ot_baddr,EP_ALEN))42 break;}if(noth!=Noth)ifnum=otptr->ot_intf;#endifif(ifnum<0)/*端口号是否合法*/returnfreebuf(pep);if(nif[ifnum].ni_state!=NIS_UP){/*端口是否处于已连接状态*/nif[ifnum].ni_idiscard++;returnfreebuf(pep);}pep->ep_type=net2hs(pep->ep_type);/*将帧类型字节序转换为本机字节序*//*ni_in函数可以根据帧类型来判断多路分解*/ni_in(&nif[ifnum],pep,readlen);/*调用ni_in函数来处理数据帧*/returnOK;drop:if(ifnum>=0&&ifnumlmd_flags=LMD_OWN;/*交还使用权*/pbre->flags=0;returnSYSERR;}流程图如下:42 42 习题5:dot2ip()函数和ip2dot()函数实现了字符串形式的点分十进制格式的IP地址与32位IP地址格式之间的相互转换。/*dot2ip.c-dot2ip*/#include#include#include/*------------------------------------------------------------------------*dot2ip-将字符串形式的点分十进制格式IP地址转换为一个没有小数点的IP地址*------------------------------------------------------------------------*/dot2ip(constchar*pdot)/*参数pdot是一个指向字符数组的指针,字符数组每个单元存储IP地址的一个字节:[192][.][168][.][101][.][1]*/{IPaddrip;/*IPaddr是个无符号长整型*/unsignedchar*p;/*定义一个指向字符数组的指针,但此指针并没有指向真正的内存空间*/inti;ip=0;p=(unsignedchar*)&ip;/*使字符数组指针p指向起始地址是&ip的内存单元*/for(i=0;pdot&&*pdot&&i#include#include/*------------------------------------------------------------------------*ip2dot-将一个没有小数点的IP地址转换为字符串形式的点分十进制格式IP地址*------------------------------------------------------------------------*/char*ip2dot(char*pdot,IPaddrip)/*pdot指向一个字符数组,用于存储转换后的IP地址;IP是没有小数点的IP地址的首地址*/{char*pch=pdot;inti;sprintf(pch,"%u",((char*)&ip)[0]&0xff);/*将IP地址第一个字节的值存入pch所指向的字符数组的第一个单元中*/pch+=strlen(pch);/*pch跳过长度为strlen(pch)的内存单元,即指向下一个单元*/for(i=1;i42 #include#include/*------------------------------------------------------------------------*netmatch–检查目的IP地址是否在此网络中*------------------------------------------------------------------------*/Boolnetmatch(IPaddrdst,IPaddrnet,IPaddrmask,Boolislocal){if((dst&mask)!=(net&mask))/*IP地址跟子网掩码进行与运算的结果就是网络号,以此来判断dst在不在该网络上*/returnFALSE;/**localsrcsshouldonlymatchunicast(单一传播)addresses(hostroutes)*/if(islocal)/*判断是否来自于本地网络*/if(isbrc(dst)||IP_CLASSD(dst))//如果dst是广播地址或是组播地址*/returnmask!=ip_maskall;/*如果是来自于本地网络并且是广播地址或是组播地址,那么发送该广播帧的主机就不需要接收此广播帧,于是将自己置成不匹配此网络,就不会再收到此广播帧了*/returnTRUE;}/*netnum.c-netnum*/#include#include#include/*------------------------------------------------------------------------*netnum-用来得到某个IP地址的网络号*------------------------------------------------------------------------*/netnum(IPaddripa){IPaddrmask=~0;/*初始化为0*/42 if(IP_CLASSA(ipa))mask=hl2net(0xff000000);/*得到A类地址的子网掩码255.0.0.0*/if(IP_CLASSB(ipa))mask=hl2net(0xffff0000);/*得到B类地址的子网掩码255.255.0.0*/if(IP_CLASSC(ipa))mask=hl2net(0xffffff00);/*得到C类地址的子网掩码255.255.255.0*/returnipa&mask;/*得到网络号,即网络地址*/}42 第3章习题解答参考思路习题1:本题主要考查对ARP软件整体结构的理解程度,并帮助学生梳理ARP软件中函数之间的关系,从而更好地掌握ARP软件中函数的功能和处理机制。详细描述请参照3.3节。习题2:本题主要考查对ARP报文处理算法的理解程度,并帮助学生进一步理解ARP报文处理算法。报文处理算法可以概括为三个部分:当报文交付给arp_in()函数时,该算法首先确认该报文是否为合法报文及特征是否与接口相符;然后查询ARP缓冲区表项,若不存在对应表项且确认该报文是发往本机,则直接创建新表项,再根据该报文对表项进行修改操作;最后根据该ARP报文是否为ARP请求来决定下一步操作,构造一个ARP应答报文或清理该ARP报文。ARP报文处理算法的利弊分析如下:在正常操作下,这是一种很有效率的处理算法,简单明了快捷。但是由算法分析可知,arp_in()函数“不计后果”地使用当前处理的ARP报文中的<发送方协议地址,发送方物理地址>创建新缓冲区或者修改已存在的缓冲表项,没有使用任何保护手段或者措施。ARP攻击者很容易利用这一缺陷,发送ARP欺骗报文。习题3:主机发送IP数据报是通过调用netwrite()函数来实现的,因此分析netwrite()函数即可了解IP数据报的发送过程。详细描述请参考3.5节。习题4:Arpalloc.c代码如下:/*--------------------------------------------------------------------------------------------------*在ARP缓冲区表中分配一个表项*--------------------------------------------------------------------------------------------------*/structarpentry*arpalloc(){/*缓冲区表中所指的当前表项,这里采用的是循环队列的数据结构,故需要记录当前表项所处的位置*/staticintaenext=0;structarpentry*pae;/*返回值*/inti;42 for(i=0;iae_state==AS_PENDING&&pae->ae_queue>=0)arpdq(pae);pae->ae_state=AS_PENDING;returnpae;}习题5:ARP攻击原理:由习题2可知,ARP报文处理算法只检查ARP报文格式的合法性,随即直接根据该报文内容修改ARP表项,这一处理方式为网络攻击者提供了“可乘之机”。ARP攻击分为两类:(1)ARP欺骗攻击即通过伪造并发送ARP报文,修改被攻击者ARP缓冲区表项以实现ARP欺骗,监听被攻击者与其他网络的通信;(2)ARP溢出攻击即通过发送大量的ARP报文致使被攻击主机的ARP表项存满无效的表项,从而导致该主机与外界通信中断。避免或阻止ARP攻击可以通过改进ARP自身(优化报文处理算法)或借助动态防御系统(如专用ARP防火墙等)来实现。42 第4章习题解答参考思路习题1:该题主要考查对IP软件整体结构的理解程度,旨在使学生能够脱离教材根据自己的理解勾勒出软件的构架。注重用自己的语言描述各函数的主要功能,以及相互之间的调用关系。具体内容参见4.3节。习题2:该题结合IP数据报结构,主要考查对C语言结构体的掌握程度。structip{charip_verpri;charip_flowlab[3];shortip_ploadlen;charip_proto;charip_ttl;charsrc[16];chardst[16];charip_data[1];};另外,可以参考LINUX或者FreeBSD相应的实现程序。习题3:该题的解答请从4.6.3节的内容中进行综合提炼。习题4:该题主要考查对IP数据报首部个字段的理解程度。IP数据报转发过程中,TTL会发生变化,这样会导致检验和变化;如果IP数据报产生分片,则IP数据报长度字段、标识字段、标志字段、分片偏移字段也会发生变化;如果IP数据报首部存在分片,则首部长度字段有可能发生变化。习题5:该题需要注意,对于不同体系结构的主机需要设计不同的转换宏,这里仅提供16位转换宏。#ifBYTE_ORDER==LITTLE_ENDIAN42 #definehs2net(x)(unsigned)((((x)>>8)&0xff)|(((x)&0xff)<<8))#definenet2hs(x)hs2net(x)#endif#ifBYTE_ORDER==BIG_ENDIAN#definehs2net(x)(x)#definenet2hs(x)(x)#endif习题6:XINU操作系统中环回接口的实现方案如下:我们从IP数据的流向图理解环回接口的实现,IP输入队列中的数据来源有两种:一种是网络中输入的报文,一种是上层协议希望通过ipsend()函数发送的报文。这两种报文经过ipproc()函数处理后调用ipputp()函数发送,报文有两个去向:一个是交付物理网络接口发送,一个是通过环回接口交付上层协议。所以在IP层数据的流向有4种:第一种是1→3,内部数据发送给内部数据;第二种是2→3,将外部接收到的数据向上层协议发送;第三种是1→4,将来自上层协议的数据向外发送;第四种是2→4,只是将收到的数据转发出去。当ipputp()函数调用netwrite()函数发送一个报文时,若路由中提供的报文出接口为本地环回接口,则netwrite()函数会调用local_out()函数将IP数据报交付上层协议,即习题6图示中的3。local_out()函数先是将IP数据报的字节序转换为主机字节序,然后将收到的数据帧重组,再处理IP数据报的选项部分,最后根据协议字段将数据发送给UDP、ICMP、TCP等上层协议。如果协议字段有错误,就发送ICMP不可达报文。因此,local_out()函数处理的是本机接收的数据,这个数据有可能是自己产生的,也可能是网络中收到的数据。42 习题7:选路和转发的主要差异及三层转发的实现方式如下:选路的原理:当路由器收到一个需要它转发的IP数据报时,它会根据数据报中的目的IP地址搜索路由表,找到相关的路由表项,并根据路由中的<目的地址,下一跳,出接口>三元组将数据报从相关的出接口转发。而路由表的维护是由专门的路由选择协议来进行的,IP层只需要在转发数据时搜索路由表即可。转发的原理:交换机接收到源主机发送的数据帧后,在MAC地址表中查找数据帧中的目的MAC地址。如果找到,就将该数据帧发送到相应的端口;如果找不到,就向所有的端口发送。同时利用接收数据帧中的源MAC地址来建立MAC地址表。选路和转发的区别主要是,选路在IP层,根据目的IP地址找到出接口;转发在数据链路层,根据MAC地址对数据进行转发。另外,转发表和路由表不同,转发表中的一行包括从网络号到发出接口的映射和一些MAC信息,而路由表是由路由选择算法建立的一个表,它通常包含从网络号到下一跳IP地址的映射,转发表可以由特殊的硬件来实现,而路由表很少这样。为了实现三层交换技术,交换机将维护一张至少包括“目的IP地址,下一跳MAC地址”在内的硬件转发表。当交换机接收到数据时,根据报文中的“目的IP地址”查询硬件转发表,根据匹配结果进行相应的数据转发,并且采用硬件芯片或高速缓冲区支持,可以达到线速。在交换机刚启动完毕时,交换机就把设备的软件路由表下载到ASIC芯片上。在需要进行三层交换的报文到达交换机后,交换机首先会查询最长匹配硬件转发表,但由于MAC地址是未知的,无法同时下载,此时的硬件转发表是无效的,所以无法进行硬件数据转发。因此,交换机将利用CPU对数据进行软件路由转发,交换机在数据转发过程中获取下一跳IP地址和数据转发出口的MAC地址,然后会被自动下载到三层硬件转发表,此时包含了下一跳IP地址和数据转发出口MAC地址的硬件转发表项才真正生效。在这之后,发往相同目的IP网段的报文到达交换机都可以直接通过最长匹配硬件转发表进行硬件转发,而其他网段的数据转发则需要重复上述过程。习题8:汇编语言函数可参考net/cksum.s文件,C语言函数可参考ucpcksum.c或者tcpcksum.c实现程序。习题9:由于通常情况下,IP报文首部发生变化的字段主要是TTL。而检验和字段是伴随TTL字段变化而变化的,因此根据RFC1624可以设计一种不需要重新计算检验和的修正方法。具体方法参见RFC1642.42 习题10:使用散列结构并通过分离链接的方法处理散列冲突,在元素个数保持一定规模的情况下可以保证insert和search的时间复杂度为O(1)。但是随着元素规模的扩大,insert和search的时间复杂度会逐渐脱离常数。同时,选择散列函数时需要非常谨慎。对于元素关键字随机化程度较高的情况,可采用二分搜索树的实现方式。这种方式可以保证search的时间复杂度为O(logn),但是对元素关键字随机化程度的要求比较高。为了防止不平衡的二分搜索树出现,可采用AVL树或者红黑树作为路由结构。只是两者在元素的insert操作时需要一些额外的时间。目前广泛流行的路由表结构是采用radix树的构造。它可以保证最坏情况下search的时间复杂度达到O(logn)。42 第5章习题解答参考思路习题1:本题主要考查对ICMP软件整体结构的理解程度,帮助学生梳理ICMP软件中函数之间的关系,从而更好地掌握ICMP软件中函数的功能和处理机制。详细描述请参照5.3节。习题2:(1)终点不可达报文:当路由器不能为报文找到路由或者主机不能交付报文时,丢弃该报文并发送该类型报文给源主机;(2)源点抑制报文:当路由器或主机因拥塞而丢弃报文时,向源主机发送该类型报文;(3)超时报文:当路由器收到TTL为零的报文或目的主机在规定的时间内没有收到所有的分片报文时,向源主机发送该类型报文;(4)参数错误报文:当路由器或主机收到存在二义性或字段缺失的报文时,丢弃该报文并发送该类型报文;(5)改变路由报文:当路由器收到本应发往其他路由器的报文时,把该报文发送给正确的路由器,并发送该类型报文通知源主机更正路由,以帮助更新路由。ICMP的5种差错报告报文结构如下:(1)终点不可达报文类型:3代码:0~15检验和未使用(全0)收到的IP数据报的一部分,包括IP数据报首部以及数据报数据的前8个字节(2)源点抑制报文类型:4代码:0检验和未使用(全0)收到的IP数据报的一部分,包括IP数据报首部以及数据报数据的前8个字节(3)超时报文类型:11代码:0或1检验和未使用(全0)收到的IP数据报的一部分,包括IP数据报首部以及数据报数据的前8个字节(4)参数错误报文类型:12代码:0或1检验和42 指针未使用(全0)收到的IP数据报的一部分,包括IP数据报首部以及数据报数据的前8个字节(5)改变路由报文类型:5代码:0~3检验和目标路由器IP地址收到的IP数据报的一部分,包括IP数据报首部以及数据报数据的前8个字节习题3:本题主要考查对PING程序实现及与ICMP报文联系的掌握程度,帮助学生更深入地了解ICMP报文的实现。详细描述请参照5.6节。习题4:icsetbuf.c文件的源代码如下。/*------------------------------------------------------------------------*icsetbuf-为ICMP报文申请一个缓冲区*icsetbuf()函数为ICMP报文分配缓冲区,设置了两个变量,其中一个变量指出该报文是否是一个差错报告报文(或是一个信息请求);而另一个指出这个报文的类型是否是对上一个请求做出的应答。*该函数很直观,具体分四种情况:对绝大多数的应答,icsetbuf()函数重新利用抵达的请求报文占用的缓冲区(即返回由入口参数pa1提供的地址);对于没有具体实现的报文类型,icsetbuf()函数释放引起差错的报文,返回SYSERR;对含有大量数据的ICMP报文,icsetbuf()函数为其分配一个大缓冲区;对其他不能利用原缓冲区的报文,icsetbuf()函数为它们分配一个标准缓冲区。*------------------------------------------------------------------------*/structep*icsetbuf(type,pa1,pisresp,piserr)inttype;char*pa1;/*旧的报文(如果有的话)*/Bool*pisresp,/*查询报文*/*piserr;/*差错报告报文*/{Structep*pep;/*正在处理的以太网帧*/*pisresp=*piserr=FALSE;switch(type){42 caseICT_REDIRECT:/*重定向报文*/pep=(structep*)getbufi(Net.netpool);/*char*getbufi(poolid)申请小缓冲区*/if(pep==NULL)return(NULL);blkcopy(pep,pa1,MAXNETBUF);/*函数原型为blkcopy(to,from,nbytes)*/pa1=(char*)pep;*piserr=TRUE;break;caseICT_DESTUR:/*终点不可达报文*/caseICT_SRCQ:/*源点抑制报文*/caseICT_TIMEX:/*超时报文*/caseICT_PARAMP:/*参数错误报文*/pep=(structep*)pa1;*piserr=TRUE;break;caseICT_ECHORP:/*ECHO应答报文*/caseICT_INFORP:/*消息应答报文*/caseICT_MASKRP:/*ICMP掩码应答报文*/pep=(structep*)pa1;*pisresp=TRUE;break;caseICT_ECHORQ:/*ECHO请求报文*/caseICT_TIMERQ:/*时间戳请求报文*/caseICT_INFORQ:/*消息请求报文*/caseICT_MASKRQ:/*ICMP掩码请求报文*/pep=(structep*)getbufi(Net.lrgpool);/*申请大缓冲区*/if(pep==NULL)return(NULL);break;caseICT_TIMERP:/*时间戳应答报文*//*IcmpOutTimestampsReps++;*/IcmpOutErrors--;/*Kludge:超时报文数加1*/freebuf(pa1);return(NULL);}switch(type){/*更新MIB统计信息量*/caseICT_ECHORP:IcmpOutEchos++;break;caseICT_ECHORQ:IcmpOutEchoReps++;break;42 caseICT_DESTUR:IcmpOutDestUnreachs++;break;caseICT_SRCQ:IcmpOutSrcQuenchs++;break;caseICT_REDIRECT:IcmpOutRedirects++;break;caseICT_TIMEX:IcmpOutTimeExcds++;break;caseICT_PARAMP:IcmpOutParmProbs++;break;caseICT_TIMERQ:IcmpOutTimestamps++;break;caseICT_TIMERP:IcmpOutTimestampReps++;break;caseICT_MASKRQ:IcmpOutAddrMasks++;break;caseICT_MASKRP:IcmpOutAddrMaskReps++;break;}returnpep;}char*getbufi(poolid)/*从预先设定的缓冲池申请缓冲区,如果没有可用的缓冲区则立即返回*/structep{/*以太帧结构*/IPaddrep_nexthop;/*niput函数使用*/shortep_len;/*以太帧长度*/structehep_eh;/*以太帧首部*/charep_data[EP_DLEN];/*以太帧的数据部分*/};icsetdata.c文件的源代码如下。/*ECHOMAX必须是偶数*/#defineECHOMAX(pip)(MAXLRGBUF-IC_HLEN-IP_HLEN(pip)-EP_HLEN)/*MAXLRGBUF最大缓冲区*//*IC_HLEN8*//*IP_HLEN(pip)((pip->ip_verlen&0xf)<<2)*//*EP_HLEN以太帧首部+以太帧长度+下一跳地址*//*------------------------------------------------------------------------*icsetdata-设置数据段,返回值是数据的长度*------------------------------------------------------------------------*/inticsetdata(type,pip,pa2)inttype;42 structip*pip;char*pa2;{structicmp*pic=(structicmp*)pip->ip_data;inti,len;switch(type){caseICT_ECHORP:/*ECHO应答报文*/len=pip->ip_len-IP_HLEN(pip)-IC_HLEN;/*长度=IP报文长度-IP首部长度-ICMP首部长度*/if(isodd(len))/*isodd()函数:判断其参数是不是奇数,如果是奇数就返回TRUE,否则返回FASLE和错误值*/pic->ic_data[len]=0;/*ICMP数据区CRC校验*/returnlen;caseICT_DESTUR:/*终点不可达报文*/caseICT_SRCQ:/*源点抑制报文*/caseICT_TIMEX:/*超时报文*/pic->ic_mbz=(long)0;/*必须为0*/break;caseICT_REDIRECT:/*重定向报文*/blkcopy(pic->ic_gw,pa2,IP_ALEN);/*blkcopy(to,from,nbytes)重定向到网关*/break;caseICT_PARAMP:/*参数错误报文*/len=(int)pa2;pic->ic_ptr=(char)len;/*参数指针*/for(i=0;iic_pad[i]=0;break;caseICT_MASKRP:/*掩码应答报文*/blkcopy(pic->ic_data,pa2,IP_ALEN);/*函数原型为blkcopy(to,from,nbytes)*/break;caseICT_ECHORQ:/*ECHO请求报文*/if((unsigned)pa2>(unsigned)(ECHOMAX(pip)))pa2=(char*)(ECHOMAX(pip));for(i=0;i<(int)pa2;++i)pic->ic_data[i]=i;42 if(isodd(pa2))/*isodd()函数:判断其参数是不是奇数,如果是奇数就返回TRUE,否则返回FASLE和错误值*/pic->ic_data[(int)pa2]=0;return(int)pa2;caseICT_MASKRQ:/*掩码请求报文*/blkcopy(pic->ic_data,ip_anyaddr,IP_ALEN);/*blkcopy(to,from,nbytes)IPaddip_anyaddr={0,0,0,0};*/returnIP_ALEN;}return0;}下面的宏用来计算一个报文的首部长度(以字节为单位)。#defineIP_HLEN(pip)((pip->ip_verlen&0xf)<<2)#defineIPMHLEN20/*以字节为单位的IP数据报首部长度*/#defineIP_ALEN4/*以字节为单位的IP地址的长度*/IPaddric2_gw;/*重定向,网关*/ic_data[];/*ICMP报文数据部分*/charic3_ptr;/*参数指针*//*ECHO这两个报文组合起来确定了两个系统是否能彼此通信。通常是调用命令PING实现*/structicmp{/*ICMP报文*/charic_type;/*报文类型(ICT_*)*/charic_code;/*报文类型代码(ICC_*)*/shortic_cksum;/*ICMP报文首部+数据部分的检验和*/}报文类型代码及相应描述如下表所示。类型代码类型描述0ECHO应答(ECHO-REPLY)报文3终点不可达报文4源点抑制报文42 5重定向报文8ECHO请求(ECHO-REQUEST)报文11超时报文12参数错误报文13时间戳请求报文14时间戳应答报文15信息请求(*已作废)16信息应答(*已作废)17地址掩码请求报文18地址掩码应答报文习题5:本题主要考查对ICMP报文处理流程的掌握程度。该情况报文处理流程可概括为:当函数接收到终点不可达报文时,首先累计不可达报文统计变量(SNMP使用该MIB变量进行网络管理工作),然后从该报文中提取出相关信息(报文类型、引起差错的原因等),通过消息队列把这些信息交付应用层协议,应用层协议会根据ICMP报文反馈的信息进行进一步处理。习题6:本题主要考查对时间戳请求/应答报文的掌握程度,使学生在全面理解的基础上提高独立设计该函数的能力。实现时间戳请求/应答处理函数的流程可概括为:(1)若该函数收到一个时间戳请求报文,则首先记录下收到该请求时其时钟所显示的通用时间,然后根据请求报文构造时间戳应答报文,将时间戳请求中的原始时间戳字段复制到应答报文中的原始时间戳字段,并填入接受时间戳,在应答报文发送离开时填入当前的通用时间,最后交由IP封装并发送;(2)若该函数收到一时间应答报文,则首先记录下收到该应答报文时所显示的通用时间T,然后提取出报文中的原始时间戳、接受时间戳、发送时间戳,计算可得:Time_S=接受时间戳-原始时间戳;Time_R=T-发送时间戳。返回Time_S+Time_R即可。习题7:本题考查对ICMPv6的理解程度,使学生在对ICMP与ICMPv6的比较中掌握这两个协议的特点和优缺点。详情描述请参考教材5.7节。42 第6章习题解答参考思路习题1:该题考查对IGMP软件整体结构的理解程度,可参考教材6.3节及图6-2进行解答。习题2:该题考查对IGMP协议的理解程度。IGMP是路由器和内部子网之间维护组成员关系的一个协议,IGMP报文只能在本地子网内传送,它的TTL总是1,使路由器不能转发该报文。路由器之间的组播路由是由组播路由协议维护的。习题3:IGMP与其他IP组播模块共同完成组播报文在IP网络中的转发。为了实现报文在IP网络中转发,除利用IGMP外还需要进行多播地址转换、多播路由选择、定时器等。多播地址转换:当多播分组传送到最后的局域网上的路由器时,必须将32位的IP多播地址转换为局域网48位的多播地址,这样才能在局域网上进行多播。多播路由选择:转发多播分组的路由器需要彼此交换两种信息。首先,这些路由器需要知道哪些网络包含给定多播组的成员。其次,这些路由器需要有足够的信息来计算到达每一个包含多播组成员的网络最短路径。这就需要多播路由选择协议和计算最短路径的算法。定时器:在路由器端,IGMP首先要为每个端口维护一个查询定时器定时查询接口维护的各个组在其连接的网络中的成员关系。其次,要为每个主机群表维护一个表项生存周期定时器。当一个主机有组播报文发送时,使用的是组播MAC地址,将报文发送给该组所有主机和与其直接相连的组播路由器,组播路由器收到后将组播MAC地址转换为组播IP地址,然后查询组播路由表,判断是否向前传送,如果要向前传送,根据相应的出口将报文传送给下一个网络中该组所有主机的组播路由器。当多播报文传送到最后的局域网上的路由器时,需将32位的IP多播地址转换为局域网48位的多播地址,局域网中含有该组的所有主机都可接收到此组播报文。习题4:主机端负责通知本地子网中的路由器其加入某个多播组或离开某个多播组;路由器负责维护多播组中的成员列表。习题5:参考教材6.4节及图6-3进行解答。42 习题6:XINU操作系统中IGMP的实现是通过tmset()函数来设置的,最终是利用了TCP模块中的定时进程tcptimer来完成的,可以结合这个思路看一下相关代码。习题7:XINU操作系统中主机群表相关操作的代码是igmp目录下以hg开头的一些文件。其中hglookup.c是用来查询遍历的,它利用数组来实现,可以通过使用其他的数据结构,如有序表、散列表等提高查找速度。习题8:主机表建立、维护和遍历过程中的信号量是HostGroup.hi_mutex,引用计数是hg_refs,可以通过查找这两个关键字来了解相关代码。习题9:IGMPv1、IGMPv2、IGMPv3三个版本之间的改进与优化之处如下:IGMPv1:主机可以加入组播组;没有离开信息(leavemessages);路由器使用基于超时的机制去发现其成员不关注的组。IGMPv2:该协议包含了离开信息,允许迅速向路由协议报告组成员的终止情况,这对高带宽组播组或易变型组播组成员而言是非常重要的。IGMPv3:与以上两个协议相比,该协议的主要改进为:允许主机指定要接收通信流量的主机对象。来自网络中其他主机的流量是被隔离的。IGMPv3也支持主机阻止那些来自于非要求的主机发送的网络报文。IGMPv3同路由器的交互过程与IGMPv2相同。但是在IGMPv1/v2中,主机只根据组地址来决定加入某个组并从任何一个源主机接收发给该组地址的组播流。而使用IGMPv3的主机通告该主机所希望加入的多播组,同时还通告该主机所希望接收的多播源的地址。主机可以通过一个包括列表或一个排除列表来指明希望从哪些源主机接收多播流。同时IGMPv3的另外一个好处是节省带宽,避免不需要的、非法的组播数据流占用网络带宽,这在多个多播源共用一个多播地址的网络环境中表现得尤其明显。42 第7章习题解答参考思路习题1:参考教材7.3节及图7-2进行解答。习题2:该题考查对几种路由协议算法的掌握程度。链路状态路由选择协议有收敛缓慢和不稳定等缺点,具体可以查看相关书籍和资料。习题3:该题考查阅读代码的能力。RIP要求路由器定时发送RIP通知报文;另外,路由表项中有个生存期rt_ttl字段,也需要定时维护。可以通过这些线索去查看相关代码。习题4:RIPv1、RIPv2、RIPng三个版本之间的比较如下表所示:RIPv1RIPv2RIPng使用的端口UDP520UDP520UDP521通告地址通告类型255.255.255.255广播224.0.0.9广播/组播FF02::9组播网络支持IPv4IPv4IPv6支持VLSM不支持支持----最大跳数161616路由认证不支持支持明文和MD5认证不支持,认证功能已经集成在IPv6中下一跳地址不携带每个路由表项都携带在专门的路由表项中携带下一跳地址更新,超时,刷新,抑制计时器30,180,120,18030,180,120,18030,180,120,180每次更新的路由表项25不认证时25条认证时24条根据链路是MTU值三个版本协议之间的改进之处如下:RIPv2对RIPv1的改进主要增加了对无分类编址、组播路由更新、路由标记和协议报文验证等机制的支持;在报文格式方面增加了子网掩码和下一跳地址字段。42 RIPng对RIPv2的改进主要体现在:(1)报文格式方面①路由信息中的目的地址和下一跳地址长度不同。RIP报文中路由信息中的目的地址和下一跳地址只有32位,而RIPng均为128位。②报文长度不同。RIP对报文的长度有限制,规定每个报文最多只能携带25个RTE,而RIPng对报文长度、RTE的数目都不作规定,报文的长度与发送接口设置的IPv6MTU有关。③报文格式。与RIP一样,RIPng报文也是由首部(header)和多个路由表项(RTE)组成。如下图所示。与RIP不同的是,在RIPng里有两类RTE,分别是:下一跳RTE:位于一组具有相同下一跳的“IPv6前缀RTE”的前面,它定义了下一跳的IPv6地址,其格式如下所示。下一跳RTE格式IPv6前缀RTE:位于某个“下一跳RTE”的后面。同一个“下一跳RTE”的后面可以有多个不同的“IPv6前缀RTE”。它描述了RIPng路由表中的目的IPv6地址、路由标记、前缀长度以及度量值。IPv6前缀RTE的格式如下图所示。42 IPv6前缀RTE格式(2)报文的发送方式方面RIPv2可以根据用户配置采用广播或组播方式来周期性地发送路由信息;RIPng使用组播方式周期性地发送路由信息。(3)安全认证方面RIPng自身不提供认证功能,而是通过使用IPv6提供的安全机制来保证自身报文的合法性。因此,RIPv2报文中的认证RTE在RIPng报文中被取消。习题5:根据题意,需要为RIP软件构造一个RIP选路表数据结构:structripst{shortrr_family;shortrr_mbz;charrr_addr[12];longrr_metric;}};当一个更新RIP报文到达时,RIP软件通过riprecv()接收到RIP通告报文后首先对RIP维护选路表进行更新(若选路表未被初始化则先初始化),更新选路表后通知rtadd()函数以更新路由表。具体实现时需要修改riprecv()函数第46行,增加RIP选路维护函数ripadd(),并在ripadd()函数中调用rtadd()函数以实现路由表更新。ripadd()函数实现流程可以参考rtadd()函数的处理流程。01:/*riprecv.c文件---*/02:#include03:#include04:#include......44:netmask(mask,rp->rr_addr);45:rr_metric=rp->rr_metric;46:ripadd(rp->rr_addr,mask,gw,rr_metric,ifnum,RIPRTTL);习题6:链路状态路由选择协议又称为最短路径优先协议,目的是映射互联42 网络的拓扑结构,每个链路状态路由器提供关于它邻居的拓扑结构的信息。链路状态路由选择协议基于Dijkstra的最短路径优先(SPF)算法,网络中的路由器并不向邻居传递“路由表项”,而是通告给邻居一些链路状态。链路状态路由选择协议使用称为代价的方法,而不是使用跳。其实现基本步骤如下:(1)每台路由器与它的邻居之间建立联系,这种联系称为邻接关系。(2)每台路由器向每个邻居发送链路状态通告LSA。对每台路由器链路都会生成一个LSA,LSA用于标识这条链路、链路状态、路由器接口到链路的代价度量值以及链路所连接的所有邻居。每个邻居在收到通告后将依次向它的邻居转发(泛洪)这些通告。(3)每台路由器要在数据库中保存一份它所收到的LSA的备份,如果所有路由器工作正常,那么它们的链路状态数据库应该相同。(4)完整的拓扑数据库,也叫做链路状态数据库,Dijkstra算法使用它对网络图进行计算,得出到每台路由器的最短路径;接着链路状态协议对链路状态数据库进行查询,找到每台路由器所连接的子网,并把这些信息输入到路由表中。现在常用于IP路由的链路状态路由协议有最短路径优先(OSPF)以及中间系统到中间系统(IS-IS)两种。42 第8章习题解答参考思路习题1:参考教材8.3节及图8-2进行解答。习题2:XINU操作系统通过upqs数组维护了所有已绑定端口的UDP相关信息的队列。udp_in()函数中有多路分解的相关代码,upalloc用于在upqs分配一个项,和多路复用相关。可以查看相关代码。习题3:该题考查阅读代码的能力。UDP中的检验和与差错控制有关。此外,当UDP报文段找不到主机中的相应进程时会向发送方发送ICMP端口不可达报文。习题4:Traceroute原理和ICMP有关。UNIX以及类UNIX环境下的Traceroute利用了UDP数据报来实现其功能。参考教材8.5节。习题5:XINU操作系统源代码udp目录下的代码是UDP的实现,dgram目录下的代码是为用户层提供的UDP的伪设备驱动实现。42 第9章习题解答参考思路习题1:参考教材9.3节及图9-2、图9-3、图9-4进行解答。习题2:该状态图在讲述TCP原理的相关书籍中均有介绍,可以参考教材9.6.2节及图9-6,并结合TCP/IP原理有关书籍中的相关章节进行解答。习题3:TCP的差错控制体现在以下几方面:序号、确认重传机制,相关字段为tcb_suna、tcb_snext、tcb_slast、tcb_rexmt、tcb_rexmtcount,体现在tcpxmit()、tcpackit()、tcpacked()、tcprexmt()等函数中;拥塞控制,相关字段有tcb_cwnd、tcb_ssthresh、tcb_srt、tcb_rtde,体现在tcpxmit()、tcprtt()、tcprexmt()等函数中;流量控制机制,相关字段有tcb_swindow、tcb_lwseq、tcb_lwack、tcb_smss,体现在tcpxmit()、tcprexmt()、tcpsndlen()、tcpswindow()、tcprwindow()等函数中。习题4:首先注册一个定时输出事件来设定TCP定时器。TCP定时器进程周期性地遍历所有定时结构,回收到期的定时结构并通过系统内部的消息通道向TCP输出进程发送一个消息。消息进入系统内部消息队列。输出模块从系统内部消息队列中获取消息,对消息进行分类并根据当前状态进行相应的消息处理。消息驱动是围绕消息的产生与处理展开的,并依靠消息循环机制来实现。它能够很好地处理不确定时延的操作。习题5:参考教材9.8.1节和9.8.2节。习题6:紧急指针的数据应该尽快提交给用户,可以考虑设置紧急数据缓冲区,当收到带有紧急指针的TCP报文时,将紧急数据复制到紧急数据缓冲区。习题7:该题可以查找有关TCP序号环绕的相关资料。42 习题8:TCP是面向连接的,所以就会出现只连接不传送数据的“半开放连接”,服务器当然要检测到这种连接并且在某些情况下释放这种连接,这就是保活定时器的作用。数据传送中,在收到对端确认时,设置保活定时器。若双方长时间无数据交互,即保活定时器超时,则发送保活报文,对端对保活报文进行确认,在收到对端确认后重置保活定时器,释放连接。习题9:XINU操作系统源代码tcpd目录下主要包括tcpinit(),tcpmopen(),tcpserver(),tcpcon(),tcpread(),tcpwrite()函数,用于完成XINU操作系统中的TCP套接字实现。其中,tcpinit()函数完成TCP设备初始化;Tcpmopen()函数用于TCP设备打开;Tcpserver()函数完成TCP被动打开;Tcpcon()函数用于初始化TCP连接;Tcpread()函数用于TCP读数据;Tcpwrite()函数用于TCP写数据。 42 第10章习题解答参考思路习题1:该题考查对UNIX内核关于BSD套接字的数据结构structsocket和structsock的掌握程度。socket结构描述了高层文件套接字接口的属性,sock结构描述了底层网络套接字接口的属性。每一个sock结构都存放着一个与底层协议相关的信息。习题2:该题考查对套接字接口工作原理的理解程度。在UNIX内核中,一个套接字描述了高层文件套接字接口和低层网络套接字接口的一对结构。底层网络套接字数据结构定义了与具体协议相对应的不同的操作表。例如,对PF_INET协议栈来说,SOCK_STREAM类型的文件套接字操作表为inet_stream_ops,网络套接字操作表为tcp_prot;报文类型(SOCK_DGRAM)的文件套接字操作表为inet_dgram_ops,网络套接字操作表为udp_prot;原始套接字类型(SOCK_RAW)的文件套接字操作表为inet_dgram_ops,网络套接字操作表为raw_prot。习题3:该题考查对IPv4的套接字地址结构和IPv6的套接字地址结构。在UNIX系统中,IPv4的套接字地址结构为structsockaddr_in,IPv6的套接字地址结构为structsockaddr_in6。通用套接字地址结构为structsockaddr。地址转换参考代码如下:voidsock_set_addr(structsockaddr*sa,socklen_tsalen,constvoid*addr){switch(sa->sa_family){caseAF_INET:{structsockaddr_in*sin=(structsockaddr_in*)sa;memcpy(&sin->sin_addr,addr,sizeof(structin_addr));return;}caseAF_INET6:{structsockaddr_in6*sin6=(structsockaddr_in6*)sa;memcpy(&sin6->sin6_addr,addr,sizeof(structin6_addr));return;}}}42 习题4:该题考查对原始套接字编程的掌握程度。通过SOCK_RAW类型套接字可以操作数据链路层协议。例如,使用SOCK_RAW套接字发送以太网ARP请求帧。习题5:该题考查对套接字I/O编程的掌握程度,有多种实现套接字I/O超时方法,例如:SIGALRM中断处理;select方法;设置SO_SNDTIMEO/SO_RCVTIMEO选项;等等。习题6:该题考查对TCP带外数据传输知识的掌握程度。TCP有带外数据的概念(有时称为加速数据)。带外数据在排队等待发送的普通数据之前发送,但带外数据是映射到现有的连接中的,而不是另外建立一个新的连接。发送方可以调用带紧急数据标志的发送函数,例如send(fd,’a’,1,MSG_OOB)。接收方可以调用recv()、recvfrom()或者recvmsg()函数,并指定MSG_OOB标志来接收数据。习题7:该题考查对getaddrinfo()函数用法的掌握程度。getaddrinfo()函数处理名称到地址以及服务到端口的转换,它返回一个addrinfo的结构指针,该addrinfo结构可被套接口函数直接使用。getaddrinfo()函数是协议无关的,既可用于IPv4,也可用于IPv6,把协议相关性安全隐藏在函数内部。可以在多线程中使用。习题8:该题考查对多线程编程知识的掌握程度。实现多线程之间数据共享的方法有多种:互斥锁、条件变量、信号量等。42 第11章习题解答参考思路习题1:该题主要考查对套接字选项SO_REUSEADDR的理解程度。定义套接字选项SO_REUSEADDR可以防止在服务器出现意外时,由于地址和端口没有释放而不能重新使用。SO_REUSEADDR可以用于在以下4种情况。(1)当有一个有着相同本地地址和端口的socket1处于TIME_WAIT状态,而所启动的程序的socket2要占用该地址和端口时,就要用到该选项。(2)SO_REUSEADDR允许在同一个端口上启动同一服务器的多个实例(多个进程)。但每个实例绑定的IP地址是不能相同的。在有多块网卡或用IPAlias技术的机器可能出现这种情况。 (3)SO_REUSEADDR允许单个进程绑定相同的端口到多个套接字上,但每个套接字绑定的IP地址不同。这和(2)相似。(4)SO_REUSEADDR允许完全相同的地址和端口重复绑定。但这只用于UDP的多播,不能用于TCP。习题2:该题主要考查对套接字选项SO_DONTROUTE的理解程度。SO_DONTROUTE选项规定发出的报文将旁路底层协议的正常路由机制。也就是,SO_DONTROUTE选项将导致报文不经由网关(绕过路由表)发送,而是发往直接相连的主机。该选项经常由路由守护进程(routed和gated)来旁路路由表(路由表不正确的情况下),强制一个报文从某个特定接口发出。习题3:该题主要考查对套接字数据传输的理解程度。在不读出数据的情况下,有三种方法知道一个套接字接收队列中可读的数据:(1)如果在没有数据可读时还有其他事情要做,则可以使用非阻塞I/O。(2)如果想检查一下数据而使数据仍留在接收队列中,则可以使用MSG_PEEK标志。如果想这样做,但又不能肯定是否有数据可读,则可以把这个标志和非阻塞接口相结合,或与MSG_DONTWAIT标志结合使用。(3)一些实现支持ioctl()函数的FIONREAD命令。ioctl()函数的第三个参数是一个指向整数的指针,在该整数中返回的值是套接字接收队列中数据的字节数。习题4:该题考查对UDP工作原理的理解程度。在BSD环境下,当一个到来的UDP数据报长度大于应用程序缓冲区时,recvmsg设置MSG_TRUNC标志,不同实现有不同的处理方式:(1)LINUX系统丢掉超出的字节并给应用程序返回标志;(2)42 Solaris系统丢掉超出的字节但不通知应用程序;(3)UNIXSVR4保留超出的字节并在随后这个套接字上的读操作中返回这些数据。习题5:利用UDP套接字实现广播通信的参考代码如下:#include#include#include#include#include#include#include#include#include#include#include#include#include#definePORT7773#defineMAXDATASIZE256intmain(intargc,char*argv[]){intsocket_fd;structsockaddr_inmy_addr,user_addr;charbuf[MAXDATASIZE];intbroadcast=1;socklen_tsize;charmy_ip[12];my_addr.sin_family=AF_INET;my_addr.sin_port=htons(PORT);my_addr.sin_addr.s_addr=inet_addr("255.255.255.255");bzero(&(my_addr.sin_zero),8);user_addr.sin_family=AF_INET;user_addr.sin_port=htons(PORT);user_addr.sin_addr.s_addr=htonl(INADDR_ANY);bzero(&(user_addr.sin_zero),8);if((socket_fd=(socket(AF_INET,SOCK_DGRAM,0)))==-1){42 perror("socket");exit(1);}setsockopt(socket_fd,SOL_SOCKET,SO_BROADCAST,&broadcast,sizeof(broadcast));if((bind(socket_fd,(structsockaddr*)&user_addr,sizeof(structsockaddr)))==-1){perror("bind");exit(1);}strcpy(buf,"Hello,I"monline!");sendto(socket_fd,buf,strlen(buf),0,(structsockaddr)&my_addr,sizeof(my_addr));size=sizeof(user_addr);recvfrom(socket_fd,buf,MAXDATASIZE,0,(structsockaddr*)&user_addr,&size);strcpy(my_ip,inet_ntoa(user_addr.sin_addr));while(1){bzero(buf,sizeof(buf));size=sizeof(user_addr);recvfrom(socket_fd,buf,MAXDATASIZE,0,(structsockaddr*)&user_addr,&size);bzero(buf,sizeof(buf));if(strcmp(buf,"I"moffline,bye!")==0){strcpy(buf,"ok,Iknow,bye!");}else{strcpy(buf,"send,Hello,Igetyou!");sleep(1);}if((sendto(socket_fd,buf,strlen(buf),0,(structsockaddr*)&user_addr,sizeof(user_addr)))==-1){perror("sendto");}}return0;}42 习题6:该题考查对UDP并发通信的理解程度。有两种实现UDP服务器并发通信的方法。第一种是简单的UDP服务器,它读入一个客户请求,发送应答,然后与这个客户就无关了。对这种情况,服务器可以调用fork()函数的一个子进程去处理请求,子进程可以从内存映像中获得客户地址,从而将处理结果返回给客户。第二种是与客户交换多个数据报的UDP服务器。问题是客户只知道服务器的众所周知的端口。客户发送请求的第一个数据报到达这个端口,服务器又如何能区分这是那个客户的后续数据报还是新请求呢?这种问题的典型解决方法是让服务器给每个客户创建一个新的套接字,绑定一个临时端口到那个套接字,并且对该客户的所有回答都使用这个套接口。同时要求客户查看服务器第一个应答中的端口号,并且向那个端口发送请求的后续数据报。习题7:以原始套接字构造伪造的IP数据报为例,参考步骤如下:第一步,创建socket。intsock_fd=socket(AF_INET,SOCK_RAW,IPPROTO_RAW);第二步,调整IP_HDRINCL开关。intbs=1;setsockopt(sock_fd,IPPROTO_IP,IP_HDRINCL,(char*)&bs,sizeof(bs));第三步,绑定真实网口。该网口必须是本机存在的,可以使用INADDR_ANY。structsockaddr_inmy_addr;my_addr.sin_family=AF_INET;my_addr.sin_port=  htons(31000);my_addr.sin_addr.s_addr=inet_addr("192.168.0.1");/*本机网口所绑定的IP地址*/bzero(&(my_addr.sin_zero),8);bind(sock_fd,(structsockaddr*)&my_addr,sizeof(structsockaddr);第四步,使用sendto()函数发送完整数据报(不含链路层头)。假设psz_msg是缓冲区地址,msg_len是数据报长。structsockaddr_intheir_addr;bzero(&(their_addr.sin_zero),8);their_addr.sin_family=AF_INET;their_addr.sin_port=htons(30010);their_addr.sin_addr.s_addr=inet_addr("192.168.0.2");/*真实可达的IP地址*/if(sendto(sock_fd,psz_msg,msg_len,0,(structsockaddr*)&their_addr,sizeof(structsockaddr))==-1){printf("senderror,%s,len%d.n",frameCount+1,strerror(errno),msg_len);/*usleep(10);如果连续发送大批量数据报,那么需要用usleep()函数来减慢发送速度;42 否则sendto()函数会丢失数据报,这可能是由于在将数据填往socket发送缓冲区时溢出而造成的*/}第五步:最后卸载sock。close(sockFd);习题8:可以利用poll或select方法来检测TCP套接字连接状态,timeout时间设置为0。42'