#include<iohead.h>
#define SER_IP "192.168.108.117" //服务器ip地址
#define SER_PORT 69 //服务器端口号
#define CLI_IP "192.168.78.128" //客户端ip地址
#define CLI_PORT 9999 //客户端端口号
int main(int argc, const char *argv[])
{
//1、创建用于通信的套接字文件描述符
int cfd = socket(AF_INET, SOCK_DGRAM, 0);
if(-1 == cfd)
{
perror("socket error");
return -1;
}
printf("socket success cfd = %d\n", cfd); //3
//2、绑定ip地址和端口号(可选)
//2.1 填充客户端地址信息结构体
struct sockaddr_in cin;
cin.sin_family = AF_INET; //通信域
cin.sin_port = htons(CLI_PORT); //客户端端口号
cin.sin_addr.s_addr = inet_addr(CLI_IP); //客户端ip地址
//2.2 进行绑定工作
if(bind(cfd, (struct sockaddr*)&cin, sizeof(cin)) == -1)
{
perror("bind error");
return -1;
}
printf("bind success\n");
//3、数据收发
//封装服务器的地址信息结构体
struct sockaddr_in sin; //服务器地址信息结构体
sin.sin_family = AF_INET;
sin.sin_port = htons(SER_PORT); //服务器端口号
sin.sin_addr.s_addr = inet_addr(SER_IP); //服务器ip地址
socklen_t addrlen = sizeof(sin); //地址信息结构体的大小
//定义协议包数组
char msgBuf[516] = "";
//提示并输入文件名
char fileName[21] = ""; //记录文件名
printf("请输入您要下载的文件名:");
scanf("%s", fileName);
//封装请求协议
short *p1 =(short*) msgBuf; //封装请求
*p1 = htons(1); //表示下载请求包
char *p2 = msgBuf + 2; //文件名部分
strcpy(p2, fileName);
char *p4 = p2 + strlen(p2) + 1; //传输模式
strcpy(p4, "octet");
int msgLen = 2 + strlen(p2) + strlen(p4) +2; //请求包的长度
//将消息发送给服务器
sendto(cfd, msgBuf, msgLen, 0, (struct sockaddr*)&sin, sizeof(sin));
//以只写的形式打开一个文件,文件名为 fileName
int fd = open(fileName, O_WRONLY | O_CREAT | O_TRUNC, 0664);
if(fd < 0) {
perror("open file error");
close(cfd);
return -1;
}
int block_num = 1; // 数据块编号从1开始
int last_packet = 0; // 标记最后一个数据包
while(1)
{
char recvBuf[516] = "";
struct sockaddr_in from_addr;
socklen_t from_len = sizeof(from_addr);
ssize_t recvLen = recvfrom(cfd, recvBuf, sizeof(recvBuf), 0,
(struct sockaddr*)&from_addr, &from_len);
if(recvLen < 0)
{
perror("recvfrom error");
break;
}
//接收服务器发来的数据包
// 解析操作码
short opcode = ntohs(*(short*)recvBuf);
// 8.1 处理数据包(DATA)
if(opcode == 3)
{
// 获取数据块编号
short recv_block = ntohs(*(short*)(recvBuf + 2));
// 验证块编号是否连续
if(recv_block == block_num)
{
// 计算有效数据长度
int data_len = recvLen - 4;
// 写入文件
if(write(fd, recvBuf + 4, data_len) < 0)
{
perror("write file error");
break;
}
// 应答包
short ack_buf[2];
ack_buf[0] = htons(4);
ack_buf[1] = htons(block_num);
// 发送ACK确认
if(sendto(cfd, ack_buf, 4, 0,(struct sockaddr*)&from_addr, from_len) < 0)
{
perror("send ACK error");
break;
}
// 检查数据包长度是否小于512字节
if(data_len < 512)
{
printf("文件下载完成\n");
last_packet = 1;
}
block_num++; // 准备接收下一个数据块
}
else
{
printf("收到乱序数据块 #%d (期望 #%d)\n", recv_block, block_num);
}
}
// 8.2 处理错误包(ERROR)
else if(opcode ==5 )
{
short error_code = ntohs(*(short*)(recvBuf + 2));
char* error_msg = recvBuf + 4;
printf("服务器返回错误 #%d: %s\n", error_code, error_msg);
break;
}
else
{
printf("收到未知操作码: %d\n", opcode);
break;
}
}
close(fd);
//4、关闭客户端
close(cfd);
return 0;
}
ubun
网编day4
最新推荐文章于 2025-09-03 21:13:05 发布