#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#define ERRMSG(msg) do{\
printf("__%d__", __LINE__);\
perror(msg);\
return -1;\
}while(0)
int main(int argc, const char *argv[])
{
if(argc < 2)
{
printf("输入服务器IP地址\n");
return -1;
}
//创建套接字
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if(-1 == sfd)
{
ERRMSG("socket");
}
//填充服务器地址信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(69);
sin.sin_addr.s_addr = inet_addr(argv[1]);
//定义接收地址信息结构体变量
struct sockaddr_in cin;
socklen_t addrlen = sizeof(cin);
//发送下载请求
char buf[128];
bzero(buf, sizeof(buf));
short* pa = (short*)buf;
*pa = htons(1);//操作码
char* pb = buf+2;//文件名地址
printf("文件名:");
fgets(pb,100, stdin);
pb[strlen(pb)-1] = '\0';
char name[100]="";
strcpy(name, pb); //记录文件名,用于在客户端创建同名文件
char* pc = buf + 2 + strlen(pb) + 1;
strcpy(pc, "octet"); //模式
if(sendto(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&sin, sizeof(sin))<0)
{
ERRMSG("sendto");
}
printf("下载请求发送成功\n");
short code = 0;//操作码
short num = 0;//块编号
char data[516] = {0};//数据包
int res = 0;
int fd;
//接收数据包
while(1){
//接收服务器返回的数据
res = recvfrom(sfd, data, sizeof(data), 0, (struct sockaddr *)&sin, &addrlen);
if(res < 0)
{
ERRMSG("recvfrom");
}
//收到的操作码
code = ntohs(*(short*)data);
//文件内容是否完整,是不是要的文件
if(3 == code && num+1 == ntohs(*(short*)(data+2)))
{
num++;//块编号+1
if(1 == num)//第一次收到数据包时打开文件
{
//打开文件
fd = open(name, O_WRONLY|O_CREAT|O_TRUNC, 0664);
if(0 > fd)
{
ERRMSG("open");
}
}
//将内容写入文件里
write(fd, data+4, res-4);
//应答
*(short*)data = htons(4);
*(short*)(data+2) = htons(num);
//发送应答
if(0 > sendto(sfd, data, 4, 0, (struct sockaddr *)&sin, addrlen)){
ERRMSG("sendto error");
}
//文件发送完成
if(res < 516)
{
printf("文件下载完成\n");
break;
}
}
}
//关闭套接字和文件
close(sfd);
close(fd);
return 0;
}
结果