7. TFTP客户端的实现
tftp是一个很简单的文件传输协议,在传输层使用UDP协议。它有四种类型的包: 读请求RRQ包,DATA包,ACK包,ERROR包,每个包的前两个字节Opcode指定包的类型。(RRQ用于请求下载,WRQ用于请求上传,我们只用到RRQ)。
下载文件的过程分析如下: 客户端(A)从任意端口X向服务器(S)的端口69发送一个RRQ包,该包中指明了要求下载的文件名;服务器(S)找到该文件,读取文件内容组成DATA包,从任意端口Y向客户端(A)的端口X发送这个DATA包,第一个DATA包编号为1;从此以后,客户端确定使用端口X,服务器确定使用端口Y, 客户端向服务器发送ACK包,编号为1。服务器接到编号为1的ACK包之后,发送第二个DATA包,如此继续下去。
怎样判断传输结束呢? 按照规定,DATA包中的数据段为512字节, 如果小于512字节,表示这是最后一个DATA包,文件已传输完毕。
(R1) Host A requests to read
(R2) Server S sends data packet 1
(R3) Host A acknowledges data packet 1
注意在这个过程中端口的变化。开始RRQ是69,但是DATA和ACK都不是使用69,而是使用另外一个随机的端口。 服务器在接到RRQ后,不返回任何回应信息,直接发送第一个DATA包,而且DATA包编号从1开始,而不是从0开始。
编程时为简单起见,客户端使用了固定的端口号X=0x8DA4,服务器端口号Y是随机的,只能通过解析UDP数据包获得。
int tftp_download(unsigned char *addr, const char *filename)
{
int i=0;
unsigned short curblock = 1;
tftp_send_request( &TxBuf[256], filename );
msdelay(100);
while (1)
{
eth_rx();
if( pGtftp == NULL )
continue;
if ( ntohs(pGtftp->opcode) == TFTP_DATA )
{
if (ntohs(pGtftp->u.blocknum) == curblock)
{
printf("\r Current Block Number = %d", curblock);
for (i=0; i
{
*(addr++) = *(pGtftp->data+i);
}
tftp_send_ack( &TxBuf[256], curblock);
if (iGLen < TFTP_DATASIZE+4)
{
break;
}
curblock += 1;
}
else if (ntohs(pGtftp->u.blocknum) < curblock)
{
tftp_send_ack( &TxBuf[256], ntohs(pGtftp->u.blocknum));
}
else
{
printf("\n\rBlock Number Not Match.");
printf("Block Number = %d, curblock = %d\n", ntohs(pGtftp->u.blocknum), curblock);
}
}
else if ( ntohs(pGtftp->opcode) == TFTP_ERROR )
{
switch( ntohs(pGtftp->u.errcode) )
{
// 此处省略
}
}
else if ( ntohs(pGtftp->opcode) == TFTP_RRQ )
{}// 此处省略若干 else if
pGtftp = NULL;
iGLen = 0;
}
printf("\n\rTransfer complete: %d Bytes.\n\r", (curblock-1)*TFTP_DATASIZE + iGLen-4 );
return 0;
}
{
int i=0;
unsigned short curblock = 1;
tftp_send_request( &TxBuf[256], filename );
msdelay(100);
while (1)
{
eth_rx();
if( pGtftp == NULL )
continue;
if ( ntohs(pGtftp->opcode) == TFTP_DATA )
{
if (ntohs(pGtftp->u.blocknum) == curblock)
{
printf("\r Current Block Number = %d", curblock);
for (i=0; i
{
*(addr++) = *(pGtftp->data+i);
}
tftp_send_ack( &TxBuf[256], curblock);
if (iGLen < TFTP_DATASIZE+4)
{
break;
}
curblock += 1;
}
else if (ntohs(pGtftp->u.blocknum) < curblock)
{
tftp_send_ack( &TxBuf[256], ntohs(pGtftp->u.blocknum));
}
else
{
printf("\n\rBlock Number Not Match.");
printf("Block Number = %d, curblock = %d\n", ntohs(pGtftp->u.blocknum), curblock);
}
}
else if ( ntohs(pGtftp->opcode) == TFTP_ERROR )
{
switch( ntohs(pGtftp->u.errcode) )
{
// 此处省略
}
}
else if ( ntohs(pGtftp->opcode) == TFTP_RRQ )
{}// 此处省略若干 else if
pGtftp = NULL;
iGLen = 0;
}
printf("\n\rTransfer complete: %d Bytes.\n\r", (curblock-1)*TFTP_DATASIZE + iGLen-4 );
return 0;
}