type
status
date
slug
summary
tags
category
icon
password
计算机网络实验报告------基于Socket 的TCP 文件传输通信
一、实验任务:
1. 实现两个程序:服务器端(server)和客户端(client)
2. 客户端发送文件传输请求,服务器端将文件数据发送给客户端
o 两个程序均在命令行方式下运行
o 客户端在命令行指定服务器的IP 地址和文件名
o 为防止重名,客户端将收到的文件改名后保存在当前目录下
o 要求至少能传输一个文本文件和一个图片文件
o 在服务器端,应输出:
1. 客户端的IP 地址和端口号
2. 发送的文件数据总字节数
3. 必要的差错报告
o 在客户端,应输出:
1. 新文件名,输出总字节数
2. (如果发生差错的话)差错报告
二、实验内容:
用C++编写客户端和服务端,二者可以进行基于TCP 的文件传输通信。二者均
在命令行的方式下运行。客户端可以在命令行指定服务器的IP 地址和端口号,二
者即可建立连接。建立连接后,客户端和服务端之间可以进行通信,利用这一特点
不但可以进行文本和图片等文件的传输,也可以让服务器和客户端进行交互。
本工具采用类似于Linux SSH 命令行的形式,由客户端接受用户的输入,处理后,通过TCP 传输给服务器,服务器对客户端传输来的命令进行识别处理,并给出对应的回应。为了让服务器和客户端能够区分文件和命令(或回复消息),采用发送不同的包进行预告,然后进行数据传输,最后传输不同的结束标识包。所以每一
次数据传输,不管是命令还是文件数据,都是至少三个包:开始预告包,数据包,结束标识包。其中标识包都是2 字节大小。这样做利用了TCP 不会丢包且不会乱序的特性。
三、实验环境:
一台/多台Windows 系统的电脑。这些电脑可以处于同一局域网,或者作为服务器的电脑具有可访问的公网IP,多个客户端可以同时与服务器建立连接。
四、软件设计:
1. 程序流程:
使用C++创建了这样几个类:Server 类、ClientHandler 类、Client 类、ClientProcessor 类。在宏定义中可以选择是作为Server 编译还是Client 编译。当作为Server 启动时,将会创建Server 类和ClientHandler(客户处理)类的实例;当作为客户端启动时,则会创建Client 类和ClientProcessor(客户工作)类的实例用来与服务端建立连接。下面将分别展述服务端和客户端的具体细节。
下面为基础命令以及数据开头结尾格式的宏定义和常量
#define LS_COMMAND "1s"
#define DOWNLOAD_COMMAND "get"
#define UPLOAD_COMMAND "upload"
#define DELETE_COMMAND "rm"
#define CD_COMMAND "cd"
#define MKDIR_COMMAND "mkdir"
constexpr char start_cm_response_send[3] = "\x01\xfe"; constexpr char start_bytearr_response_send[3] = "\x04\xfe"; constexpr char end_command_send[3] = "\x02\xff";
constexpr char end_bytearr_response_send[3] = "\x05\xff";
#define upload_timeout 5
2. 服务端:
启动后,首先初始化一个Server 的实例,在这个实例中将初始化Socket 相关
资源,并循环检测是否有客户端请求与自己建立连接。如果有客户与服务端建立连
接,那么将为每个客户初始化一个ClientHandler 实例,用来处理服务器与这一客户的事务,其中完成连接建立后,每个ClientHandler 都将启动一个自己的线
程处理用户传来的数据,以此来支持多客户的连接。
连接建立后,为每个客户所分配的ClientHandler 实例将保存和处理该用户的所有请求。其中,权限为private 的变量有,SOCKET 类型的clnt_sock 用来保存和客户之间的socket 连接;fs::path 类型的变量currentPath 用来保存当前客户的工作目录。当ClientHandler 的构造函数被执行的时候,将会给二者写入值,其中currentPath 初始值会设置为当前服务端软件所在目录的路径,如果用户使用了cd 相关的命令进入新的文件夹,则会发生改变。
首次建立连接,服务端将返回当前工作目录的路径。随后,将在为每个客户分配
的handle 线程中,单独处理每个客户的请求。
服务端收到命令,将执行对应操作。所有命令格式如下,如果是不符合格式的命
令,或者命令参数有错误,分别会返回不同的错误码给客户端。
软件中所设计的命令如下:
Ls 命令:返回当前目录下所有文件/文件夹名称,并表明类型。如果是空目 录将返回Empty Dictionary。
Get 命令:发送文件给客户(客户下载)。服务端的异常处理:不存在的
文件或者请求下载了文件夹将返回不同的固定错误码,客户端可识别并提示
用户,同时不再请求文件传输。客户端的异常处理将在下一部分提出。
Rm 命令:从服务器上删除文件。后跟文件名,可以删除文件。如果输入 rm -r <目录名>,则可以删除指定目录。如果不加-r 而请求删除文件 夹,或者-r 删除文件,或者请求删除不存在的文件/目录,则分别会返回
不同的错误码给客户端。正常删除则返回成功删除的提示码给客户。
Upload 命令:上传文件到服务器,后跟文件名。在上传开始之前服务器会 检测是否已经有同名文件,如果命令是upload <文件名>则提示文件已存 在,不接收文件。如果命令为upload -o <文件名>,o 表示overwrite 则
正常接收文件并且会覆盖保存。收到消息后,服务器会返回消息提示已经准备接收文件,并根据宏定义设置超时。数据将分段被发送,每一段都会insert 到一个vector 中。全部接收完成后会收到来自客户端的提示码,将根据文件保存情况分别回复成功和失败。
Cd 命令:进入目录。使用cd / <目录> 可以根据绝对路径进入目录,使用 cd<目录名>则会进入当前文件夹下的目录,使用cd 不跟参数可以回到服 务器软件所在目录,使用cd ..可以返回上一级目录。异常处理包括目录不 存在以及是文件不是目录的情况,分别返回错误码给客户端。
Mkdir 命令:新建文件夹。使用Mkdir <目录名> 可以在当前工作目录下 新建一个文件夹,将根据能否成功创建返回不同的提示码给客户端。
其他所有未考虑到的错误,则会交给SEH 进行处理。
提示码表:
P// 当前工作目录(后加绝对路径)
FAIL_INVALID_COMMAND 不存在的命令
FAIL_INVALID_COMMAND_FORMAT 错误不合法的指令格式/参数SUCCESS_START_DOWNLOADING 可以开始下载
SUCCESS_RDY 传输文件状态就绪
SUCCESS_SEND_FINISH 传输文件发送结束
SUCCESS_CREATED 成功创建目录
SUCCESS_FINISH_RECV 成功接收完成
FAIL_NOT_EXIST 错误文件不存在
SUCCESS_DELETED_DIR 成功删除目录
FAIL_UNABLE_DELETE_DIR 无法删除目录
FAIL_NOT_A_DIR 错误要删除的是文件而不是目录
FAIL_EXISTED_NO_OVERWRITE 错误文件已存在并且没有覆盖参数FAIL_RECV_TIMEOUT 错误文件接收超时
FAIL_WRITING_FILE 错误文件无法写入到磁盘
FAIL_NO_DIR 无法cd,目录不存在/是文件
FAIL_UNABLE_CREATE_DIR 错误无法新建文件夹
3. 客户端:
客户端启动后,会自动检测当前目录下的config.ini 文件并尝试读取已保存的服务器IP 地址和端口号,尝试建立连接。如果读取失败,或者建立连接失败,则会让用户重新输入。如果用户在启动的时候同时按住ctrl,也会允许用户重新输入配置。连接成功后会覆盖保存到config.ini 中
当作为Client 启动时,将初始化一个Client 类实例,构造函数将保存服务端的ip 和端口号,作为两个private 变量。该实例则拥有一个ClientProcessor 类,通过createProcessor 函数启动Processor 的线程。Client 类自己有一个线程,用来处理用户输入和信息发送,另一个ClientProcessor 类的线程以阻塞方式等待TCP 消息的接收。
InputHandler:
作为输入的处理,只会检测用户的exit/upload/get 指令。
如果用户输入exit,则主动关闭套接字并退出。
如果用户输入upload,则会检测当前目录下有没有要上传的文件,如果没有则报
错,不发送任何指令给服务器。如果有的话,则发送指令给服务器。服务器根据服
务器目录是否有同名文件,再进行不同的操作。如果接收到错误码,则停止。
如果用户输入get,则会检测当前目录下是否已经存在文件。如果已经存在,给出提示,报错。如果用户输入的是get -o,则会把新下载的文件文件名加上“.L”
后,保存下来,避免覆盖。
下载开始时,会收到来自服务器的信息,提示文件传输开始并告知文件总大小,
传输一旦开始,将根据已经收到的字节数和总大小动态显示出进度条。
上传开始时,会将文件指针移动到末尾,从而获取整个文件的大小,传输一旦开
始,将会根据已上传字节数和总大小动态显示出进度条。
4. 软件原理图和软件流程图:
软件原理图:

软件流程图:

5. 通信协议:

每一条指令/文件分片,都将发送三个包,头和尾分别是宏定义的固定数值。因为TCP 传输特性,不会丢包,不会乱序,所以一定能保证收发的正常进行。其中对于文件传输开始/就绪/结束的提示码,也是作为指令内容以指令格式传输。
五、实验结果演示及分析:
1. 使用宏定义控制server 和client 的编译。
2. 服务器所处目录:

3. 运行服务器和客户端,客户端自动读取config.ini 中的配置并与服务器建 立连接。客户端的命令行提示目前服务器上所处目录。

4. 使用ls 命令列出当前服务器目录的所有文件。

5. 进行传输文本和图片的测试。
首先是文本传输。下载服务器目录的“测试文本下载内容abc.txt”,这个txt 的内容就是abc

从预览可见,下载文本的内容正确。
再下载图片内容。图片名称是测试传输图片.png

通过预览可见图片正确无误。
6. 文件大小显示。图片是68771 字节,合0.07MB。文本文件内容只有abc 三个字母,所以大小为3 字节。大小太小,换算成MB 四舍五入为0。


7. 在客户端输入mkdir 新建一个目录,ls 发现成功。

8.使用cd 命令进入目录

9.使用cd ..回到上一级目录。随着cd 命令命令提示符也会改变。

10.使用rm -r 命令删除目录

使用rm 命令删除test.txt,ls 发现成功

11.新建一个testrm 的文件夹,尝试用rm 删除,发现rm 不能删除文件夹,rm
- r 则可以删除。

12.尝试删除不存在的文件,服务器报错返回并打印

13.尝试用rm -r 命令删除文件,服务器报错。但是rm 命令可以删除。避免误
操作。

14.使用upload 命令上传沙丘.mp4,一个520mb 的电影,并观察进度。ls 发现
上传成功



15.再次upload 沙丘.mp4,发现同名文件会导致报错

16.使用upload -o 覆盖上传,成功


17.Get 命令下载,get 沙丘.mp4,同名文件已经存在,报错。

18.Get -o 命令下载,存放到后缀.L 的文件中



19.尝试下载不存在的文件,服务器报错返回并打印

20.同时启动两个客户端与服务器建立连接

21.分别cd 进入不同目录

22.分别ls,各自看到的是各自工作目录的内容,互不干扰。

六、实验总结和心得体会
1. 遇到的问题和解决过程:
服务器无法同时处理多个客户端的请求,等待TCP 包的时候会阻塞:为每个客户端新建一
个类的实例,并分别使用线程进行客户端的请求管理。
文件操作往往会涉及到同名文件/已存在文件等问题,因此添加了多个命令,让客户端/服务器根据命令后的参数作出不同的操作。
字符串处理:用户的命令往往不会输入的非常精确,可能遇到命令与参数之间出
现了多个空格的情况,则需要程序具有鲁棒性,能对用户不规范格式的正确命令作
出处理。
进度显示:采用\r 返回行首以覆盖之前的输出,std::cout.flush()确保输出立即
显示,以此达到让进度条每次在同一行更新,并显示出来新的进度。
2. 收获和提高
通过实践深入理解了传输层的TCP 协议和应用层的Socket 编程接口,以及它们在网络OSI 模型中的位置和作用
掌握了使用C/C++的API(比如socket(), bind(), listen(), accept(), connect(), send(), recv()等)来创建和管理TCP 连接
了解、掌握了线程的概念和对线程的使用,与《操作系统》的知识相结合,利用
线程并行同时处理多个客户端的事务。
掌握了对套接字编程中可能遇到的各种异常和错误情况进行处理,例如网络延
迟、断开连接等。
学会了设计一套适用于自己对应用开发要求的应用层协议,比如定义数据包的格
式、错误控制、同步机制等。
掌握如何将文件数据封装到协议数据包中,以及如何在接收端正确解码并重建文
件
学习了如何将软件分解为多个模块/对象,将一个功能模块封装成一个类,提升代码的可读性和可维护性,移植性。提高了使用调试工具的能力,编写并运行单元测
试和集成测试来确保程序的稳定性。
3. 本实验题目的设计和安排
完成实验所需要的时间相对较长。
七、附录
Main.cpp 根据宏定义进行不同版本的编译
Includes.h 包含文件
Server.h 服务器、客户端、处理器、工作器实现
- Author:Lynnette177
- URL:https://next.lynnette.uk/article/socket_experiment
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!
Relate Posts