效果图
下载
上传:
代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SER_PORT 8888
#define SER_IP "192.168.124.23" // "0.0.0.0"
// 下载函数声明
int do_download(int cfd,struct sockaddr_in sin);
// 上传函数声明
int do_upload(int cfd,struct sockaddr_in sin);
int main(int argc,char **argv)
{
if(argc < 2)
{
printf("please input ip\n");
return -1;
}
// 创建数据报式套接字s
int cfd = socket(AF_INET,SOCK_DGRAM,0); // SOCK_DGRAM 配置udp协议
if(cfd <0){
fprintf(stderr,"__%d__",__LINE__);
perror("socket");
return -1;
}
printf("sokect success __%d__\n",__LINE__);
// 允许端口快速重用
int reuse = 1;
if(setsockopt (cfd, SOL_SOCKET,SO_REUSEADDR, &reuse,sizeof(reuse))<0)
{
fprintf(stderr, "__%d__ ", __LINE__);
perror("setsockopt");
return -1;
}
printf("reuseaddr success __%d__\n", __LINE__);
// 绑定客户端自身的地址信息,非必须绑定
//若不绑定,则IP是当前运行环境的IP,端口号是随机端口(49152~65535)
// 服务器的地址信息结构体---》port :69
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(69);
sin.sin_addr.s_addr = inet_addr(argv[1]);
int loop =1;
int choose = 0;
while(loop)
{
printf("*******************\n");
printf("******1.下载*******\n");
printf("******2.上传*******\n");
printf("******3.退出*******\n");
printf("******************\n");
printf("请输入>>>");
scanf("%d",&choose);
while(getchar() != '\n'); // 循环吸收垃圾字符
switch(choose){
case 1:
do_download(cfd,sin);
break;
case 2:
do_upload(cfd,sin);
break;
case 3:
loop = 0;
break;
default:
printf("input err ,please re input\n");
}
}
// 关闭
close(cfd);
return 0;
}
int do_download(int cfd,struct sockaddr_in sin)
{
char filename[20] ="";
memset(filename,0,20);
printf("please input download filename:");
scanf("%s",filename);
while(getchar() != '\n'); // 循环吸收垃圾字符
// 组下载请求包
char require_buf[128] ="";
short* p1 = (short*)require_buf;
*p1 = htons(1); // 操作码 代码下载操作
char* p2 = require_buf+2;
strcpy(p2,filename); //文件名
char* p4 =p2+strlen(filename)+1;
strcpy(p4,"octet"); //模式
int len = 2+strlen(p2)+strlen(p4)+1;
//给服务器发送一个下载请求
if(sendto(cfd,require_buf,len,0,(struct sockaddr*)&sin,sizeof(sin)) < 0)
{
fprintf(stderr,"line:%d",__LINE__);
perror("sendto");
return -1;
}
printf("sendto download require success __%d__\n",__LINE__);
// 获取服务器的临时地址信息
struct sockaddr_in dstaddr;
int addrlen = sizeof(dstaddr);
// 收到的数据包
char data_buf[516];
while(1){
// 清空数据缓存区
memset(data_buf+4,0,512);
// 收数据包
int res = recvfrom(cfd,data_buf,sizeof(data_buf),0,(struct sockaddr*)&dstaddr,&addrlen); // 返回收到的字符
if(res<0){
fprintf(stderr,"__%d__",__LINE__);
perror("recvfrom");
return -1;
}
// 判断是否是对应的操作码
short *data_p = (short*)data_buf;
short data_flag = ntohs(*(data_p+1)); // 存放块编号,在发送ack时会用到
char *write_p = data_buf+4;
if(3 == ntohs(*data_p))
{
int fd = open(filename,O_WRONLY|O_CREAT|O_APPEND,0666);
if(fd <0){
printf("open err %s\n", strerror(errno));
return -23;
}
// 写入文件
int ret = write(fd, write_p,res-4);
if(ret < 0 || ret != res -4){
perror(" write err");
return -25;
}
// 关闭文件
ret = close(fd);
if(ret <0){
perror("close err");
return -25;;
}
}
else if(5 == ntohs(*data_p)){
// 编写当出现ERROR时的代码
printf("Error from server: %s\n", data_buf + 4);
return -1;
}
else{
printf("操作码 err \n");
return -1;
}
// 发ack
char ack[4]="";
short *ack_p = (short*)ack;
short *ack_p_block = (short*)(ack+2);
*ack_p = htons(4); // 操作码,代表是ack应答包
*ack_p_block = htons(data_flag); //和数据包对应的块编号
if(sendto(cfd,ack,4,0,(struct sockaddr*)&dstaddr,sizeof(dstaddr)) < 0)
{
fprintf(stderr,"__%d__",__LINE__);
perror("sendto");
return -1;
}
printf("sendto ack success%d\n",data_flag);
// 判断数据长度是否小于512,若小于512结束循环。
if((res-4) < 512)
break;// 说明已经发送完了最后一个包,现在需要退出。
}
return 0;
}
int do_upload(int cfd,struct sockaddr_in sin){
// 获取上传文件名
char filename[20] ="";
memset(filename,0,20);
printf("please input upload filename:");
scanf("%s",filename);
while(getchar() != '\n'); // 循环吸收垃圾字符
// 组上传请求包
char require_buf[128] ="";
short* p1 = (short*)require_buf;
*p1 = htons(2); // 操作码 代表上传操作
char* p2 = require_buf+2;
strcpy(p2,filename); //文件名
char* p4 =p2+strlen(filename)+1;
strcpy(p4,"octet"); //模式
int len = 2+strlen(p2)+strlen(p4)+1;
//给服务器发送一个上传请求
if(sendto(cfd,require_buf,len,0,(struct sockaddr*)&sin,sizeof(sin)) < 0)
{
fprintf(stderr,"line:%d",__LINE__);
perror("sendto");
return -1;
}
printf("sendto uplopad require success __%d__\n",__LINE__);
// 获取服务器的临时地址信息
struct sockaddr_in dstaddr;
int addrlen = sizeof(dstaddr);
// 收到的第一个应答包
char ack_buf[4];
// 清空数据缓存区
memset(ack_buf,0,4);
int res = recvfrom(cfd,ack_buf,sizeof(ack_buf),0,(struct sockaddr*)&dstaddr,&addrlen); // 返回收到的字符
if(res<0){
fprintf(stderr,"__%d__",__LINE__);
perror("recvfrom");
return -1;
}
short *ack_make_p = (short*)ack_buf;
short *ack_block_p = (short*)(ack_buf+2);
if(0 != ntohs(*ack_block_p) || ntohs(*ack_make_p) != 4)
{
printf("make:%d,block:%d",ntohs(*ack_make_p),ntohs(*ack_block_p));
printf("ack err");
return -1;
}
printf("recv ack_pag success make:%d block%d:__%d__\n",ntohs(*ack_make_p),ntohs(*ack_block_p),__LINE__);
//给服务器发数据包
//打开文件
int fd = open(filename, O_RDONLY);
if(fd<0){
perror("open err");
return -23;
}
char file_buf[516]; //来接收文件内容,组合上传数据包
//组合数据包
short block_number =0;
short *make_p = (short*)file_buf;
*make_p = htons(3); // 操作码
short *block_p =(short*)(file_buf+2);
// 循环获取文件内容,并将操作码和块编号组合
while(1){
*block_p = htons(block_number++); //块编号 ,从1开始,一直往后加
memset(file_buf+4,0,512);
int ret = read(fd, file_buf+4, 512);
if(ret <0) {
perror("read err");
return -3;
}
//给服务器发送数据包
if(sendto(cfd,file_buf,516,0,(struct sockaddr*)&dstaddr,sizeof(dstaddr)) < 0)
{
fprintf(stderr,"line:%d",__LINE__);
perror("sendto");
return -1;
}
printf("sendto uplopad data_pag success __%d__\n",__LINE__);
// 收到的应答包
// 清空数据缓存区
memset(ack_buf,0,4);
// 收数据包的应答包
res = recvfrom(cfd,ack_buf,sizeof(ack_buf),0,(struct sockaddr*)&dstaddr,&addrlen); // 返回收到的字符
if(res<0){
fprintf(stderr,"__%d__",__LINE__);
perror("recvfrom");
return -1;
}
short *ack_make_p = (short*)ack_buf;
short *ack_block_p = (short*)(ack_buf+2);
if((block_number-1) != ntohs(*ack_block_p) || ntohs(*ack_make_p) != 4)
{
printf("make:%d,block:%d",ntohs(*ack_make_p),ntohs(*ack_block_p));
printf("ack err");
return -1;
}
printf("recv ack_pag success __%d__\n",__LINE__);
// 代表文件已经读取完成
if(ret < 512){
if(close(fd)<0){
perror("close");
return -23;
}
break;
}
}
return 0;
}
标签:UDP,struct,int,cfd,传输服务,tftp,include,buf,sin
From: https://blog.csdn.net/qq_61958489/article/details/143776056