TFTP练习
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
//定义辅助宏
#define ERR_MSG(msg)do{\
fprintf(stderr,"__%d__:",__LINE__);\
perror(msg);\
}while(0)
#define PROT 69
#define IP "192.168.1.11"
int do_download(int sfd,struct sockaddr_in sin)
{
char buf[516] = ""; //初始化字符串
char filename[20]="";
printf("请输入要下载的文件名:");
scanf("%s",filename);
//组下载请求包
char* ptr = buf;
unsigned short* ptr1 = (unsigned short*)ptr;
*ptr1 = htons(1); //下载请求的网络字节序
char* ptr2 = ptr+2;
//char* ptr2 = ptr1+2;
strcpy(ptr2, filename);
char* ptr3 = ptr2+strlen(ptr2);
*ptr3 = 0;
char* ptr4 = ptr3+1;
strcpy(ptr4, "octet");
/*
char *ptr5=ptr4+1;
*ptr5=0;
*/
int size = 2+strlen(ptr2)+1+strlen(ptr4)+1;
printf("%d\n",size);
//发送下载请求包
if(sendto(sfd, buf, size, 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
printf("发送下载请求包成功\n");
//打开一个文件
//unsigned char str[600]="";
socklen_t addrlen=sizeof(sin);
unsigned short block_num = 0; //块编号 或者 错误码
int fd_w=open(filename,O_WRONLY|O_CREAT|O_TRUNC,0777);
while(1)
{
bzero(buf,sizeof(buf));
//循环接收服务器返回的数据包
ssize_t recv_len=recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&addrlen);
printf("size=%ld\n",recv_len);
if( recv_len < 0)
{
ERR_MSG("recvfrom");
return -1;
}
printf("buf[1]=%d\n",buf[1]);
//分析是数据包还是错误包
//如果是数据包,则将数据提取出来,数据在buf+4的位置上
//将提取出来的数据写入到文件中
//组ACK包,回复ACK给服务器
//直到数据内容的长度小于512个字节
//如果是错误包,
//则打印错误信息,退出
if( 3 == buf[1])
{
printf("此包为数据包\n");
//判断块编号
if(block_num == ntohs(*(unsigned short*)(buf+2)))
{
write(fd_w,buf+4,size-4);
//block_num++;
}
//发出应答包
buf[1]=4;
if(sendto(sfd, buf, 4, 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
//判断包的大小
if(size <516 )
{
printf("下载完毕\n");
break;
}
}
else if( 5 == buf[1])
{
printf("此包为错误包\n");
break;
}
}
return 0;
}
int do_upload(int sfd,struct sockaddr_in sin)
{
char buf[516] = ""; //初始化字符串
char filename[20]= "";
printf("请输入上传的文件名:");
scanf("%s",filename);
//fgets(filename,20,stdin);
getchar();
//filename[strlen(filename)-1]=0;
//判断文件是否存在
int fd_r=open(filename,O_RDONLY);
/*
printf("%d\n",errno);
printf("%d\n",ENOENT);
*/
if( fd_r <0)
{
if(errno == ENOENT)
{
printf(">>>文件不存在,请重新输入<<<\n");
return -2;
}
else
{
ERR_MSG("open");
return -2;
}
}
//组上传请求包
char* ptr = buf;
unsigned short* ptr1 = (unsigned short*)ptr;
*ptr1 = htons(2); //上传请求的网络字节序
char* ptr2 = ptr+2;
//char* ptr2 = ptr1+2;
strcpy(ptr2, filename);
char* ptr3 = ptr2+strlen(ptr2);
*ptr3 = 0;
char* ptr4 = ptr3+1;
strcpy(ptr4, "octet");
/*
char *ptr5=ptr4+1;
*ptr5=0;
*/
int size = 2+strlen(ptr2)+1+strlen(ptr4)+1;
//printf("%d\n",size);
//发送上传请求包
if(sendto(sfd, buf, size, 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
//printf("buf=%s\n",buf);
printf("发送上传请求包成功\n");
unsigned short block_num = 0; //块编号 或者 错误码
socklen_t addrlen=sizeof(sin);
while(1)
{
//循环接收服务器返回的数据包
bzero(buf,sizeof(buf));
int ret =recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&addrlen);
printf("ret=%d\n",size);
if( ret< 0)
{
ERR_MSG("recvfrom");
return -1;
}
printf("%d\n",buf[1]);
if( 4 == buf[1])
{
//判断块编号
if(block_num == ntohs(*(unsigned short*)(buf+2)))
{
//修改操作码
buf[1]=3;
//填充块编号
block_num++;
*(unsigned short*)(buf+2) = htons(block_num);
int res= read(fd_r,buf+4,sizeof(buf)-4);
printf("res=%d\n",res);
if(res <0 )
{
ERR_MSG("read");
return -1;
}
else if( 0 == res)
{
printf("%s文件上传完毕\n",filename);
break;
}
//block_num++;
//发出数据
if(sendto(sfd, buf, res+4, 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
}
else
{
printf("文件上传失败\n");
break;
}
}
else if( 5 == buf[1])
{
printf("此包为错误包\n");
break;
}
}
return 0;
}
效果图: