技术开发 频道

自己动手编写嵌入式Bootloader之二

  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;

  }
0
相关文章