当前位置:文档之家› 简单文件传送协议(TFTP)的C语言实现

简单文件传送协议(TFTP)的C语言实现

?----------------------- 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

相关主题