/*************************************************
* 这个程序实现了TFTP客户端,在Linux上编译通过并 *
* 正常使用。使用socket编程,具体的TFTP协议由于 *
* 篇幅太长,这里没有列出来,可以参考网址: *
* http://www.faqs.org/rfcs/rfc1350.html *
************************************************/
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <string.h>
//下面定义的几个宏是TFTP协议的操作码,参考协议
#define OP_RRQ 1 //读请求
#define OP_WRQ 2 //写请求
#define OP_DATA 3 //数据包
#define OP_ACK 4 //确认包
#define OP_ERROR 5 //错误信息
//TFTP错误信息编码
static char *error_string[] = {
"Not defined error!", //0
"File not found!", //1
"Access denied!", //2
"Disk full or allocation exceeded!", //3
"Illegal TFTP operation!", //4
"Unknown transfer ID!", //5
"File already exists!", //6
"No such user!" //7
};
int main(int argc, char **argv)
{
int socketFD; //socket描述符
struct sockaddr_in tftpServer, tftpClient; //自身和服务器的socket信息
FILE *fp; //用于保存本地文件
char buffer[516]; //网络通信用的buffer
int addrlen;
int lastPacket, currentPacket; //包计数
unsigned short *s_ptr;
char *c_ptr;
int tmp;
int ret;
if (argc < 3)
{
//使用方式
printf("Usage: %s <ipaddr> <filename>\n", argv[0]);
return 0;
}
//创建socket
socketFD = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == socketFD)
{
printf("Can not create socket!\n");
return 0;
}
//客户端自身信息
bzero(&tftpClient, sizeof (tftpClient));
tftpClient.sin_family = AF_INET;
tftpClient.sin_addr.s_addr = INADDR_ANY;
tftpClient.sin_port = htons(0);
//绑定socket到本机IP地址
tmp = bind(socketFD, (struct sockaddr *)&tftpClient, sizeof (tftpClient));
if (0 != tmp)
{
printf("Can not bind socket!\n");
goto error_1;
}
//服务器信息
bzero(&tftpServer, sizeof (tftpServer));
addrlen = sizeof (tftpServer);
tftpServer.sin_family = AF_INET;
tftpServer.sin_addr.s_addr = inet_addr(argv[1]);
tftpServer.sin_port = htons(69); //TFTP服务端口
s_ptr = (unsigned short *)&buffer[0];
*s_ptr = htons(OP_RRQ); //操作码
c_ptr = &buffer[2];
strcpy(&buffer[2], argv[2]); //远程文件名
c_ptr += strlen(argv[2]);
*c_ptr++ = 0;
strcpy(c_ptr, "netascii"); //模式
c_ptr += 8;
*c_ptr++ = 0;
//创建一个本地文件
fp = fopen(argv[2], "w");
if (NULL == fp)
{
printf("Can not create locale file!\n");
goto error_1;
}
lastPacket = 0;
while (1)
{
//发送请求或者回应包到服务器
tmp = sendto(socketFD, buffer, c_ptr - &buffer[0], 0,
(struct sockaddr *)&tftpServer, sizeof (tftpServer));
if (-1 == tmp)
{
printf("Can not send buffer!\n");
goto error_2;
return 0;
}
//从服务器获取数据包
tmp = recvfrom(socketFD, buffer, 516, 0, (struct sockaddr *)&tftpServer, &addrlen);
if (-1 == tmp)
{
printf("Receive data error!\n");
goto error_2;
return 0;
}
else
{
switch (ntohs(*s_ptr))
{
//数据包
case OP_DATA:
currentPacket = ntohs(*(s_ptr+1));
//验证包的顺序
if ((lastPacket + 1) != currentPacket)
{
printf("ERROR: packet error!\n");
goto error_2;
}
lastPacket = currentPacket;
tmp -= 4;
//写入数据到本地文件
ret = fwrite(&buffer[4], 1, tmp, fp);
if (tmp != ret)
{
printf("ERROR: write to local file error!\n");
goto error_2;
}
//如果等于512则不是最后一个包
if (512 != tmp)
{
goto read_ok;
}
//准备返回信息
*s_ptr = htons(OP_ACK);
*(s_ptr + 1) = htons(currentPacket);
c_ptr = &buffer[4];
break;
//服务器返回错误信息
case OP_ERROR:
//根据错误码,打印错误信息
printf("ERROR: %s\n", error_string[ntohs(*(s_ptr+1))]);
goto error_2;
break;
//发生其他未知错误
default:
printf("ERROR: Unknow error!\n");
goto error_2;
break;
}
}
}
read_ok:
fclose(fp);
close(socketFD);
return 0;
error_2:
fclose(fp);
unlink(argv[2]);
error_1:
close(socketFD);
return 0;
}
|