#include<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#define ERR_MSG(msg) do{\
printf("line %d\n",__LINE__);\
perror(msg);\
}while(0)
#define PORT 69 //端口号的网络字节序 1024~49151
#define N 516
#define IP "192.168.101.33"
int download(int sfd,struct sockaddr_in sin);
int upload(int sfd,struct sockaddr_in sin);
int main(int argc, const char *argv[])
{
//创建报式套接字
int sfd = socket(AF_INET,SOCK_DGRAM,0);
if(sfd < 0)
{
ERR_MSG("socket");
return -1;
}
printf("报式套接字创建成功 sfd = %d __%d__\n",sfd,__LINE__);
//填充服务器地址信息结构体
struct sockaddr_in sin ;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(IP);
char c;
while(1)
{
printf("********************\n");
printf("********1.上传******\n");
printf("********2.下载******\n");
printf("********3.退出******\n");
printf("********************\n");
scanf("%c",&c);
getchar();
switch(c)
{
case '1':
upload(sfd,sin);
case '2':
download(sfd,sin);
case '3':
return 0;
default:
printf("输入不合法\n");
}
}
close(sfd);
return 0;
}
int download(int sfd,struct sockaddr_in sin)
{
char inputname[20] = "";
printf("input download name \n");
fgets(inputname,20,stdin);
inputname[strlen(inputname)-1] = 0;
char buf[N] = "";
int size = sprintf(buf,"%c%c%s%c%s%c",0,1,inputname,0,"octet",0);//组包
if(sendto(sfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("sendto");
return -1;
}
int fd;
ssize_t recv_len;
unsigned short num = 1;//块编号初始化是1
socklen_t addrlen = sizeof(sin);
int flag = 0;
while(1)
{
bzero(buf,sizeof(buf));
recv_len = recvfrom(sfd,buf,N,0,(struct sockaddr*)&sin,&addrlen);
if(recv_len < 0)
{
ERR_MSG("recvfrom");
return -1;
}
if(3 == buf[1])//如果是数据包开始下载
{
if(0 == flag)//防治文件重复打开,所以只在第一次打开后面flag修改后就不打开
{
fd = open(inputname,O_WRONLY|O_CREAT|O_TRUNC,0664);
if(fd < 0)
{
ERR_MSG("open");
return -1;
}
flag = 1;
}
if(htons(num) == *(unsigned short *)(buf+2))
{
if(write(fd,buf+4,recv_len-4) < 0)
{
ERR_MSG("write");
break;
}
//回复ack
buf[1] = 4;
if(sendto(sfd,buf,4,0,(struct sockaddr*)&sin,sizeof(sin)) < 0)
{
ERR_MSG("sendto");
}
if(recv_len < 512+2+2)
{
printf("文件下载完毕\n");
break;
}
num ++;
}
}
else if(5 == buf[1])
{
printf("错误包\n");
break;
}
}
return 0;
}
int upload(int sfd,struct sockaddr_in sin)
{
char upname[20] = "";
printf("input upname\n");
fgets(upname,20,stdin);
upname[strlen(upname)-1] = 0;
int fd = (upname,O_RDONLY);
if(fd < 0)
{
ERR_MSG("fd");
return -1;
}
//1.发出上传请求包sendto,等待服务器回应
char buf[N] = "";
int size = sprintf(buf,"%c%c%s%c%s%c",0,2,upname,0,"octet",0);
if(sendto(sfd,buf,size, 0,(struct sockaddr*)&sin,sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
//2.循环读取数据发送数据,当读取到的res为0时说明上传结束
int recv_len;
unsigned short num = 0;
socklen_t addrlen = sizeof(sin);
while(1)
{
bzero(buf,sizeof(buf));
recv_len = recvfrom(sfd,buf,sizeof(buf), 0, (struct sockaddr*)&sin,&addrlen);
if(recv_len)
{
ERR_MSG("recvfrom");
return -1;
}
if(4 == buf[1])//收到服务器允许
{
if(htons(num) == *(unsigned short*)(buf+2))//防止快编号重复
{
buf[1] = 3;
num ++;
*(unsigned short*)(buf+2) = htons(num);//更新块编号
//开始读取
int res = read(fd,buf+4,N-4);
if(res < 0)
{
ERR_MSG("read");
return -1;
}
else if(0 == res)
{
printf("传输完毕\n");
break;
}
//开始发送(读取到的字节数res + 操作码2+块编号2)
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;
}
【网络编程】TFTP
于 2023-05-18 08:42:03 首次发布