// 客户端
#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
// 服务器端口号
#define POST 69
// 服务器ip
#define IP "192.168.2.140"
// 下载
int download(int serfd, struct sockaddr_in sin)
{
char buf[516] = { 0 }; // 数据包
// 1.初始化请求下载包
printf("input filename:");
char filename[128] = "";
scanf("%s", filename);
short* opcode = (short*)buf;
*opcode = htons(1);
sprintf(buf + 2, "%s%c%s%c", filename, '\0', "octet", '\0');
socklen_t sin_size = sizeof(sin); // 地址信息结构体大小
// 2.发送下载请求
if (-1 == sendto(serfd, buf, sizeof(buf), 0, (struct sockaddr*)&sin, sin_size)) {
perror("sendto");
return -1;
}
ssize_t len = 0; // 保存接收的数据大小
unsigned short num = 0; // 保存数据包块编号
int filefd = -1; // 初始化文件fd
while (1) {
bzero(buf, sizeof(buf));
// 3.接收数据
len = recvfrom(serfd, buf, sizeof(buf), 0, (struct sockaddr*)&sin, &sin_size);
if (-1 == len) {
perror("recvfrom");
return -1;
}
// 校验数据包的操作码是否==3
if (3 == buf[1]) {
// 校验数据包的块编号是否与本地记录一致
if (*(unsigned short*)(buf + 2) == htons(num + 1)) {
num++;
// 校验数据包块编号是否==1
if (1 == ntohs(*(unsigned short*)(buf + 2))) {
filefd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0664);
if (-1 == filefd) {
perror("open");
return -1;
}
}
// 保存数据到本地
if (-1 == write(filefd, buf + 4, len - 4)) {
perror("write");
close(filefd);
return -1;
}
// 回复ACK
buf[1] = 4;
if (-1 == sendto(serfd, buf, 4, 0, (struct sockaddr*)&sin, sin_size)) {
perror("recvfrom");
close(filefd);
return -1;
}
// 判断是否下载完毕
if (len - 4 < 512) {
printf("%s下载完毕\n", filename);
break;
}
}
} else if (5 == buf[1]) {
fprintf(stderr, "DOWNLOAD_ERROR:%d : %s\n", ntohs(*(short*)(buf + 2)), buf + 4);
return -1;
}
}
close(filefd);
return 0;
}
int main(int argc, const char* argv[])
{
// 1.创建报式套接字
int serfd = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == serfd) {
perror("socket");
return -1;
}
// 2.定义服务器地址信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET; // 地址簇
sin.sin_port = htons(POST); // 服务器端口号, 需要本地字节序-->网络字节序
sin.sin_addr.s_addr = inet_addr(IP); // 服务器ip, 需要点分十进制-->网络字节序
int options = -1; // 菜单选项
while (1) {
puts("------1.下载------");
puts("------2.上传------");
puts("------0.退出------");
scanf("%d", &options);
switch (options) {
case 0:
break;
case 1:
download(serfd, sin);
break;
case 2:
break;
default:
break;
}
}
return 0;
}
UDP项目-下载功能
最新推荐文章于 2023-10-08 22:00:00 发布