作业:
UDP tftp协议 上传下载文件
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#define ERR_MSG(msg) do{\
fprintf(stderr,"line=%d\n",__LINE__);\
perror(msg);\
}while(0)
#define SER_IP "192.168.31.161"
#define SER_PORT 69
int main(int argc, const char *argv[])
{
printf("**************\n");
printf("*****1下载****\n");
printf("*****2上传****\n");
printf("*****3退出****\n");
printf("**************\n");
char proto[]="octet";
unsigned short opt = 1;
scanf("%hu",&opt);
if(opt != 1 && opt != 2){
exit(0);
}
char file[32]="";
printf("请输入文件名:");
scanf("%s",file);
int packsize = 2+strlen(file)+1+strlen(proto)+1;
char buf[516]="";
memset(buf,0,sizeof(buf));
sprintf(buf,"%c%c%s%c%s%c",0,opt,file,0,proto,0);
//socket 创建新的套接字
int cfd = socket(AF_INET,SOCK_DGRAM,0);
if(cfd < 0){
ERR_MSG("socket");
return -1;
}
//printf("socket success cfd=%d\n",cfd);
//setsockopt 设置套接字选项
int reuse = 1;
if(setsockopt(cfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)) < 0){
ERR_MSG("setsockopt");
return -1;
}
//printf("setsockopt success\n");
//服务器地址信息结构体
struct sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_port=htons(SER_PORT);
sin.sin_addr.s_addr=inet_addr(SER_IP);
//发送请求给服务器
ssize_t res = sendto(cfd,buf,packsize,0,(struct sockaddr *)&sin,sizeof(sin));
if(res < 0){
ERR_MSG("sendto");
return -1;
}
//打开文件
int fd = 0;
if(opt == 1){
fd = open(file,O_WRONLY | O_CREAT | O_TRUNC,0775);
}else if(opt == 2){
fd = open(file,O_RDONLY);
}
if(fd < 0){
ERR_MSG("open");
return -1;
}
struct sockaddr_in temp;
socklen_t addrlen = sizeof(temp);
uint16_t blocknumber=0;
uint16_t lastblock=-1;
uint16_t optnumber=0;
while(1){
//获取服务器返回的信息,同时获取服务器地址信息
res = recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr *)&temp,&addrlen);
if(res < 0){
ERR_MSG("recvfrom");
return -1;
}
//printf("recvfrom [%s:%d]\n",inet_ntoa(temp.sin_addr),ntohs(temp.sin_port));
//printf("res=%ld\n",res);
//获取操作码
optnumber = ntohs(*(unsigned short *)buf);
//printf("optnumber=%d\n",optnumber);
//获取块编号
blocknumber = ntohs(*(unsigned short *)(buf+2));
//printf("blocknumber=%d\n",blocknumber);
if(opt == 1 && optnumber == 3){
//避免收到同样的数据包
if(lastblock == blocknumber){
continue;
}else{
lastblock = blocknumber;
}
//下载文件
if(write(fd,buf+4,res-4) < 0){
ERR_MSG("write");
return -1;
}
//数据长度小于512,则下载完毕
if(res-4 < 512){
printf("下载完成\n");
break;
}
//只需要修改操作码,取前4位即可
buf[1] = 4;
//发送ACK给服务器
res = sendto(cfd,buf,4,0,(struct sockaddr *)&temp,sizeof(temp));
if(res < 0){
ERR_MSG("sendto");
return -1;
}
}else if(opt == 2 && optnumber == 4){
//上传文件
memset(buf,0,sizeof(buf));
//操作码设置为3:数据包
optnumber = 3;
//块编号+1
blocknumber ++;
*(unsigned short *)buf = htons(optnumber);
*(unsigned short *)(buf+2) = htons(blocknumber);
//读取文件
res = read(fd,buf+4,sizeof(buf)-4);
if(res < 0){
ERR_MSG("read");
return -1;
}
//数据包长度=操作码+快编号+res
packsize = 4+res;
//printf("send pack blocknumber=%d packsize=%d\n",blocknumber,packsize);
//发送数据包给服务器
if(sendto(cfd,buf,packsize,0,(struct sockaddr *)&temp,sizeof(temp)) < 0){
ERR_MSG("sendto");
return -1;
}
if(res < sizeof(buf)-4){
printf("上传完成\n");
break;
}
}else{
//出错
if(optnumber == 5){
printf("出现错误 code=%d msg = %s\n",blocknumber,buf+4);
}
break;
}
}
close(fd);
close(cfd);
return 0;
}
ubuntu@ubuntu:20230309_net$ ./a.out
**************
*****1下载****
*****2上传****
*****3退出****
**************
2
请输入文件名:11_test_tcp_server.c
上传完成
ubuntu@ubuntu:20230309_net$ ./a.out
**************
*****1下载****
*****2上传****
*****3退出****
**************
1
请输入文件名:5.png
下载完成