#define PORT 69 //端口号:服务器绑定的端口号
#define IP "192.168.1.11" //IP:服务器绑定的IP
#define FILENAME "5.png"
int main(int argc, const char *argv[])
{
//创建报式套接字
int cfd = socket(AF_INET, SOCK_DGRAM, 0);
if(cfd < 0)
{
ERR_MSG("socket");
return -1;
}
//绑定客户端的地址信息结构体到套接字--->非必须绑定
//若不绑定,则操作系统会给客户端绑定运行主机的IP和随机端口
//填充服务器自身的地址信息结构体,给sendto函数使用
//要发给谁,就填谁的地址信息
struct sockaddr_in sin;
sin.sin_family = AF_INET; //必须填AF_INET
sin.sin_port = htons(PORT); //端口号:服务器绑定的端口号
sin.sin_addr.s_addr = inet_addr(IP); //IP: 服务器绑定的IP
//读写请求
char buf[516] = "";
//操作码
unsigned short* p1 = (unsigned short*)buf;
*p1 = htons(1);
//文件名
char* p2 = buf+2;
strcpy(p2, "5.png");
// 0
// char* p3 = p2+strlen(p2);
// *p3 = 0;
//模式
char* p4 = p2 + strlen(p2)+1; //p3+1;
strcpy(p4, "octet");
int len = 2+strlen(p2)+1+strlen(p4)+1;
//发送读写请求
if(sendto(cfd, buf, len, 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
printf("sendto sucess\n");
ssize_t res = 0;
struct sockaddr_in rcvaddr; //存储接收到的数据包从哪里来
socklen_t addrlen = sizeof(rcvaddr);
unsigned short num = 1; // 块编号
int fd = -1; //定义本地下载文件描述符
//接收数据包及发送ACK,判断是否下载完毕
while(1)
{
//接收数据包
bzero(buf, sizeof(buf));
res = recvfrom(cfd, buf, sizeof(buf), 0, (struct sockaddr*)&rcvaddr, &addrlen);
if(res < 0)
{
ERR_MSG("recvfrom");
return -1;
}
//判断接收到的是数据包还是ERROR
if(3 == ntohs(*p1))
{
//判断块编号, 避免数据包重复
if(htons(num) == *(p1+1))
{
num++;
if(-1 == fd) //保证只有开始会有一次文件打开的操作
{
fd = open(FILENAME, O_WRONLY|O_CREAT|O_TRUNC, 0664);
if(fd < 0)
{
ERR_MSG("open");
break;
}
}
if(write(fd, buf+4, res-4) < 0)
{
ERR_MSG("write");
return -1;
}
//回复ACK
// bzero(buf, sizeof(buf));
*p1 = htons(4);
if(sendto(cfd, buf, 4, 0, (struct sockaddr*)&rcvaddr, addrlen) < 0)
{
ERR_MSG("sendto");
return -1;
}
printf("sendto sucess\n");
//判断是否下载完毕,数据小于512下载完毕
if(res-4 < 512)
{
printf("数据传输结束\n");
break;
}
}
else //若块编号不同,数据包出现问题,跳出循环,下载失败
{
remove(FILENAME);
break;
}
}
else if(5 == ntohs(*p1))
{
//printf("ERROR\n");
switch(ntohs(*(p1+1)))
{
case 0: printf("%s\n", buf+4); break;
case 1: printf("%s\n", buf+4); break;
case 2: printf("%s\n", buf+4); break;
case 3: printf("%s\n", buf+4); break;
case 4: printf("%s\n", buf+4); break;
case 5: printf("%s\n", buf+4); break;
case 6: printf("%s\n", buf+4); break;
}
break;
}
}
//关闭文件描述符
close(fd);
close(cfd);
return 0;
}