windows c++ 用udp广播发送文件,要求服务端分包发送文件,每个包带上当前包序号,长度,包头标记; 客户端收到每个包后,根据包号直接写入文件,并且要 考虑数据包丢失、重传、超时等问题,给出正确的实例代码
时间: 2024-05-10 09:15:30 浏览: 116
以下是一个简单的实例代码,其中使用了Winsock库来实现UDP通信。服务端分包发送文件,每个包带有包头标记、包序号和长度,客户端接收并写入文件,同时处理重传、超时和丢包等问题。
服务端代码:
```c++
#include <iostream>
#include <fstream>
#include <winsock2.h>
using namespace std;
#pragma comment(lib, "ws2_32.lib") // 链接ws2_32.lib库
#define PORT 8888
#define BUFFER_SIZE 1024
struct PacketHeader {
int packetNo;
int packetLength;
bool isEnd;
};
bool sendFile(SOCKET sock, const char* fileName, sockaddr_in clientAddr) {
ifstream file(fileName, ios::binary);
if (!file.is_open()) {
cout << "Failed to open file: " << fileName << endl;
return false;
}
char buffer[BUFFER_SIZE];
int packetNo = 0, bytesSent = 0;
PacketHeader header;
while (!file.eof()) {
file.read(buffer, BUFFER_SIZE);
int bytesRead = file.gcount();
header.packetNo = packetNo++;
header.packetLength = bytesRead;
header.isEnd = file.eof();
int headerLen = sizeof(header);
char* packet = new char[headerLen + bytesRead];
memcpy(packet, &header, headerLen);
memcpy(packet + headerLen, buffer, bytesRead);
bytesSent = sendto(sock, packet, headerLen + bytesRead, 0, (sockaddr *)&clientAddr, sizeof(clientAddr));
if (bytesSent == SOCKET_ERROR) {
cout << "Failed to send data: " << WSAGetLastError() << endl;
return false;
}
delete[] packet;
}
file.close();
return true;
}
int main() {
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
cout << "Failed to initialize Winsock library." << endl;
return 1;
}
SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock == INVALID_SOCKET) {
cout << "Failed to create socket: " << WSAGetLastError() << endl;
WSACleanup();
return 1;
}
sockaddr_in serverAddr, clientAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(PORT);
serverAddr.sin_addr.s_addr = INADDR_ANY;
if (bind(sock, (sockaddr *)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
cout << "Failed to bind socket: " << WSAGetLastError() << endl;
closesocket(sock);
WSACleanup();
return 1;
}
cout << "Server is listening on port " << PORT << "..." << endl;
while (true) {
char buffer[BUFFER_SIZE];
int clientAddrLen = sizeof(clientAddr);
int bytesReceived = recvfrom(sock, buffer, BUFFER_SIZE, 0, (sockaddr *)&clientAddr, &clientAddrLen);
if (bytesReceived == SOCKET_ERROR) {
cout << "Failed to receive data: " << WSAGetLastError() << endl;
continue;
}
buffer[bytesReceived] = '\0';
cout << "Received request from " << inet_ntoa(clientAddr.sin_addr) << ":" << ntohs(clientAddr.sin_port) << endl;
if (sendFile(sock, buffer, clientAddr)) {
cout << "File sent successfully." << endl;
}
else {
cout << "Failed to send file." << endl;
}
}
closesocket(sock);
WSACleanup();
return 0;
}
```
客户端代码:
```c++
#include <iostream>
#include <fstream>
#include <winsock2.h>
using namespace std;
#pragma comment(lib, "ws2_32.lib") // 链接ws2_32.lib库
#define PORT 8888
#define BUFFER_SIZE 1024
#define TIMEOUT 5000 // 超时时间
struct PacketHeader {
int packetNo;
int packetLength;
bool isEnd;
};
bool receiveFile(SOCKET sock, const char* fileName, sockaddr_in serverAddr) {
ofstream file(fileName, ios::binary);
if (!file.is_open()) {
cout << "Failed to create file: " << fileName << endl;
return false;
}
char buffer[BUFFER_SIZE];
int packetNo = 0;
while (true) {
PacketHeader header;
int headerLen = sizeof(header);
int bytesReceived = recv(sock, (char *)&header, headerLen, 0);
if (bytesReceived == SOCKET_ERROR) {
cout << "Failed to receive data: " << WSAGetLastError() << endl;
return false;
}
if (bytesReceived == 0) {
cout << "Connection closed by server." << endl;
return false;
}
int packetLen = header.packetLength;
char* packet = new char[packetLen];
bytesReceived = recv(sock, packet, packetLen, 0);
if (bytesReceived == SOCKET_ERROR) {
cout << "Failed to receive data: " << WSAGetLastError() << endl;
return false;
}
if (bytesReceived == 0) {
cout << "Connection closed by server." << endl;
return false;
}
if (header.packetNo != packetNo) {
// 收到的包序号不是期望的序号,需要重传之前的包
cout << "Packet loss detected. Resending packet " << packetNo << "..." << endl;
int bytesSent = sendto(sock, (char *)&packetNo, sizeof(packetNo), 0, (sockaddr *)&serverAddr, sizeof(serverAddr));
if (bytesSent == SOCKET_ERROR) {
cout << "Failed to send data: " << WSAGetLastError() << endl;
return false;
}
continue;
}
packetNo++;
file.write(packet, packetLen);
delete[] packet;
if (header.isEnd) {
break;
}
}
file.close();
return true;
}
int main(int argc, char* argv[]) {
if (argc != 3) {
cout << "Usage: " << argv[0] << " <server IP> <file name>" << endl;
return 1;
}
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
cout << "Failed to initialize Winsock library." << endl;
return 1;
}
SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock == INVALID_SOCKET) {
cout << "Failed to create socket: " << WSAGetLastError() << endl;
WSACleanup();
return 1;
}
sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(PORT);
serverAddr.sin_addr.s_addr = inet_addr(argv[1]);
char buffer[BUFFER_SIZE];
strcpy_s(buffer, BUFFER_SIZE, argv[2]);
int bytesSent = sendto(sock, buffer, strlen(buffer), 0, (sockaddr *)&serverAddr, sizeof(serverAddr));
if (bytesSent == SOCKET_ERROR) {
cout << "Failed to send data: " << WSAGetLastError() << endl;
closesocket(sock);
WSACleanup();
return 1;
}
fd_set readSet;
FD_ZERO(&readSet);
FD_SET(sock, &readSet);
timeval timeout;
timeout.tv_sec = TIMEOUT / 1000;
timeout.tv_usec = (TIMEOUT % 1000) * 1000;
int result = select(0, &readSet, NULL, NULL, &timeout);
if (result == SOCKET_ERROR) {
cout << "Failed to select socket: " << WSAGetLastError() << endl;
closesocket(sock);
WSACleanup();
return 1;
}
if (result == 0) {
cout << "Connection timed out." << endl;
closesocket(sock);
WSACleanup();
return 1;
}
int packetNo = 0;
bytesSent = sendto(sock, (char *)&packetNo, sizeof(packetNo), 0, (sockaddr *)&serverAddr, sizeof(serverAddr));
if (bytesSent == SOCKET_ERROR) {
cout << "Failed to send data: " << WSAGetLastError() << endl;
closesocket(sock);
WSACleanup();
return 1;
}
if (receiveFile(sock, argv[2], serverAddr)) {
cout << "File received successfully." << endl;
}
else {
cout << "Failed to receive file." << endl;
}
closesocket(sock);
WSACleanup();
return 0;
}
```
注意:
1. 在发送数据包时,需要先将包头和数据拷贝到一个新的缓冲区中,再通过 `sendto` 函数发送。
2. 在接收数据包时,需要先接收包头,再根据包头中的长度接收数据,避免粘包或截断。
3. 当收到的包序号与期望的包序号不一致时,需要重传之前的包。此处只重传一个包,实际应用中可能需要重传多个包。
4. 客户端需要设置超时时间,防止长时间等待而导致的阻塞。可以使用 `select` 函数实现超时等待。
阅读全文