//使用recv()函数接收经过的IP包 /* 第一个参数接收操作所用的套接字描述符; 第二个参数接收缓冲区的地址; 第三个参数接收缓冲区的大小,也就是所要接收的字节数; 第四个参数是一个附加标志,如果对所发送的数据没特殊要求,直接设为0。 因为IP数据包的最大长度是65535B,因此缓冲区的大小不能小于65535B。 设置缓冲区后,可利用循环来反复监听接收IP包,用recv()函数实现接收功能 **/ int size = recv(sock,buffer,BUFFER_SIZE,0); IP_HEADER ip=*(IP_HEADER *)buffer; cout<<\ cout<<\版本:\ cout<<\头部长度:\ cout<<\服务类型:Priority\ \ cout<<\总长度:\ cout<<\标识符:\ cout<<\标志位:\\ cout<<\片偏移:\ cout<<\生存周期:\ cout<<\协议:Protocol\ cout<<\头部校验和:\ cout<<\原地址:\ cout<<\目的IP地址:\ outfile<<\ outfile<<\版本:\ outfile<<\头部长度:\ outfile<<\服务类型:Priority\Service\ outfile<<\总长度:\ outfile<<\标识符:\ outfile<<\标志位:\\ outfile<<\片偏移:\ outfile<<\生存周期:\ outfile<<\协议:Protocol\ outfile<<\头部校验和:\ outfile<<\原地址:\
16 / 37
}
outfile<<\目的IP地址:\
}
::closesocket(sock); ::WSACleanup(); return 0;
实验三 FTP客户机 课程设计目的:
? 设计并实现一个FTP客户机的程序,了解FTP服务的基本原理和FTP协议的工作过程,加深对TCP协议和流式套接字编程的理解。
课程设计要求:
? 要求在FTP客户机程序中至少实现目录变换(CWD),列表(LIST),下载(RETR)功能。 ? 以命令行形式运行: ftpclient serveraddress
? 输出内容:FTP客户机与服务器交互过程中的命令与应答信息。
课程设计分析:
? 使用流式套接字 ? 参考RFC959
17 / 37
程序设计原理:
简介
FTP 文件传输协议,提供的是文件传输服务,C/S的方式进行网络访问,端口号一般为21。
支持密名访问的FTP都包含一个Anonymous用户,使用Anonymous登陆后只需输入e-mail即可登录。
用户登录后即可进行文件操作,本次课程设计我采用的是命令行方式的操作界面。对文件的操作需要使用指定命令(如列目录使用LIST)。 根据命令性质的不同对两类命令进行分析 1) 简单的无数据传输方式:
以CWD为例:
client->setCommand(\
命令格式规范化,例如要进入根目录下的tools目录就可使命令规范化为”CWD
请注意这里的字符串一个字符都不能差,更详细的说明参见RFC959文档,内有详细说明。
client->sendCommand();
sendCommand()即为发送规范化的命令。 client->receiveCommand(); 接收应答信息并分离应答码。 2) 复杂的有数据传输方式:
带有数据传输的文件需要建立数据套接字来传输,建立数据套接字的方式有两种,主动模式,被动模式。 简单的介绍下这两种模式:
A. 主动模式:主动模式是客户端发送数据连接请求,并告知服务器端已开
放的正在监听的端口,由服务器发送连接请求,建立连接。
B. 被动模式:被动模式是发送被动连接模式命令,服务器由应答码返回可
用的ip与端口信息,通过对应答码的分析找到端口号,用客户端连接正在监听的服务器端口。
由于主动模式的局限性,即在有NAT转换时无法将信息送至正确的位置,因为经过NAT技术转换的端口号代表的是一台计算机的内网编号,而不是计算机网络端口的端口号,所以无法正确送达。也因此本程序采用被动模式。 当数据链路建立完成后,数据链路需要进行初始化,数据链路有两种常用的模式,ASCII方式和二进制方式,下面介绍下这两种传输方式:
A. ASCII方式:ASCII方式传输数据时,数据以一个字符一个字符为单位
传输,这种方式一般使用在文本文件等一些使用ASCII码来储存信息的文件传输中ASCII方式同样用于文件目录列表的传输。使用ASCII方式传输二进制文件,例如可执行文件,图片文件,动画文件,等等,会使文件损坏。
B. 二进制方式:二进制文件传输数据时,数据以比特为单位传输,到达客
18 / 37
户端后,由操作系统将其合并转换。使用2进制可以传输绝大多数的文件,但是二进制方式也有其不可避免的局限性,二进制传输文件要求服务器和客户端使用相同类型的操作系统,如果不同类型下载的文件无法使用,但可以使用二进制方式传输到另一台拥有相同操作系统的机器上,文件可以使用。所以二进制方式传输同样不适用于网络传输。
确定了传输类型后,就可以创建套接字,格式化命令,发送命令,并接受信息和应答码与两不同的SOCKET上。 char* filename = new char[512]; memset(filename,NULL,512);
/************************************ PASV方式传输
************************************/ client->setCommand(\client->sendCommand(); client->receiveCommand();
/************************************ 分析应答码取端口号
************************************/ client->getPort();
/************************************ 设置传输类型初始化数据套接字
************************************/ client->setCommand(\client->sendCommand(); client->receiveCommand(); client->interlizeDataSocket();
cout<<\cin>>filename;
memset(temp,NULL,512);
/************************************ 格式化路径信息Direct
************************************/ memcpy(temp,Direct,strlen(Direct)); memcpy(temp+strlen(Direct),\
memcpy(temp+strlen(Direct)+strlen(\/************************************ 格式化命令RETR
************************************/ client->setCommand(\client->sendCommand(); client->receiveCommand();
/************************************ 接收数据信息和应答信息
************************************/
19 / 37
client->receiveData(filename); client->receiveCommand();
/************************************ 清理内存
************************************/ delete filename;
下面对封装的这个FTP类进行剖析。 封装类分析
FTP客户端主要有一个类ftpClient来完成封装,具体的成员包括 成员函数: Private:
ftpClient::getCode
前提条件:cmdBuffer中尚存有PASV命令的应答信息 用于获取cmdBuffer中的端口信息并进行转换 ftpClient::interlizeDataSocket 初始化控制连接 Public:
ftpClient::~ftpClient
ftpClient的析构函数,包含释放缓冲区的任务。 ftpClient::ftpClient
ftpClient的构造函数,初始化缓冲区,调用interlizeSocket()函数初始化控制连接。
ftpClient::receiveCommand 接收控制信息应答码 ftpClient::receiveData 接收数据链路信息 ftpClient::receiveList 接收列表信息
ftpClient::sendCommand 发送命令
ftpClient::sendData 发送数据用于上载 ftpClient::setCommand ftpClient::setCommand
setCommand分别对应单指令和双指令代码进行重载,将命令格式化并储存在cmdBuffer中。 成员变量:
ftpClient::buffer 数据缓冲
ftpClient::cmdBuffer 应答信息缓冲
ftpClient::command
20 / 37