需求:
创建UDP客户端实现利用TFTP协议从服务器上下载文件与上传文件。
代码实现过程:
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <pthread.h>
#define ERR_MSG(msg) do{\
fprintf(stderr,"line : %d\n",__LINE__);\
perror("msg");\
}while(0)
#define IP "192.168.8.130" //填本機ubantuIP ifconfig查看
#define PORT 69 //1024~49151
#define FIL "download.png"
char buf[600] = "";
ssize_t res = 0;
int sfd = 0;
socklen_t addrlen = 0;
short num = 0;
unsigned short numS = 1;
void* funcS(void* arg){
struct sockaddr_in sein = *((struct sockaddr_in*)arg);
int fdS = open("upload.c",O_RDONLY);
if(fdS < 0){
ERR_MSG(open);
return NULL;
}
while(1){
bzero(buf,sizeof(buf));
if(recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sein,&addrlen) < 0){
ERR_MSG(recvfrom);
return NULL;
}
bzero(buf,sizeof(buf));
sprintf(buf,"%c%c",0,3);
*(short*)(buf+2) = htons(numS);
// *(short*)(buf+2) = htons(numS);
res = read(fdS,buf+4,512);
printf("numS = %d htons(numS) = %d num = %d res = %ld\n",numS,htons(numS),num,res);
if(res < 0){
ERR_MSG(read);
return NULL;
}
// printf("buf = %s\n",buf+4);
if(sendto(sfd,buf,res+4,0,(struct sockaddr*)&sein,sizeof(sein)) < 0){
ERR_MSG(sendto);
return NULL;
}
if(res != 512){
printf("文件上传完毕\n");
break;
}
numS++;
}
}
void* funcR(void* arg){
struct sockaddr_in sein = *((struct sockaddr_in*)arg);
int flag = 0;
int fdR = -1;
unsigned short numC = 0;
while(1){
//接受
if(0 == flag){
fdR = open(FIL,O_RDWR|O_CREAT|O_TRUNC,0664);
if(fdR < 0){
ERR_MSG(open);
return NULL;
}
flag=1;
}
//UDP是无连接不可靠的,数据包可能会重复接收到的通信
//可以在本地记录每次收到的包的编号
//如果本地记录的包的编号与数据包发送回来的块编号不一致,则不处理
//if(htons(numC+1) == *(unsigned short*)(buf+2)){
bzero(buf,sizeof(buf));
res = recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sein,&addrlen);
short *p1 = (short*)(buf+2);
num = ntohs(*p1);
printf("num = %d res = %ld\n",num,res);
if(res < 0){
ERR_MSG(recv);
return NULL;
}
if(write(fdR,buf+4,res-4) < 0){
ERR_MSG(write);
return NULL;
}
bzero(buf,sizeof(buf));
short *p2 = (short*)buf;
*p2 = htons(4);
short *p3 = p2+1;
*p3 = htons(num);
if(sendto(sfd,buf,4,0,(struct sockaddr*)&sein,sizeof(sein)) < 0){
ERR_MSG(sendto);
return NULL;
}
if(res!=516){
fprintf(stderr,"文件下载完毕\n");
close(fdR);
break;
}
numC++;
//}
}
}
int main(int argc, const char *argv[])
{
//創建报式套接字
sfd = socket(AF_INET,SOCK_DGRAM,0);
if(sfd < 0){
ERR_MSG(socket);
return -1;
}
//填充地址信息結構體
//根據地址族的不同,地址信息結構體不一致,AF_INET:man 7 ip
struct sockaddr_in sein;
sein.sin_family = AF_INET; //必須填AF_INET
sein.sin_port = htons(PORT); //端口號的網絡字節序,1024~49151
sein.sin_addr.s_addr = inet_addr(IP); //本機IP地址的網絡字節序,ifconfig
addrlen = sizeof(sein);
char choose = 0;
int size = 0;
pthread_t tidS,tidR;
while(1){
printf("-------------------------\n");
printf("---------1.download------\n");
printf("---------2.upload--------\n");
printf("---------3.exit----------\n");
printf("-------------------------\n");
printf("请输入>>>");
choose = getchar();
getchar();
switch(choose){
case '1':
//do_download
//发送下载请求
bzero(buf,sizeof(buf));
size = sprintf(buf,"%c%c%s%c%s%c",0,1,"5.png",0,"octet",0);
printf("buf = %s size = %d \n",buf+1,size);
if(sendto(sfd,buf,size,0,(struct sockaddr*)&sein,sizeof(sein)) < 0){
ERR_MSG(send);
return 0;
}
printf("send success\n");
if( pthread_create(&tidR,NULL,funcR,&sein) != 0){
ERR_MSG(pthread_create);
return -1;
}
pthread_join(tidR,NULL);
break;
case '2':
//do_upload
发送上传请求
bzero(buf,sizeof(buf));
size = sprintf(buf,"%c%c%s%c%s%c",0,2,"upload.c",0,"octet",0);
if(sendto(sfd,buf,size,0,(struct sockaddr*)&sein,sizeof(sein)) < 0){
ERR_MSG(send);
return 0;
}
printf("send success\n");
if( pthread_create(&tidS,NULL,funcS,&sein) != 0){
ERR_MSG(pthread_create);
return -1;
}
pthread_join(tidS,NULL);
break;
case '3':
printf("程序退出\n");
return 0;
}
}
close(sfd);
return 0;
}