?----------------------- Page 1-----------------------
第12卷第3期 +,,。 V01.12 No.3
20lo年3月 鬣钎雾喾 Mar.2010
doi:lO.3969/j.issn.1563--4795.2010.03.018
简单文件传送协议ITFTP)的C语言实现
谢永悠
(西南交通大学信息科学与技术学院,四川 成都610031)
摘 要:’rFTP(简单文件传送协议)是TCP/TP协议族中用来在客户机与服务器之间进行简单
文件传输的协议。文中给出了在visual C++6.0开发平台上,用C语言按照聊协议在服务器
跟多客户端之间进行文件传榆的实现方法。该方法可以传输超过32MB的文件。
关键词:Tnm:server;client;超时重传
0引言 1 系统所要解决的问题
利用TFrP简单文件传输协议可以实现Ⅱ’rP 在唧文件的传输过程中,通常都要求有一
server与Tn’P client之间的文件传输.包括多客 定的容错能力。大部分的错误都会导致连接中
户的下载和上传请求。 断。假如错误由一个错误的数据包引起.则这个
如果客户端发送的是下载请求,那么,服务 包将不被确认,也不会被重新发送,因此,另一
器将根据客户端发过来的报文。解析出文件的路 方将无法接收到。如果错误包丢失,则将使用超
径和文件名,并且根据解析出来的文件名,开始 时机制。一般的错误主要是由三种情况引起:一
读文件并构造报文。然后再经过获取客户端发过 是不能满足请求;二是收到的数据包内容错误,
来的端口号,把DATA报文发送给客户端;如果 而这种错误又不能由延时或重发解释;三是对需
客户端发送的是上传请求,那么,服务器端也必 要资源的访问丢失(如硬盘满)。
须解析出文件名及要保存的路径。若满足条件, 椰只在一种情况下不中断连接,这种情况
则发送ACK给客户端以确认已经接受客户端的请 是源端口不正确,在这种情况下。指示错误的包
求,然后等待客户的DATA报文。客户端接收 会被发送到源机。事实上,Ⅱ耶协议的限制很
ACK后,就可开始发送数据报文。服务器开始解 多.这些都是为了实现起来比较方便而进行的。
析报文。并将其写到指定的路径及文件中。
任何传输请求都来自一个读取或写入文件的
2系统实现
请求,这个请求也是连接请求。如果服务器批准
2.1输入模块
此请求,则服务器将打开连接,数据以定长512
字节传输。每个数据包一般都含有一块数据,服 输入模块可实现输入命令的接收、判断和容
务器发出下一个数据包以前.必须得到客户对上 错处理,负责接收客户的输入信息并解析命令。
一个数据包的确认。如果一个数据包的大小小于 并将接收到的命令存入结构体glptKeeplnput,以
512字节。则表示传输结束。如果数据包在传输 供其他模块读取。
过程中丢失.则发出方会在超时后重新传输最后 函数qiptlnputProgress(&glptKeeplnput)用于
一个未被确认的数据包。通信的双方都是数据的 接收客户的输入。再把客户输入的命令、文件
发出者与接收者,其中一方传输数据接收应答, 名、主机的ip地址保存到结构体中。其实现代码
另一方发出应答接收数据。 如下:
typedef struct input p存放最终输入
命令的结构体木/
收稿日期:2009—10—21
删.eada.cn 2010.3电子元嚣件左用 55
万方数据
----------------------- Page 2-----------------------
第12卷第3期 电手元嚣件盔用 V01.12 No.3
2010年3月 Electronic Component&DeviceApplications Mar.2010
{ INT8 bufer【SRV—PACKET_LENGTH】;
INT8 protoc【10];产协议木, 接收数据。定义数据块大小为512字节:
INT8mode[5】; 产传输类型宰/ INT32 alen=sizeof(Client);
INT8 ipaddr【20】;p目的主机牛/ recvfrom(sock,(clear木)&RecvBuff,5 12,0,
INT8 option【4】; ,幸操作类型木/ (struct sockaddr木)&Client,&alen);
INT8 source[301;p源文件名字宰/ 获得客户端请求操作码:
INT8 dest【50】; 产操作后文件存 locakBuff_opcode=ntobs(Buff.opcode);
储地木, 打开本地文件:
pfile=fopen(path,”rb”);path为解析的路
径与文件名。
2.2 1'】51甲server模块
读本地文件:
本模块可调用构造报文模块,解析多个客户 Fread(Buff,sizeof(char),5 1 2,pfile);
的请求,同时负责处理客户端请求,包括下载与 填充TFTP首部:
上传。检查包号是否正确,并实现容错功能,从 sendBuff.opcode=hton(3);操作码为3说明
而保证传输文件的正确性及可靠性。本模块的主 是数据包:
要步骤的结构代码如下: sendBuff.tu—block=htons(time);块号;
初始化Winsock库,注意要加上ws2—32.1ib库 mencpy(&(sendBuff.th—data),FileBuff,i);数
文件: 据:
WSADTA WSAData; 送文件给客户端f接收文件或数据与之类
WSAStartup(MAKEWORD(2,2),&WSADa— 似1:
ta); Sendto(sock, (char书) &SendBuff,i+4,
创建套接字: MSG_DONTROUTE,(struct sockaddr宰)&Client,
SOCKETsListen=INVALID——SOCKET; sizeof(Client));
sListen=socket(AF_INET,SOCK DGRAM, 定义解析报文的函数.其中解析客户的请求
o);严这里用UDP协议,所以协议类型为 代码如下:
SOCK—DGRAMo卑| void gtpGetWRrq (char母type,char
初始化本地主机地址信息: file name口,char buff口)
Struct sockaddr_in ServerAddr; {
ServerAddr.sin_family=AF_INET; inti,j;
ServerAddr.sin_addr=inet_addr(MYIp);/ *type=(buff[1】==’13 9"r7:,w7;判断
*MYIp为ip地址;,Ic/ buff的前两位,如为1是读,如为2是写。
ServerAddr.sin_port=htons(69); / file_name D】=buff[i】;第三字节开始
木哪服务器默认端口号:乖/ 就是文件名。
绑定套接字: l
Bind(sock,(struct sockaddr幸)&ServerAddr, 解析接收到的buff中数据的代码如下:
sizeof(struct sockaddr_in)); void gtpGetData(unsigned int木BKNUM,char
初始化远程地址信息: buff_data口,char buff口,char str口)
Struct sockaddr_in Client; {
Client.sin_family=AF_INET; inti,j;
Client.sin_port=htons(INADDR_ANY); 宰BKNUM=getInt(buff+2);
Client.sin_addr.s_addr=inet_addr(RemoteIp)
; for(i--4,j=0;j<5 12;i++’j++)
收到报文的缓存: {
56 电子元器件主硐 2010.3 www.ecda.cn
万方数据
----------------------- Page 3-----------------------
第12卷第3期 V01.12 No.3
2010年3月 最谛参巍 Mar.2010
把第四字节开始的512字节的值赋 ReleaseMutex(g_signalfoflisO扩释放木/
给buff data的数据存储缓冲区中: return index;女II果成功就返回链表头
l )
buff_data IJ】=们’;—~ g_signalfoflist=CreateMutex(0,0,”sin-
) glelist”)扩保护链表指针水,
解析接收ACK的包号: if(sgnApplicationOperator(fileName,type))
void gtpGetAck(unsigned int宰BKNUM, {/宰判断是否得到信号量,如果得到创建线
charbuff口) 程,Ic/
{ _beginthread(srvThreadFuction,0,(void木)
*BKNUM=getInt(buff+2)C/buff的第三 &threaddata)扩创建线程水/
第四位是ACK块号。 )
l 2.3 Tn[1PClient模块实现
gtpGetError 0解析接收到的ERROR的类
客户端的主要功能是解析输入模块的输人命
型。
令,读取相应的文件或写文件,并把根据解析得
void gtpGetError(charErrMsg【】,char buff 到的相应报文发到服务器端。邢客户端的实现
13);
主要体现在对T兀’P各种数据包首部字段的分析及
l
填充,填充(读,写)请求字段可以得到图1所示
缓冲区的信息存储gljErrMsgI约缓冲区
的读/写请求数据包格式。
中。
) 2bytes string 1 byte string 1 byte
解析接收客户请求的文件路径:
void iptGettPath(char file_name【】,char OpcodeIFilenamel 0IModel0
path口)
图1读/写请求数据包格式
用多线程处理多客户的请求。若有客户请
求,就建立一个线程与该客户相对应,并把该线 定义一个stpSetWRrq 0函数来填充请求字
程是读或写文件请求
的信息记录在一个链表中。 段,把客户的请求保存到buff发送到服务器。其
一般要求用信号量来保护链表.要求读写链表时 代码如下:
要遍历链表。对链表操作的时候还要求保护。每 Int stpSetWRrq (char type,int mode,char
一个线程负责与一个客户端通信。当客户端完成 水filename,char*buffer,int size)
上传或下载后。线程结束。多线程主要是在接收 (
到一个客户的请求后。要由服务器记录下客户的 intij;
端口号与IP,并把这些信息保存起来,以建立与 char str[4];
客户端的通信,直到线程结束。其主要的实现代 buff【o】0=,07;
码如下: buff【1】=(type=='r3 7’1 7:27;//分析是写还
/卑申请链表指针对文件进行操作乖/ 是读。(下载还是上传)
INT32 sgnApplicationOperator(INT8书曲le- for(i=2,j=0;file_name D]!=弋07;i++'j++)
Name,eonst INT32 opcode) /,把文件名保存到发送的buff中
f memset(&str,0,4);
WaitForSingleObject( g_signalforlist, itoa(cCLIENTID,str,lO);
SNG—WAITE_TIME);胪申请信号量,Ic/ for(i:.o;i<3;i++)
产如果文件没有被写,那么记录链表,如果 //模式存储
没有该文件,或者是读,也记录链表,-c/ }
伽聊.ecd瞳cn 2010.3电子元器件盔用 57
万方数据
----------------------- Page 4-----------------------
第12卷第3期 电手元嚣件主用 V01.12 No.3
2010年3月 Electronic Component&Device Applications MaI".2010
图2所示是ACK包格式。其填充地段函数se. l
tACK 0:的代码如下: 客户端实现定时重传功能.就是在客户端下
载时发送下载请求报文或收到报文后发送ACK报
2bytes2bytes
文。若到设定时间还没有收到服务器端的下一个
数据报文,则重新发送该请求或者ACK报文。当
Block#
Opeode
客户端请求上传时,发送的上传请求或收到数据
图2 ACK包格式 报文后。若到设定时间还没有收到服务器端的
ACK报文,也重新发送请求上传或数据报文。其
int stpS etAck (unsigned short num,char
主要实现代码如下:
木buffer,int size)
nResult=select(0,&set,NULL,NULL,&timeout);
{
产主要是用select实现超时重传木/
char str[4】;
if(nResult==01
inti;
{
buff【0】=,07;//bu£f第一字节为0
if(overtimeNum>01
buff【1】=,47;//第二字节4是ACK的opcode;
{
//设置ACK的包号在buff+2中
p输出超时重传的次数木/
memset(&str,0,4);
pfinff(”连接超时,正在进行第%d次重
for(i=0;i<3;i++)
传、rI”,16一overtimeNum);
l ,木构造data报文,构造ACK报文或请求
2 bytes的Block报文块号最多为65535块,每
报文串/
块报文512字节,因此,最多可以传输32 MB文
sendto(sConneet,packetBuff,recvNum+
件。本文在构造报文时,Block的值可以循环,
4,
当Block大于65535时,Block将重新置0,故可实
0,(struct sockaddr木)&serverAddr,len);
现文件无限大的文件传输。其DATA包的格式如 严从新发送构造的报文发送拳/
图3所示。 overtimeNum一一:严重传次数减一事/
DATA包的格式
continue;
。
2 bytes 2 bytes nbytes )
else/宰超时次数超过重传次数宰/
OpcodeI Block#IData {
pfinff(”连接超时!\Il”)扩断开连接:Ic/
图3 DA’rA包的格式 fclose(pfile)扩关闭文件牛/
break;
下面是为DATA包格式的填充gtaGetData(:);
函数代码: )
}
void stpSetDatavoid stpSetData(INT32 block-
Num,INT8 dataBuff口,INT8 packetBuff口,INT32
3 结束语
dataNum)
{ 本文按照聊协议给出了实现多客户端与服
//设置bu£f的前两位buff【o】=0,buff[1】=3 务器的文件传输方法。该方法能够传输大于32
为opcode。 MB的文件。其中客户端与思科的唧服务器兼
//设置buⅡ【2,3】为DATA包号。 容。在传输过程中,当网络出现故障时,还可实
//其他的为512字节的数据。 现超时重传。并具有一定的容错能力。
58 电予元嚣件主用 2010.3删眦ecd以cn
万方数据
----------------------- Page 5-----------------------
简单文件传送协议(TFTP)的C语言实现
作者: 谢永悠
作者单位: 西南交通大学信息科学与技术学院,四川,成都,610031
刊名:
电子元器件应用
英文刊名: ELECTRONIC COMPONENT & DEVICE APPLICATIONS
年,卷(期): 2010,12(3)
本文链接:/Periodical_dzyqjyy201003018.aspx