实验三

目 录

计算机网络实验报告 1

一、 实验目的 3

二、 实验内容 3

0. 准备工作 3

1. 完善socket客户机 5

2. 字符串回送服务器(TCP迭代) 8

3. 字符串回送服务器(TCP并发) 16

三、 实验结果分析 21

四、 实验小结与感想 22

实验目的

  1. 掌握TCP和UDP协议主要特点
  2. 理解socket的基本概念和工作原理,编程实现socket通信

实验内容

准备工作

  1. 使用终端命令行安装本地echo服务,监听TCP 7号端口
  2. `sudo apt update`

  1. `sudo apt install xinetd`

  1. 修改/etc/xinetd.d/echo文件中的内容,将disable设置为no

  1. `sudo service xinetd reload`

  1. `netstat -an|grep :7`

  1. 输入`telnet localhost 7`,连接本机echo服务器,输入任意文本,观察响应

  1. 输入`Ctrl+]`结束echo服务,输入`close`或`quit`退出telnet

完善socket客户机

  1. 开启两个终端窗口,分别编译、运行server_example.c和client_example.c,观察它们实现的功能
  2. 编译、运行server_example.c和client_example.c

  1. 观察它们实现的功能

首先运行server_example,终端不会显示任何内容,处于server端处于监听状态

然后再运行client_example,client端会向server端发送一个字符串 “Hello Network!”,并在终端上显示出该条发送的信息

server端接到该条信息后在终端上显示接收到这条信息

server端同时向client端发送一条字符串 “Hello Network!”,并在终端上显示发送了这条字符串的信息

client端收到server端发送过来的字符串,在终端上输出接收到该条字符串的信息

倘若将运行这两个程序的顺序颠倒一下,就会产生以下效果:

可以看到,client端发送一个字符串后无法收到来自server端的回复;而如果之后再打开server端,也无法将二者建立起连接,无论client端再发送多少次,server端也无法接收到来自client端的信息。

  1. 按以下要求,修改范例client_example.c,实现类似telnet连接echo服务器的效果
  2. 为所有socket函数调用添加错误处理代码
  3. 范例中服务器地址和端口是固定值,请将它们改成允许用户以命令行参数形式输入
  4. 范例中客户机发送的是固定文本“Hello Network!”,请改成允许用户输入字符串,按回车发送
  5. 实现循环,直至客户机输入“bye”退出

实验结果:

终端使用命令`./server 12345`指定端口号12345运行服务端:

使用命令`./client localhost 12345`指定地址和端口号运行客户端:

可以看到,server端变为接收状态:

向客户机输入字符串:

几乎同一时刻,可以看到server端接收到信息并向终端输出信息

在client端输入“bye”后,进程终止,并向终端输出“Bye!”以示结束:

字符串回送服务器(TCP迭代)

  1. 客户机和服务器的运行情况

第一个客户机正常收发:

第2个客户机输入后,无响应:

第1个客户机bye之后,第2个客户机马上收到回复并进入循环:

按Ctrl+c终止服务器程序:

  1. 设置服务器listen( )的backlog为0或1,同时打开多个终端窗口、让4个客户机连接服务器,使用netstat命令观察socket状态变化,对照TCP连接状态图(5-28/5-29/5-30),说明变化过程以及backlog与客户机完成队列的数量关系
  2. backlog设置为0:

让4个客户机连接服务器,使用netstat命令观察socket状态变化:

`netstat -anp | grep 12345`

  1. 只打开server端

  1. 打开1个client端

  1. 打开2个client端

  1. 打开3个client端

  1. 打开4个client端

同时打开4个客户端,有2个可以被建立并正常运行

  1. backlog设置为1:

让4个客户机连接服务器,使用netstat命令观察socket状态变化:

`netstat -anp | grep 12345`

  1. 只打开server端

  1. 打开1个client端

  1. 打开2个client端

  1. 打开3个client端

  1. 打开4个client端

同时打开4个客户端,有3个可以被建立并正常运行

  1. 端口为什么要进行字节顺序转换,不转换会有什么情况?

代码改为不进行字节顺序转换:

注意,这里想要看到效果,只能修改server端和client端其中一个的代码,如果两个都修改的话,相当于两个都错了,从结果来看反而是对的了。

这里我修改了server.c代码:

编译、运行修改后的代码,观察结果如下:

可以看到,client端被拒绝连接了。

我们可以通过netstat找到答案:

检测不到任何2000的端口运行。

使用`netstat -anplt`命令,我们可以看到并不是我们想要监听的2000端口,而是53255,这也是为什么client端想要向2000端口发送被拒绝的原因了。

原因:在进行网络编程时,由于网络的字节顺序和主机的字节顺序可能存在不同,我们需要对它们进行转换以统一“格式”。如果没有正确对两者进行转换,从而导致两方产生了不同的解释,就会出现bug。

  1. 试验客户机也像服务器一样bind固定端口,看看结果如何。

先在终端运行命令:`sudo sysctl net.ipv4.tcp_timestamps=0`

运行客户机,连接服务器:

观察到运行正常。

客户机主动退出、再次运行客户机,netstat观察:

使用`sudo sysctl net.ipv4.tcp_timestamps=0`命令的作用是关闭端口复用;当3000端口处于TIME_WAIT状态时,再次绑定该端口打开client端,系统不会将3000端口分配给client端使用,而会随机分配一个空闲状态的端口供client端使用。

字符串回送服务器(TCP并发)

  1. 客户机和服务器运行时的截图

  1. 服务器accept之后会返回一个用于传输数据的socket,调用fork()会使父子进程同时拥有此socket描述符,父进程分支中是否需要关闭该socket?

答:需要关闭。如果不关闭,在退出客户端后,还有多个网络处于CLOSE_WAIT状态。

代码测试:

  1. 关闭

  1. 查看运行是否正常

可以看到,运行正常。

  1. netstat观察多个客户机退出后的连接状态

使用`netstat -anp|grep 12345`命令:

连接三个client端后使用该命令可以查看到如下内容:

关闭第一个client端后使用该命令可以查看到如下内容:

关闭第二个client端后使用该命令可以查看到如下内容:

关闭最后一个client端后使用该命令可以查看到如下内容:

  1. 不关闭

然后重新编译。

  1. 查看运行是否正常

可以看到,不关闭此socket描述符,仍可以正常运行。

  1. netstat观察多个客户机退出后的连接状态

使用`netstat -anp|grep 12345`命令:

连接三个client端后使用该命令可以查看到如下内容:

关闭第一个client端后使用该命令可以查看到如下内容:

关闭第二个client端后使用该命令可以查看到如下内容:

关闭最后一个client端后使用该命令可以查看到如下内容:

若此时,再打开一个client端,使用该命令可以看到如下内容:

之前因为和三个client端连接的server子进程,在三个client端关闭了之后,一直处于CLOSE_WAIT状态,如果之后再次有client端接入进来,处于CLOSE_WAIT状态的网络也不会自动和client端连接,而是会新开一个ESTABLISHED。由于端口数量是有限的,可以预见,随着时间的推移,client端的开关次数增多,总会到达一个时候,表面上没有多余的端口可供client端连接了,实际上所有的端口都处于CLOSE_WAIT状态,这会极大程度地造成资源利用不充分。

综上,父进程分支中应该关闭这个用于传输数据的socket。

BUG修复:

一开始完成这个程序的时候其实还有一个bug,在第一次获取client端的ip时总是会获取到0.0.0.0,而之后连接进来的client端却都是正确的。

为了修复这个bug我花费了不少工夫。原因其实是在定义变量client_len的时候,没有给它赋初始值,我翻阅man手册查看accept的函数原型发现,如果要使用addrlen就必须要给它赋一个初始大小:

于是对代码进行修改,对client_len赋初始值:

然后再编译、运行,发现BUG得到了解决,可以正确得到第一个client端的地址和端口号:

实验结果分析

本次实验我顺利地完成了实验内容的必须要求,并且在不懈的努力下成功修复了一个很隐蔽的bug,并完成了选做的内容。

运行./server 2000 和 ./client localhost 2000可以顺利观察到TCP的迭代情况,当第一个client端输入 “bye”终止后,server端才能接受到第二个client端发送过来的数据。

运行./server1 2000 和 ./client localhost 2000可以顺利观察到TCP的并发情况,多个client端可以几乎同时向server端发送数据,无需等待排在前面的client端终止。

实验小结与感想

本次实验完成较为顺利。

因为我提前预习了,所以在实验课前就已经将程序大致完成了。在实验课上,我着重做了选做的内容,并在洪老师的指导下,大致明白了转换字节顺序的重要性以及bind函数能实现的功能。

 

本实验完整代码链接:https://gitee.com/i-rong/huaiyuyao/tree/master/cn/homework/3

posted @ 2023-03-30 23:17  Lumen3ever  阅读(282)  评论(0)    收藏  举报