c++ 利用udp发广播传文件,客户端发出文件时每个包请使用结构体做包头,包头里面必须包含包长度,包序号,给出 服务端和客户端的例子,要求服务端保证文件的写入不能乱序,确保文件正确完整
时间: 2024-05-04 17:20:52 浏览: 76
udp 服务端和客户端,c++
服务端代码:
```cpp
#include <iostream>
#include <fstream>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define PORT 8888
#define BUF_SIZE 1024
using namespace std;
struct PacketHeader {
int seq_num; // 包序号
int packet_len; // 包长度
};
int main() {
int sockfd; // 套接字文件描述符
struct sockaddr_in server_addr, client_addr; // 服务端和客户端地址结构体
int addr_len = sizeof(client_addr); // 地址结构体大小
char buf[BUF_SIZE]; // 接收缓冲区
int seq_num = 0; // 包序号
// 创建套接字
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket error");
exit(1);
}
// 初始化服务端地址结构体
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(PORT);
// 绑定套接字和地址结构体
if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("bind error");
exit(1);
}
// 打开文件
ofstream fout("recv.txt", ios::out | ios::binary);
if (!fout) {
perror("open file error");
exit(1);
}
while (true) {
// 接收数据
int n = recvfrom(sockfd, buf, BUF_SIZE, 0, (struct sockaddr *)&client_addr, (socklen_t *)&addr_len);
if (n == -1) {
perror("recvfrom error");
exit(1);
}
// 解析包头
PacketHeader *header = (PacketHeader *)buf;
int len = header->packet_len;
int num = header->seq_num;
// 判断是否为结束包
if (len == 0) {
break;
}
// 判断是否为正确的包序号
if (num == seq_num) {
// 写入文件
fout.write(buf + sizeof(PacketHeader), len);
// 发送确认包
sendto(sockfd, &seq_num, sizeof(seq_num), 0, (struct sockaddr *)&client_addr, addr_len);
// 包序号加一
seq_num++;
} else {
// 发送上一个正确的包序号
int ack_num = seq_num - 1;
sendto(sockfd, &ack_num, sizeof(ack_num), 0, (struct sockaddr *)&client_addr, addr_len);
}
}
// 关闭文件和套接字
fout.close();
close(sockfd);
return 0;
}
```
客户端代码:
```cpp
#include <iostream>
#include <fstream>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define PORT 8888
#define BUF_SIZE 1024
using namespace std;
struct PacketHeader {
int seq_num; // 包序号
int packet_len; // 包长度
};
int main(int argc, char *argv[]) {
if (argc != 2) {
cout << "Usage: " << argv[0] << " <filename>" << endl;
return 0;
}
char *filename = argv[1]; // 文件名
int sockfd; // 套接字文件描述符
struct sockaddr_in server_addr; // 服务端地址结构体
char buf[BUF_SIZE]; // 发送缓冲区
int seq_num = 0; // 包序号
// 创建套接字
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket error");
exit(1);
}
// 初始化服务端地址结构体
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(PORT);
// 打开文件
ifstream fin(filename, ios::in | ios::binary);
if (!fin) {
perror("open file error");
exit(1);
}
while (true) {
// 读取数据
int len = fin.read(buf + sizeof(PacketHeader), BUF_SIZE - sizeof(PacketHeader)).gcount();
if (len == 0) {
break;
}
// 构造包头
PacketHeader header = {seq_num, len};
memcpy(buf, &header, sizeof(PacketHeader));
// 发送数据
sendto(sockfd, buf, len + sizeof(PacketHeader), 0, (struct sockaddr *)&server_addr, sizeof(server_addr));
// 接收确认包
int ack_num;
recvfrom(sockfd, &ack_num, sizeof(ack_num), 0, NULL, NULL);
// 判断是否为正确的包序号
if (ack_num == seq_num) {
// 包序号加一
seq_num++;
} else {
// 回退包序号
seq_num = ack_num + 1;
fin.seekg(seq_num * (BUF_SIZE - sizeof(PacketHeader)), ios::beg);
}
}
// 发送结束包
PacketHeader header = {seq_num, 0};
memcpy(buf, &header, sizeof(PacketHeader));
sendto(sockfd, buf, sizeof(PacketHeader), 0, (struct sockaddr *)&server_addr, sizeof(server_addr));
// 关闭文件和套接字
fin.close();
close(sockfd);
return 0;
}
```
阅读全文