主函数:
#include <stdio.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#define ERR_MSG(msg) do{\
fprintf(stderr, "__%d__:", __LINE__);\
perror(msg);\
}while(0)
#define PORT 69 //服务器绑定的端口号
#define IP "192.168.31.43" //服务器绑定的IP
int do_download(int sfd,struct sockaddr_in sin);
int do_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;
}
//绑定客户端自身的IP和端口 --->非必须绑定
//因为要发送给服务器,所以要先填充服务器的地址信息结构体
//在sendto函数中使用。
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT); //服务器的端口号
sin.sin_addr.s_addr = inet_addr(IP); //服务器自身的IP地址
char c = 0;
while(1)
{
printf("************************\n");
printf("****** 1.下载 ******\n");
printf("****** 2.上传 ******\n");
printf("****** 3.退出 ******\n");
printf("************************\n");
printf("请输入(1,2,3)>>>");
c = getchar();
while(getchar()!= 10);
switch(c)
{
case '1': //下载
do_download(sfd,sin);
break;
case '2': //上传
do_upload(sfd,sin);
break;
case '3':
goto END;
default:
printf("输入错误,请重新输入\n");
}
}
END:
close(sfd);
return 0;
}
int do_download(int sfd,struct sockaddr_in sin)
{
char buf[516] = ""; //初始化字符串
ssize_t res=0;
socklen_t len=sizeof(sin);
//组下载请求包
char* ptr = buf;
unsigned short* ptr1 = (unsigned short*)ptr;
*ptr1 = htons(1); //下载请求的网络字节序
char* ptr2 = ptr+2;
strcpy(ptr2, "5.png");
char* ptr3 = ptr2+strlen(ptr2);
*ptr3 = 0;
char* ptr4 = ptr3+1;
strcpy(ptr4, "octet");
int size = 2+strlen(ptr2)+1+strlen(ptr4)+1;
//发送下载请求包
if(sendto(sfd, buf, size, 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
printf("发送下载请求包成功\n");
//打开一个文件
int fd=open("./1.png",O_WRONLY|O_CREAT|O_TRUNC|0664);
if(fd<0)
{
ERR_MSG("open");
return -1;
}
printf("open success\n");
while(1)
{
//循环接收服务器返回的数据包
res=recvfrom(sfd,(void*)buf,sizeof(buf),0,(struct sockaddr*)&sin,&len);
if(res<0)
{
ERR_MSG("recvfrom");
return -1;
}
//分析是数据包还是错误包
unsigned short *p=(unsigned short*)buf;
if(*p==htons(3))
{
if(write(fd,buf+4,res-4)<0)
{
ERR_MSG("write");
return -1;
}
//组ACK包,回复ACK给服务器
bzero(buf+4,sizeof(buf+4));
unsigned short* p_ack=(unsigned short*)buf;
*p_ack=htons(4);
if(sendto(sfd, p_ack, 4, 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
//直到数据内容的长度小于512个字节
if(res<516)
{
printf("下载完成\n");
break;
}
}
if(*p==htons(5))
{
//如果是错误包,
//则打印错误信息,退出
printf("error:%s\n",buf+4);
return -1;
}
}
close(fd);
return 0;
}
int do_upload(int sfd,struct sockaddr_in sin)
{
//组上传请求包
char buf[516]="";
ssize_t res=0;
socklen_t len=sizeof(sin);
char* ptr=buf;
unsigned short* ptr1=(unsigned short*)ptr;
*ptr1=htons(2);
char* ptr2 = ptr+2;
strcpy(ptr2, "4_tftpCli.c");
char* ptr3 = ptr2+strlen(ptr2);
*ptr3 = 0;
char* ptr4 = ptr3+1;
strcpy(ptr4, "octet");
int size = 2+strlen(ptr2)+1+strlen(ptr4)+1;
//发送上传请求包
if(sendto(sfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("sendto");
return -1;
}
int fd=open("./4_tftpcli.c",O_RDONLY);
if(fd<0)
{
ERR_MSG("open");
return -1;
}
printf("open success\n");
unsigned short num=0;
while(1)
{
bzero(buf,sizeof(buf));
//接收数据包
if(recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&len)<0)
{
ERR_MSG("recvfrom");
return -1;
}
unsigned short* p=(unsigned short*)buf;
if(htons(4)==*p)
{
if(num==ntohs(*(unsigned short*)(buf+2)))
{
buf[1]=3;
num++;
*(unsigned short*)(buf+2)=htons(num);
//读取数据到数据包
res=read(fd,buf+4,512);
if(res<0)
{
ERR_MSG("read");
return -1;
}
else if(0==res)
{
printf("文件上传完成\n");
break;
}
//发送数据包
if(sendto(sfd,buf,res+4,0,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("sendto");
return -1;
}
}
}
if(htons(5)==*p)
{
printf("error:%s\n",p+4);
break;
}
}
close(fd);
return 0;
}