网络编程与分层协议设计部分习题答案_第1页
网络编程与分层协议设计部分习题答案_第2页
网络编程与分层协议设计部分习题答案_第3页
网络编程与分层协议设计部分习题答案_第4页
网络编程与分层协议设计部分习题答案_第5页
已阅读5页,还剩4页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第2章 Linux下C编程环境习题4信号忽略是指不对该信号做出任何响应,就犹如该信号没有发生过一样,进程不对其做任何处理.信号阻塞是指若在某一时刻该信号发生了,此时内核不会将该信号发送给进程,而是将该信号保存起来,待到该函数解除对该信号的阻塞之后,再发送给该进程进行处理.在信号受到阻塞和进程解除对该信号的阻塞,但信号还未到达进程之间的时间段,进程可以任意改变对该信号的处理.当然如果到该进程结束之时如果还没有解除阻塞的话对则该信号的处理和忽略差不多习题5参见程序2_5.c习题6参见程序2_6.c习题7参见程序2_7.c和程序2_7_withmutex.c习题8此处第8行和第10行是可以交换的.当

2、将其交换以后,首先执行pthread_cond_signal(&mqlock_ready),此时对于另一个之前因条件不满足的线程thread2_run感知到条件变量的变化,开始获取锁.类似于调用pthread_mutex_lock(&mqlock),而此时线程thread1_run尚未解除锁,因此前一个线程thread2_run因无法获取锁而阻塞.随后线程thread1_run调用thread_mutex_unlock(&mqlock)释放锁.此时线程thread2_run得到锁,继续执行.当然对于其他的程序环境是否能够交换视具体环境而定.总得来说都对,不过都有缺陷(参看Unix环境高级编程P

3、697-11.4)。习题9参见程序2_9.c第3章 网络编程中常用的典型知识习题2参见程序3_2.c习题4struct len_and_flag unsigned short reserved:4, hlen:4, fin:1, syn:1, rst:1, psh:1, ack:1, urg:1, ece:1, cwr:1;习题5可以将链表节点置于宿主的固定位置,例如宿主的首个元素位置,然后通过将指向链表节点的指针强制类型转换为宿主节点的地址。习题6参见程序3_6.c习题7先分析为什么要使用双向链表,而不使用单链表:考虑普通的单链表,如下:struct nodestruct node*next

4、;假定a是某个单链表中的节点(struct node a),而b是刚定义的节点(struct node b)对于这样的链表,在指定的节点a后插入b很容易:b.next=a.next;a.next=&b;然而在指定的节点a前插入b却很麻烦;struct node head; 设head为头结点struct node *temp;temp=&head;while(temp-next!=&a)temp=temp-next;得到a的前一个节点temp-next=&b;b.next=&a;将b插入temp与a之间这就花费了一定的时间来搜索a的前一个节点,而linux要节省这部分时间.因此使用双向链表,这

5、就决定一个节点应该含有两个指针(一个向前,一个向后)然后分析为什么prev要是用二级指针,而不使用一级指针:Linux内核中,除了有通用了双向链表,还有通用的哈希链表。后者定义与前者有些不同。因为通常一个哈希表的表头要占用很大空间,而如果每个表头都用一个双向链表来做的话,就显得太浪费了。只用一个指针可以实现相同的功能,并且可以节省一半的表头存储空间.因此这就决定表头只含有一个指针(向前)哈希链表定义如下:struct hlist_head struct hlist_node *first; struct hlist_node struct hlist_node *next, *pprev;由于

6、表头结构体hlist_head与节点结构体hlist_node的定义不一样,将使得我们的pprev指针无法直接指向hlist_head,那我们可以让其指向first。而要指向first则pprev必定为二级指针.习题8参见程序3_8.c习题9为了让notifier_chain_register函数的实参能够向主程序返回该函数的处理后结果,该函数在设计上使用了2级指针。如果使用1级指针则无法通过实参带回改变后的结果。习题10在3.2中的程序可以按8比特进行加运算,这两种运算在逻辑上都是可行的,但是按8bit进行加运算,其加的次数差不多是按16bit的2倍,浪费了时间第4章 基础套接字习题1参见程

7、序4_1.c习题2参见程序4_2.c习题3参见程序4_3.c习题4参见程序4_4.c习题5参见程序4_5.c习题6fprintf(stdout,Server got connection from %s portnumber %un, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port);习题7第二个客户端可以连接到服务器,但是客户端程序将阻塞于read函数,直到第一个客户端请求结束,因为服务器程序为迭代的,而并非并行的,accept函数接收第一个连接请求后,没有fork出子进程来处理客户端请求,因此套接口一直被占用,直到客户

8、端关闭连接,服务器才执行close关闭连接,这是服务器端才能接收第二个新的连接。所以,若第二个客户端也发出请求,则必须等到第一个客户端请求处理完成,才能够获得服务器的应答。习题8注释掉程序第6566行后,3次使用4.2节的客户端访问此服务器后,退出客户端,执行ps -a命令后会出现如下现象; 3912 pts/0 00:00:00 4.3 3926 pts/0 00:00:00 4.3 4006 pts/0 00:00:00 4.3 这表明三个子进程仍作为僵死进程存在着。其所占的内存空间和其他资源没有被回收。这是由于注释掉6566行后,程序将不在捕捉SIGCHLD信号,而内核对此信号的默认动作

9、是忽略。由此可以看出,对并发服务器进行SIGCHLD信号捕捉是必要的,因为我们不愿看到僵死进程的出现。习题9用set follow-fork-mode child进入gdb调试工具后,在160行处设置断点后,如题所述,启动第一个客户端无法立刻得到回答,当启动第二个客户端时,可以看到,发出的请求立刻就得到了服务器的应答。这是由于,fork后,父进程关闭套接口描述符后,又返回到主循环执行,并阻塞于accept函数,等待接收新的连接请求。当第二个客户端发来连接请求后,服务器接收,并fork一个新的子进程来处理客户端的请求。这个子进程不会阻塞于write。因此,第二个客户端可以立刻得到服务器的应答。习

10、题10不启动服务器,而单独执行客户端程序时,会发现客户端程序永远阻塞于它的recvfrom调用。即键入格式化要求后不会收到任何应答。程序也不会退出。该错误由sendto引起,但是sendto本身却成功返回,该ICMP错误直到后来才返回,因此称其为异步错误。要使客户端能够发现该错误而退出,有两个方法。第一个方法是为recvfrom设置一个超时,调用函数alarm就可实现超时的设置。第二个方法是为UDP使用connect函数,使其成为连接的UDP套接口,但是使用connect后的UDP套接口,不能给输出操作指定目的ip和端口号,也就是说,我们不再使用sendto,而改用write或send。写到已

11、连接的UDP套接口上的内容都会自动发送到由connect函数指定的协议地址。同时,我们也不必使用recvfrom函数,而是改用read或recv。此时由内核为输入操作返回的数据报仅仅是那些来自connect所指定的协议地址的数据报。 因此我们可以将4.5的客户端程序作如下修改。首先注释掉8691行和107113行。然后在第85行后添加以下代码 if(connect(s,(struct sockaddr *)&adr_srvr,len_inet)=-1) printf(connect errorn); exit(1);然后再在114行前添加z=read(s,dgram,sizeof dgram)

12、;最后重新编译,运行(仍然不启动服务器)程序就不会永远阻塞于recvfrom调用,而是返回一个“Connection refused: recvfrom()”错误后退出。第5章 高级套接字习题1对套接口设置了SO_KEEPALIVE选项后,即使任何保活探测分段均无响应,我们也不能肯定对端主机已经崩溃。因而TCP可能会终止一个有效连接。考虑以下情况:某个中间路由崩溃15分钟是可能的,而这段时间又正好与主机的11分又15秒的保持存活探测周期完全重叠。因而它可能终止存活的连接。(主机的11分15秒的保持存活连接是指:第一个对端保持连接存活探测分段没有任何响应,源自Berkeley 的TCP将另外发送

13、8个探测分段,相隔75秒一个,试图得到响应。TCP在发出第一个探测分段后11分又15秒(75*9)内没有得到任何响应则放弃。)习题2从原理上说,可以使用SO_REUSEADDR选项来保证客户端立刻再次使用之前用过的端口。但是我们一般都不这么做,因为客户端不像服务器那样绑定在众所周知的端口,客户端发起连接时,一般由内核临时分配一个端口,因此两个不同的客户端使用两个不同的临时端口。习题4参见程序5_4.c习题5参见程序5_5.c习题6参见程序5_6.c第6章 网络协议习题2参见教材P194-P196介绍的函数append_data和msg_fragment的处理过程习题3参见教材P235-P237

14、介绍的函数msg_forward和append_frag_head的处理过程习题40xf9ca习题5在NAT环境下,通常不能使用FTP的主动模式,除非NAT服务进行专门的应用层网关的设置,允许外网访问内网习题7参见程序6_7c.c, 6_7s.c习题8对齐在4字节边界第7章 ICMP协议程序设计习题1由7.2的分析我们知道,ICMP的查询请求的应答消息就是之前发送的ICMP查询请求消息内容的直接拷贝,也就是说被ping的目的主机不对数据做任何处理。举个例子来说,假设发送时的id字段内容为a04c,sequence内容字段为0001以下为数据发送过程中在内存中的存储方式。icmp中的数: a04

15、c 0001小端机内存: 4ca0 0100发送到对端大端机内存: 4ca0 0100 (如果大端机要对数据处理,该数据则被解释为4ca0 0100,但是此处不做任何处理,直接回射)回射到主机 小端机内存: 4ca0 0100由此可见,回射回来的值在内存中的存储方式没有发生改变,其值仍是a04c 0001 因此程序的72行和73行对id和sequence字段进行赋值时没有进行字节序的转换。另外从校验和的角度来看,如果发送时进行了字节序转换,那么在接受时,为了保证得到正确的校验和,也必须再转换回来。所以,为了简单,不进行转换。习题2在填入检验和之前对checksum字段进行清零是必须的,因为若不

16、清零,则该字段中的值为一随机值,发送数据方在进行校验和计算的时候该值也被加入计算,因此其结果包含了未清零的那部分值。而接收方在接收到数据后进行校验和的计算时不会包含该随机值,因此其结果一般不会为负0,即一般都不会得到正确结果,除非清零前的值刚好为0。(其实最终checksum的结果为未清零前checksum字段值的反。但是该值是个随机值,所以将无法进行验证。)习题3为了很明显地看到该处代码被执行,添加如下代码/*156157行改为*/if(icmp-icmp_id!=pid) printf(icmp_id errnon); return -1;/*102行-103行*/if(handle_pkt() printf(handle_pkt errnon); continue;如果在程序执行过程中,有打印以上两句则可以验证return -1被执行首先运行系统自带的ping程序,比如执行如下命令:然后

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论