包含文本,表情,图片聊天数据的协议以及实现,其中包含了用到的内存池代码
以下是一个简单的C++ 内存池实现示例,内存池的主要目的是提前分配一块较大的内存区域,然后在需要内存时从这块预分配的区域中分配,使用完毕后归还到池中,避免频繁地向操作系统申请和释放内存,从而提高内存分配和释放的效率,尤其适用于频繁进行小块内存分配和释放的场景。
#include <iostream>
#include <vector>
#include <cstdlib>
// 内存块结构体,用于管理内存池中每个内存块的信息
struct MemoryBlock {
void* memory; // 指向实际分配的内存地址
size_t size; // 该内存块的大小
bool is_used; // 标记该内存块是否已被使用
MemoryBlock* next; // 指向下一个内存块,用于构建链表结构
};
class MemoryPool {
public:
// 构造函数,初始化内存池,传入要分配的内存池总大小和每个内存块的大小
MemoryPool(size_t pool_size, size_t block_size) : poolSize(pool_size), blockSize(block_size) {
// 分配初始的内存池空间
memoryPool = static_cast<char*>(malloc(pool_size));
if (!memoryPool) {
std::cerr << "Failed to allocate memory pool." << std::endl;
exit(1);
}
// 初始化第一个内存块
MemoryBlock* first_block = new MemoryBlock;
first_block->memory = memoryPool;
first_block->size = block_size;
first_block->is_used = false;
first_block->next = nullptr;
freeBlocks.push_back(first_block);
// 根据总内存池大小和单个内存块大小,划分出其他内存块并添加到空闲链表中
char* current = memoryPool + block_size;
size_t remaining_size = pool_size - block_size;
while (remaining_size >= block_size) {
MemoryBlock* new_block = new MemoryBlock;
new_block->memory = current;
new_block->size = block_size;
new_block->is_used = false;
new_block->next = freeBlocks.back();
freeBlocks.push_back(new_block);
current += block_size;
remaining_size -= block_size;
}
}
// 析构函数,释放内存池所占用的内存以及内存块结构体的内存
~MemoryPool() {
free(memoryPool);
for (auto block : freeBlocks) {
delete block;
}
for (auto block : usedBlocks) {
delete block;
}
}
// 从内存池中分配内存,如果有空闲内存块则返回可用内存地址,否则返回nullptr
void* allocate() {
if (freeBlocks.empty()) {
return nullptr;
}
MemoryBlock* block = freeBlocks.back();
freeBlocks.pop_back();
block->is_used = true;
usedBlocks.push_back(block);
return block->memory;
}
// 将使用完的内存归还给内存池,标记对应的内存块为空闲状态
void deallocate(void* ptr) {
for (auto it = usedBlocks.begin(); it!= usedBlocks.end(); ++it) {
if ((*it)->memory == ptr) {
(*it)->is_used = false;
freeBlocks.push_back(*it);
usedBlocks.erase(it);
break;
}
}
}
private:
char* memoryPool; // 指向整个内存池的内存地址
size_t poolSize; // 内存池的总大小
size_t blockSize; // 每个内存块的大小
std::vector<MemoryBlock*> freeBlocks; // 空闲内存块链表,使用vector方便管理
std::vector<MemoryBlock*> usedBlocks; // 已使用内存块链表
};
你可以使用以下方式来测试这个内存池:
int main() {
// 创建一个内存池,总大小为1024字节,每个内存块大小为64字节
MemoryPool pool(1024, 64);
// 从内存池中分配内存
int* ptr1 = static_cast<int*>(pool.allocate());
if (ptr1) {
*ptr1 = 10;
std::cout << "Allocated memory at address: " << ptr1 << ", value: " << *ptr1 << std::endl;
}
// 再分配一块内存
int* ptr2 = static_cast<int*>(pool.allocate());
if (ptr2) {
*ptr2 = 20;
std::cout << "Allocated memory at address: " << ptr2 << ", value: " << *ptr2 << std::endl;
}
// 归还一块内存到内存池
pool.deallocate(ptr1);
// 再次分配内存,可能会分配到之前归还的那块内存
int* ptr3 = static_cast<int*>(pool.allocate());
if (ptr3) {
*ptr3 = 30;
std::cout << "Allocated memory at address: " << ptr3 << ", value: " << *ptr3 << std::endl;
}
return 0;
}
这个内存池实现相对比较简单,在实际应用中还可以进行很多优化和扩展,例如:
- 内存对齐:确保分配的内存满足特定的对齐要求,比如按照机器字长对齐,这在一些对内存访问性能要求较高的场景(如处理结构体数组等)很重要。
- 支持不同大小的内存块:可以采用更复杂的内存管理策略,如伙伴系统等,来管理不同大小的内存块,满足更多样化的内存分配需求。
- 线程安全:如果内存池在多线程环境下使用,需要添加适当的锁机制来保证线程安全,避免多个线程同时操作内存块链表等导致的问题。
希望这个示例对你理解和实现内存池有所帮助。
以下是一个更完善的C++内存池实现代码,包含了内存对齐、支持不同大小内存块(采用简单的分块策略)以及简单的线程安全处理(使用互斥锁)等功能,同时提供了更详细的测试用例来展示其使用方法。
#include <iostream>
#include <vector>
#include <cstdlib>
#include <mutex>
#include <algorithm>
// 内存块结构体,用于管理内存池中每个内存块的信息
struct MemoryBlock {
void* memory; // 指向实际分配的内存地址
size_t size; // 该内存块的大小
bool is_used; // 标记该内存块是否已被使用
MemoryBlock* next; // 指向下一个内存块,用于构建链表结构
};
class MemoryPool {
public:
// 构造函数,初始化内存池,传入要分配的内存池总大小
MemoryPool(size_t pool_size) : poolSize(pool_size) {
// 分配初始的内存池空间
memoryPool = static_cast<char*>(malloc(pool_size));
if (!memoryPool) {
std::cerr << "Failed to allocate memory pool." << std::endl;
exit(1);
}
// 初始化内存块链表,先放入一个最大尺寸的内存块(即整个内存池大小)
MemoryBlock* first_block = new MemoryBlock;
first_block->memory = memoryPool;
first_block->size = pool_size;
first_block->is_used = false;
first_block->next = nullptr;
freeBlocks.push_back(first_block);
}
// 析构函数,释放内存池所占用的内存以及内存块结构体的内存
~MemoryPool() {
free(memoryPool);
for (auto block : freeBlocks) {
delete block;
}
for (auto block : usedBlocks) {
delete block;
}
}
// 设置内存对齐字节数,默认按照机器字长对齐(这里简单示例取8字节对齐,可根据实际调整)
void setAlignment(size_t alignment = 8) {
this->alignment = alignment;
}
// 从内存池中分配指定大小的内存,如果有空闲内存块且大小合适则返回可用内存地址,否则返回nullptr
void* allocate(size_t size) {
std::lock_guard<std::mutex> guard(mutex_); // 加锁,保证线程安全
// 对要分配的大小进行内存对齐处理
size = alignSize(size);
// 遍历空闲内存块链表,查找合适大小的空闲内存块
for (auto it = freeBlocks.begin(); it!= freeBlocks.end(); ++it) {
if ((*it)->size >= size &&!(*it)->is_used) {
MemoryBlock* block = *it;
block->is_used = true;
usedBlocks.push_back(block);
// 如果内存块大小有剩余,分割出剩余部分作为新的空闲内存块
if (block->size > size) {
splitBlock(block, size);
}
freeBlocks.erase(it);
return block->memory;
}
}
return nullptr;
}
// 将使用完的内存归还给内存池,标记对应的内存块为空闲状态
void deallocate(void* ptr) {
std::lock_guard<std::mutex> guard(mutex_); // 加锁,保证线程安全
for (auto it = usedBlocks.begin(); it!= usedBlocks.end(); ++it) {
if ((*it)->memory == ptr) {
(*it)->is_used = false;
mergeBlocks(*it); // 尝试合并相邻的空闲内存块
freeBlocks.push_back(*it);
usedBlocks.erase(it);
break;
}
}
}
private:
char* memoryPool; // 指向整个内存池的内存地址
size_t poolSize; // 内存池的总大小
size_t alignment; // 内存对齐字节数
std::vector<MemoryBlock*> freeBlocks; // 空闲内存块链表,使用vector方便管理
std::vector<MemoryBlock*> usedBlocks; // 已使用内存块链表
std::mutex mutex_; // 互斥锁,用于实现线程安全
// 对给定的大小进行内存对齐,返回对齐后的大小
size_t alignSize(size_t size) {
return (size + alignment - 1) & ~(alignment - 1);
}
// 分割内存块,将一个较大的空闲内存块分割成已分配部分和剩余的空闲部分
void splitBlock(MemoryBlock* block, size_t size) {
MemoryBlock* new_block = new MemoryBlock;
new_block->memory = static_cast<char*>(block->memory) + size;
new_block->size = block->size - size;
new_block->is_used = false;
new_block->next = block->next;
block->size = size;
block->next = new_block;
freeBlocks.push_back(new_block);
}
// 合并相邻的空闲内存块,提高内存利用率
void mergeBlocks(MemoryBlock* block) {
// 向前合并
auto it = std::find(freeBlocks.begin(), freeBlocks.end(), block);
if (it!= freeBlocks.begin()) {
auto prev_it = std::prev(it);
if (!(*prev_it)->is_used && static_cast<char*>((*prev_it)->memory) + (*prev_it)->size == block->memory) {
(*prev_it)->size += block->size;
(*prev_it)->next = block->next;
delete block;
block = *prev_it;
it = prev_it;
}
}
// 向后合并
if (it!= freeBlocks.end()) {
auto next_it = std::next(it);
if (next_it!= freeBlocks.end() &&!(*next_it)->is_used && static_cast<char*>(block->memory) + block->size == (*next_it)->memory) {
block->size += (*next_it)->size;
block->next = (*next_it)->next;
delete *next_it;
}
}
}
};
以下是使用这个内存池的测试代码示例:
int main() {
// 创建一个内存池,总大小为1024字节
MemoryPool pool(1024);
pool.setAlignment(); // 使用默认的内存对齐字节数(8字节)
// 分配不同大小的内存块并使用
void* ptr1 = pool.allocate(64);
if (ptr1) {
int* int_ptr1 = static_cast<int*>(ptr1);
*int_ptr1 = 10;
std::cout << "Allocated memory at address: " << ptr1 << ", value: " << *int_ptr1 << std::endl;
}
void* ptr2 = pool.allocate(128);
if (ptr2) {
float* float_ptr2 = static_cast<float*>(ptr2);
*float_ptr2 = 3.14f;
std::cout << "Allocated memory at address: " << ptr2 << ", value: " << *float_ptr2 << std::endl;
}
// 归还内存块到内存池
pool.deallocate(ptr1);
pool.deallocate(ptr2);
// 再次分配内存,验证是否能正确分配和使用之前归还的内存
void* ptr3 = pool.allocate(64);
if (ptr3) {
int* int_ptr3 = static_cast<int*>(ptr3);
*int_ptr3 = 20;
std::cout << "Allocated memory at address: " << ptr3 << ", value: " << *int_ptr3 << std::endl;
}
// 多线程环境下测试(简单示意,可进一步完善)
std::vector<std::thread> threads;
const int num_threads = 5;
const int num_allocations = 10;
for (int i = 0; i < num_threads; ++i) {
threads.push_back(std::thread([&pool]() {
for (int j = 0; j < num_allocations; ++j) {
void* ptr = pool.allocate(32);
if (ptr) {
// 简单操作分配到的内存,这里仅示例赋值
int* int_ptr = static_cast<int*>(ptr);
*int_ptr = j;
pool.deallocate(ptr);
}
}
}));
}
for (auto& th : threads) {
th.join();
}
return 0;
}
在上述代码中:
MemoryPool类的构造函数负责分配整个内存池的内存空间,并初始化第一个内存块加入到空闲内存块链表中。setAlignment方法可以设置内存对齐的字节数,默认采用8字节对齐,通过alignSize函数来实现具体的对齐操作,确保分配的内存满足对齐要求。allocate方法在分配内存时,首先会对请求的内存大小进行对齐处理,然后遍历空闲内存块链表查找合适大小的空闲内存块,若找到且有剩余空间还会进行内存块的分割操作,同时使用互斥锁保证在多线程环境下的操作安全。deallocate方法用于归还内存到内存池,在归还时会尝试合并相邻的空闲内存块以提高内存利用率,同样通过互斥锁保证线程安全。
测试代码展示了在单线程和简单多线程环境下内存池的基本使用方式,包括内存分配、使用、归还以及在多线程中重复分配和归还的操作情况。不过在实际更复杂的应用场景中,还可以根据具体需求进一步优化内存池的性能、扩展功能等,比如采用更复杂的内存管理算法、更精细的线程安全控制机制等。
请注意,代码中一些错误处理(如内存分配失败等情况)可以根据实际应用场景进一步完善,使其更加健壮。希望这个完整的实现示例对你有所帮助。
以下是一个较为完整的C++代码实现,包含了用户注册、登录、保持心跳、用户与用户之间发送文本消息、表情和图片功能的类似聊天应用的示例,基于UDP协议,并使用了之前提到的一些机制如自定义协议首部、内存池以及可靠性相关机制(校验和、确认和重传等)。这个示例只是一个基础的框架,实际应用中还可以根据需求进一步优化和扩展功能。
1. 相关头文件和命名空间
#include <iostream>
#include <fstream>
#include <cstring>
#include <ctime>
#include <cstdlib>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <vector>
#include <map>
#include <thread>
#include <mutex>
#include <queue>
#include <sstream>
#include "MemoryPool.h" // 假设之前的内存池代码放在MemoryPool.h和MemoryPool.cpp中,这里包含头文件
using namespace std;
2. 定义数据包首部结构体
// 自定义数据包首部结构体
struct ChatPacketHeader {
unsigned short version; // 协议版本号,用于后续协议升级兼容等情况,初始设为1
unsigned char msg_type; // 消息类型,如1表示文字,2表示表情,3表示图片,4表示注册请求,5表示登录请求,6表示心跳消息,7表示确认消息等
unsigned short total_packets; // 当消息被分割成多个UDP数据包时,此为总数据包数量,若无需分割则为1
unsigned short packet_seq; // 当前数据包在整个消息中的序号,从0开始
unsigned int msg_id; // 消息唯一标识,用于区分不同的消息,比如可以用时间戳结合随机数生成
unsigned int sender_id; // 发送者的唯一标识,比如用户账号对应的编号等
unsigned int receiver_id; // 接收者的唯一标识,与发送者对应
unsigned short data_length; // 当前数据包中数据部分的长度(即除去首部后的有效数据长度)
};
3. 计算校验和的函数(这里采用简单的CRC16校验算法示例,可替换为更合适的算法)
// CRC16校验算法函数(简单示例,可优化)
unsigned short calculateCRC16(const char* data, int length) {
unsigned short crc = 0xFFFF;
for (int i = 0; i < length; ++i) {
crc ^= (unsigned short)data[i] << 8;
for (int j = 0; j < 8; ++j) {
if (crc & 0x8000) {
crc = (crc << 1) ^ 0x1021;
} else {
crc <<= 1;
}
}
}
return crc;
}
4. 用户结构体定义
// 用户结构体,用于存储用户相关信息
struct User {
unsigned int user_id;
string username;
string password;
// 可以添加更多用户相关属性,如头像、个性签名等,这里暂简化
};
5. 全局变量和数据结构(用于简单模拟用户数据库等,实际应用中应使用数据库系统)
vector<User> userList; // 存储所有用户信息的列表,模拟用户数据库
mutex userListMutex; // 互斥锁,用于保护用户列表的并发访问
map<unsigned int, string> onlineUsers; // 存储在线用户,键为用户ID,值为用户名,用于快速查找在线用户
mutex onlineUsersMutex; // 互斥锁,用于保护在线用户列表的并发访问
6. 用户注册函数
// 用户注册函数,将新用户信息添加到用户列表中
bool registerUser(const string& username, const string& password) {
lock_guard<mutex> guard(userListMutex);
for (const auto& user : userList) {
if (user.username == username) {
return false; // 用户名已存在,注册失败
}
}
User newUser;
newUser.user_id = userList.size() + 1;
newUser.username = username;
newUser.password = password;
userList.push_back(newUser);
return true; // 注册成功
}
7. 用户登录函数
// 用户登录函数,验证用户名和密码,若正确则将用户标记为在线
bool loginUser(const string& username, const string& password) {
lock_guard<mutex> guard(userListMutex);
for (const auto& user : userList) {
if (user.username == username && user.password == password) {
lock_guard<mutex> onlineGuard(onlineUsersMutex);
onlineUsers[user.user_id] = username;
return true; // 登录成功
}
}
return false; // 登录失败
}
8. 发送端类的定义及实现
class ChatSender {
public:
ChatSender(const char* destination_ip, int destination_port, size_t pool_size) :
dest_ip(destination_ip), dest_port(destination_port),
memoryPool(pool_size), unconfirmedPacketsMutex(), unconfirmedPackets(),
ackReceivedMutex(), ackReceived()
{
// 创建UDP套接字
sockfd = socket(AF_INET, SOCK_DUDP, 0);
if (sockfd < 0) {
perror("Socket creation failed");
exit(1);
}
// 配置服务器地址结构体
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(destination_port);
server_addr.sin_addr.s_addr = inet_addr(destination_ip);
// 启动一个线程用于处理未确认数据包的超时和重传
thread retransmitThread(&ChatSender::handleUnconfirmedPacketsThread, this);
retransmitThread.detach();
}
~ChatSender() {
close(sockfd);
}
// 发送注册请求消息
void sendRegisterRequest(const string& username, const string& password) {
ChatPacketHeader header;
header.version = 1;
header.msg_type = 4; // 注册请求消息类型为4
header.total_packets = 1;
header.packet_seq = 0;
header.msg_id = generateMessageId();
header.sender_id = 0; // 暂设为0,可根据实际调整
header.receiver_id = 0; // 暂设为0,可根据实际调整
header.data_length = username.length() + password.length() + 2; // 加上分隔符长度
string data = username + "\n" + password; // 用换行符分隔用户名和密码,可自定义格式
char* buffer = static_cast<char*>(memoryPool.allocate(sizeof(header) + data.length() + sizeof(unsigned short)));
if (!buffer) {
cerr << "Memory allocation failed for register request" << endl;
return;
}
memcpy(buffer, &header, sizeof(header));
memcpy(buffer + sizeof(header), data.c_str(), data.length());
// 计算校验和并添加到数据包末尾
unsigned short checksum = calculateCRC16(buffer, sizeof(header) + data.length());
memcpy(buffer + sizeof(header) + data.length(), &checksum, sizeof(checksum));
// 发送数据包
if (sendto(sockfd, buffer, sizeof(header) + data.length() + sizeof(checksum), 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("Sendto failed");
} else {
// 记录已发送的数据包信息
UnconfirmedPacket packet;
packet.header = header;
packet.data = buffer;
packet.send_time = time(nullptr);
{
lock_guard<mutex> guard(unconfirmedPacketsMutex);
unconfirmedPackets.push(packet);
}
}
}
// 发送登录请求消息
void sendLoginRequest(const string& username, const string& password) {
ChatPacketHeader header;
header.version = 1;
header.msg_type = 5; // 登录请求消息类型为5
header.total_packets = 1;
header.packet_seq = 0;
header.msg_id = generateMessageId();
header.sender_id = 0; // 暂设为0,可根据实际调整
header.receiver_id = 0; // 暂设为0,可根据实际调整
header.data_length = username.length() + password.length() + 2; // 加上分隔符长度
string data = username + "\n" + password;
char* buffer = static_cast<char*>(memoryPool.allocate(sizeof(header) + data.length() + sizeof(unsigned short)));
if (!buffer) {
cerr << "Memory allocation failed for login request" << endl;
return;
}
memcpy(buffer, &header, sizeof(header));
memcpy(buffer + sizeof(header), data.c_str(), data.length());
// 计算校验和并添加到数据包末尾
unsigned short checksum = calculateCRC16(buffer, sizeof(header) + data.length());
memcpy(buffer + sizeof(header) + data.length(), &checksum, sizeof(checksum));
// 发送数据包
if (sendto(sockfd, buffer, sizeof(header) + data.length() + sizeof(checksum), 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("Sendto failed");
} else {
// 记录已发送的数据包信息
UnconfirmedPacket packet;
packet.header = header;
packet.data = buffer;
packet.send_time = time(nullptr);
{
lock_guard<mutex> guard(unconfirmedPacketsMutex);
unconfirmedPackets.push(packet);
}
}
}
// 发送心跳消息,用于保持连接,简单告知服务器或对方自己在线
void sendHeartbeatMessage(unsigned int sender_id, unsigned int receiver_id) {
ChatPacketHeader header;
header.version = 1;
header.msg_type = 6; // 心跳消息类型为6
header.total_packets = 1;
header.packet_seq = 0;
header.msg_id = generateMessageId();
header.sender_id = sender_id;
header.receiver_id = receiver_id;
header.data_length = 0;
char* buffer = static_cast<char*>(memoryPool.allocate(sizeof(header) + sizeof(unsigned short)));
if (!buffer) {
cerr << "Memory allocation failed for heartbeat message" << endl;
return;
}
memcpy(buffer, &header, sizeof(header));
// 计算校验和并添加到数据包末尾
unsigned short checksum = calculateCRC16(buffer, sizeof(header));
memcpy(buffer + sizeof(header), &checksum, sizeof(checksum));
// 发送数据包
if (sendto(sockfd, buffer, sizeof(header) + sizeof(checksum), 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("Sendto failed");
} else {
// 记录已发送的数据包信息
UnconfirmedPacket packet;
packet.header = header;
packet.data = buffer;
packet.send_time = time(nullptr);
{
lock_guard<mutex> guard(unconfirmedPacketsMutex);
unconfirmedPackets.push(packet);
}
}
}
// 发送文字消息
void sendTextMessage(const string& text, unsigned int sender_id, unsigned int receiver_id) {
ChatPacketHeader header;
header.version = 1;
header.msg_type = 1; // 文字消息类型为1
header.total_packets = 1;
header.packet_seq = 0;
header.msg_id = generateMessageId();
header.sender_id = sender_id;
header.receiver_id = receiver_id;
header.data_length = text.length();
char* buffer = static_cast<char*>(memoryPool.allocate(sizeof(header) + text.length() + sizeof(unsigned short)));
if (!buffer) {
cerr << "Memory allocation failed for text message" << endl;
return;
}
memcpy(buffer, &header, sizeof(header));
memcpy(buffer + sizeof(header), text.c_str(), text.length());
// 计算校验和并添加到数据包末尾
unsigned short checksum = calculateCRC16(buffer, sizeof(header) + text.length());
memcpy(buffer + sizeof(header) + text.length(), &checksum, sizeof(checksum));
// 发送数据包
if (sendto(sockfd, buffer, sizeof(header) + text.length() + sizeof(checksum), 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("Sendto failed");
} else {
// 记录已发送的数据包信息
UnconfirmedPacket packet;
packet.header = header;
packet.data = buffer;
packet.send_time = time(nullptr);
{
lock_guard<mutex> guard(unconfirmedPacketsMutex);
unconfirmedPackets.push(packet);
}
}
}
// 发送图片消息(假设图片以二进制形式读取发送,这里简单示意,可优化图片处理逻辑)
void sendImageMessage(const char* image_path, unsigned int sender_id, unsigned int receiver_id) {
ifstream imageFile(image_path, ios::binary);
if (!imageFile) {
cerr << "Failed to open image file" << endl;
return;
}
imageFile.seekg(0, ios::end);
long int image_size = imageFile.tellg();
imageFile.seekg(0, ios::beg);
const int max_data_size = 1472 - sizeof(ChatPacketHeader); // 假设基于以太网类似场景,预留首部空间
int total_packets = image_size / max_data_size + (image_size % max_data_size > 0? 1 : 0);
ChatPacketHeader header;
header.version = 1;
header.msg_type = 3; // 图片消息类型为3
header.total_packets = total_packets;
header.msg_id = generateMessageId();
header.sender_id = sender_id;
header.receiver_id = receiver_id;
char* buffer = nullptr;
int packet_seq = 0;
while (!imageFile.eof()) {
header.packet_seq = packet_seq;
imageFile.read(buffer + sizeof(header), max_data_size);
int read_size = imageFile.gcount();
header.data_length = read_size;
memcpy(buffer, &header, sizeof(header));
// 计算校验和并添加到数据包末尾
unsigned short checksum = calculateCRC16(buffer, sizeof(header) + read_size);
memcpy(buffer + sizeof(header) + read_size, &checksum, sizeof(checksum));
// 发送数据包
if (sendto(sockfd, buffer, sizeof(header) + read_size + sizeof(checksum), 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("Sendto failed");
} else {
// 记录已发送的数据包信息
UnconfirmedPacket packet;
packet.header = header;
packet.data = buffer;
packet.send_time = time(nullptr);
{
lock_guard<mutex> guard(unconfirmedPacketsMutex);
unconfirmedPackets.push(packet);
}
}
packet_seq++;
}
imageFile.close();
}
// 其他类型消息(如表情等)发送函数可以类似地按照上述思路编写,这里省略
// 简单的消息ID生成函数(示例,可改进)
unsigned int generateMessageId() {
return static_cast<unsigned int>(time(nullptr)) * 1000 + rand();
}
// 处理未确认数据包的超时和重传的线程函数
void handleUnconfirmedPacketsThread() {
while (true) {
this_thread::sleep_for(chrono::milliseconds(500)); // 每隔一定时间检查一次,可调整间隔时间
vector<UnconfirmedPacket> toRetransmit;
{
lock_guard<mutex> guard(unconfirmedPacketsMutex);
time_t current_time = time(nullptr);
while (!unconfirmedPackets.empty()) {
// 假设超时时间为5秒(可根据实际调整)
if (current_time - unconfirmedPackets.front().send_time > 5) {
toRetransmit.push_back(unconfirmedPackets.front());
unconfirmedPackets.pop();
} else {
break;
}
}
}
for (const auto& packet : toRetransmit) {
// 重传数据包
if (sendto(sockfd, packet.data, sizeof(packet.header) + packet.header.data_length + sizeof(unsigned short), 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("Resend failed");
} else {
// 更新发送时间
UnconfirmedPacket new_packet = packet;
new_packet.send_time = time(nullptr);
{
lock_guard<mutex> guard(unconfirmedPacketsMutex);
}
}
}

浙公网安备 33010602011771号