#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
#define ERR_MSG(msg)do{\
fprintf(stderr,"line:%d\n",__LINE__);\
perror(msg);\
}while(0)
#define SER_PORT 69
#define SER_IP "192.168.31.111"
int download_cli(char filename[])
{
//创建报式套接字
int cfd= socket(AF_INET,SOCK_DGRAM,0);
if(cfd<0)
{
ERR_MSG("socket");
return -1;
}
printf("socket suceess=%d__%d__\n",cfd,__LINE__);
/*//填充服务器的地址信息结构体,绑定到套接字上 给下面的sendto使用*/
struct sockaddr_in sin;
sin.sin_family =AF_INET;
sin.sin_port =htons(SER_PORT);
sin.sin_addr.s_addr=inet_addr(SER_IP);//tftp服务器所在的ip
socklen_t addrlen=sizeof(sin);
//发送下载请求协议
char buf[516]={0};
short* p1=(short *)buf;
*p1=htons(1);//操作码
char* p2=buf+2;
strcpy(p2,filename);//要下载的文件名
char* p3=p2+strlen(p2);
*p3=0;
char* p4=p3+1;
strcpy(p4,"octet");//模式
char* p5=strlen(p4)+p4;
*p5=0;
int size=4+strlen(p2)+strlen(p4);
if(sendto(cfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG ("send");
return -1;
}
printf("发送成功\n");
//打开文件
int fd=open(filename,O_WRONLY|O_CREAT|O_TRUNC,0666);//只写
if(fd<0)
{
ERR_MSG("open");
return -1;
}
char buf2[128]="";
ssize_t res;
while(1){
//接受服务器发送的数据包、
//提取数据包中的数据(buf+4),另存到文件里
//给服务器回复ack,组ACK包,快编号与接收到的数据包的快编号一致
//如果收到回复的是错误包,则打印错误码+错误信息,退出
bzero(buf,sizeof(buf));
res =recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&addrlen);
if(res<0)
{
ERR_MSG("recvfrom");
return -1;
}
printf("len:%ld\n",res);
//判断操作码是否是ERR
if(buf[1]==0x03)
{
printf("正在写入文件\n");
//写入文件
char *p=buf+4;
write(fd,p,res-4);
/* {*/
/*ERR_MSG("write");*/
/*return -1;*/
/*}*/
}
//发送应答包
int size=sprintf(buf2,"%c%c%c%c",0x00,0x04,buf[2],buf[3]);
printf("端口号 %d\n",sin.sin_port);
if(sendto(cfd,buf2,size,0,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("snedto");
return -1;
}
if(res-4<512)
{
printf("下载成功\n");
return 0;
}
printf("[%s :%d]:%s\n",inet_ntoa(sin.sin_addr),ntohs(sin.sin_port),buf);
}
//关闭套接字
close(cfd);
close(fd);
}
int uploading_cli(char filename[])//上传
{
//创建报式套接字
int cfd= socket(AF_INET,SOCK_DGRAM,0);
if(cfd<0)
{
ERR_MSG("socket");
return -1;
}
printf("socket suceess=%d__%d__\n",cfd,__LINE__);
struct sockaddr_in sin;
sin.sin_family =AF_INET;
sin.sin_port =htons(SER_PORT);
sin.sin_addr.s_addr=inet_addr(SER_IP);//tftp服务器所在的ip
//向这服务器发送上传请求
char buf[516] = "";
memset(buf,0,sizeof(buf));
int size = sprintf(buf,"%c%c%s%c%s%c",0x00,0x02,filename,0,"octet",0);
if(sendto(cfd,buf,size,0,(struct sockaddr *)&sin,sizeof(sin)) < 0){
ERR_MSG("sendto");
return -1;
}
printf("sendto success\n");
struct sockaddr_in ser_sin;//ser_sin表示服务器回复时的IP和随机端口
socklen_t addrlen = sizeof(ser_sin);
ssize_t res = 0;
int size_ACK = 0;
int fd = open(filename,O_RDONLY,0666);
char r_buf[512] = "";
int read_res = 0;
char buf_ACK[4] = "";
while(1){
//接受服务器回复的ACK包
memset(buf_ACK,0,sizeof(buf_ACK));
memset(buf,0,sizeof(buf));
res = recvfrom(cfd,buf_ACK,sizeof(buf_ACK),0,(struct sockaddr *)&ser_sin,&addrlen);
if(res < 0){
ERR_MSG("recvfrom");
return -1;
}
printf("[%s:%d]\n",inet_ntoa(ser_sin.sin_addr),ntohs(ser_sin.sin_port));
printf("%d%d%d%d\n",buf_ACK[0],buf_ACK[1],buf_ACK[2],buf_ACK[3]);
//根据服务器回复的ACK包封装数据包
buf_ACK[1] = 0x03;
buf_ACK[3] = buf_ACK[3]+1;
buf[0] = buf_ACK[0];
buf[1] = buf_ACK[1];
buf[2] = buf_ACK[2];
buf[3] = buf_ACK[3];
char *p = buf+4;
read_res = read(fd,r_buf,sizeof(r_buf));
strcat(p,r_buf);
printf("buf =%ld\n",sizeof(buf));
if(sendto(cfd,buf,read_res+4,0,(struct sockaddr *)&ser_sin,sizeof(ser_sin)) < 0){
ERR_MSG("sendto");
return -1;
}
printf("sendto success\n");
if(read_res < 512){
break;
}
}
//关闭套接字
close(cfd);
}
int main (int argc, const char *argv[])
{
printf("--------请输入你要执行的操作-------\n");
printf("---------1下载--------------\n");
printf("----------2上传--------------\n");
int flag=0;
char filename[20]={0};
scanf("%d",&flag);
if(flag!=1&&flag!=2)
{
printf("请重新输入\n");
return -1;
}else if(flag==1)//下载
{
printf("请输入你要下载的文件\n");
scanf("%s",filename);
download_cli(filename); //封装下载函数
}else if(flag==2)//上传
{
printf("请输入你要上传的文件\n");
scanf("%s",filename);
uploading_cli(filename); //封装上传函数
}
return 0;
}
2023.4.21
最新推荐文章于 2024-09-22 14:42:14 发布
该代码实现了一个简单的TFTP客户端,支持通过UDP协议进行文件的下载和上传。在下载过程中,客户端创建UDP套接字,向服务器发送下载请求,接收并处理服务器的数据包,将数据写入文件。在上传过程中,客户端同样创建套接字,发送上传请求,读取本地文件内容并发送到服务器,等待服务器的ACK响应。
摘要由CSDN通过智能技术生成