当前位置: 首页 > news >正文

Kernel

1. 构造函数与析构函数

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include"TcpServer.h"// 构造函数:初始化中介者指针
TcpServer::TcpServer(INetMediator* pTcpS) {m_pMediator = pTcpS;
}// 析构函数:默认空实现
TcpServer::~TcpServer() {}

2. 初始化网络(initNet)

功能:加载 Winsock 库、创建 TCP 套接字、绑定 IP 端口、监听连接、创建接受连接线程

// 初始化网络(加载库,创建套接字,绑定IP端口,监听,创建接受链接的线程)
bool TcpServer::initNet() {// 1. 加载Winsock库(版本2.2)WORD version = MAKEWORD(2, 2);WSADATA data = {};int err = WSAStartup(version, &data);if (0 != err) {cout << "WSAStartup fail" << endl;return false;}// 验证版本是否匹配if (2 != HIBYTE(data.wVersion) || 2 != LOBYTE(data.wVersion)) {cout << "WSAStartup version fail" << endl;return false;}cout << "WSAStartup success" << endl;// 2. 创建TCP套接字m_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (INVALID_SOCKET == m_sock) {cout << "socket error:" << WSAGetLastError << endl;return false;}cout << "socket success" << endl;// 3. 绑定IP和端口sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(TCP_PORT);  // TCP_PORT为宏定义的端口号addr.sin_addr.S_un.S_addr = INADDR_ANY;  // 绑定所有网卡IPerr = bind(m_sock, (sockaddr*)&addr, sizeof(addr));if (SOCKET_ERROR == err) {cout << "bind error:" << WSAGetLastError() << endl;return false;}cout << "bind success" << endl;// 4. 开始监听连接(监听队列长度由LISTEN_QUEUE_LENGTH宏定义)err = listen(m_sock, LISTEN_QUEUE_LENGTH);if (SOCKET_ERROR == err) {cout << "listen error:" << WSAGetLastError() << endl;return false;}cout << "listen success" << endl;// 5. 创建接受连接的线程(阻塞的accept放在线程中执行)m_handle = (HANDLE)_beginthreadex(0,                      // 线程安全级别(默认)0,                      // 堆栈大小(默认1M)&acceptThread,          // 线程函数地址this,                   // 线程参数(当前类实例指针)0,                      // 创建后立即运行nullptr                 // 不返回线程ID);return true;
}

3. 接受连接线程函数(acceptThread)

功能:循环接受客户端连接,为每个客户端创建独立的数据接收线程

// 接收连接的线程函数(静态成员函数)
unsigned __stdcall TcpServer::acceptThread(void* IpVoid) {TcpServer* pThis = (TcpServer*)IpVoid;  // 转换为当前类实例指针SOCKET sock = INVALID_SOCKET;           // 客户端套接字sockaddr_in addrClient = {};            // 客户端地址信息int size = sizeof(addrClient);          // 地址结构体大小HANDLE handle = nullptr;                // 接收数据线程句柄unsigned int threadID = 0;              // 接收数据线程ID// 循环接受连接(m_bRunning控制线程退出)while (pThis->m_bRunning) {// 阻塞等待客户端连接sock = accept(pThis->m_sock, (sockaddr*)&addrClient, &size);if (INVALID_SOCKET == sock) {cout << "accept error:" << WSAGetLastError() << endl;continue;}// 连接成功,打印客户端IPcout << "accept success:" << inet_ntoa(addrClient.sin_addr) << endl;// 为该客户端创建独立的接收数据线程handle = (HANDLE)_beginthreadex(0,                      // 线程安全级别(默认)0,                      // 堆栈大小(默认1M)&TcpServer::recvThread, // 接收数据线程函数地址pThis,                  // 线程参数(当前类实例指针)0,                      // 创建后立即运行&threadID               // 返回线程ID);// 保存线程ID与客户端套接字的映射关系pThis->m_mapThreadIdToSocket[threadID] = sock;// 保存线程句柄(用于后续回收)if (handle) {pThis->m_listHandle.push_back(handle);}}return 1;
}

4. 接收数据线程函数(recvThread)

功能:线程入口函数,调用 recvData 处理实际数据接收

// 接收数据线程函数(静态成员函数)
unsigned __stdcall TcpServer::recvThread(void* IpVoid) {TcpServer* pThis = (TcpServer*)IpVoid;  // 转换为当前类实例指针pThis->recvData();                      // 调用成员函数接收数据return 1;
}

5. 接收数据(recvData)

功能:从客户端套接字接收数据,处理粘包问题,通过中介者转发数据

// 接收数据核心函数
void TcpServer::recvData() {Sleep(5);  // 短暂休眠,确保接收连接线程已保存套接字映射// 获取当前线程ID,通过映射表找到对应的客户端套接字unsigned int threadID = GetCurrentThreadId();SOCKET sock = INVALID_SOCKET;if (m_mapThreadIdToSocket.count(threadID) > 0) {sock = m_mapThreadIdToSocket[threadID];} else {cout << "TcpServer::recvData socket error, threadID:" << threadID << endl;return;}int offset = 0;         // 数据接收偏移量(处理粘包)int nRecvNum = 0;       // 单次接收数据长度int packSize = 0;       // 数据包总长度(先接收长度,再接收数据)// 循环接收数据(m_bRunning控制线程退出)while (m_bRunning) {offset = 0;// 1. 先接收数据包长度(解决粘包问题)nRecvNum = recv(sock, (char*)&packSize, sizeof(int), 0);if (nRecvNum <= 0) {cout << "TcpServer::recvData get packSize error:" << WSAGetLastError() << endl;break;}// 2. 申请内存存储数据包char* pack = new char[packSize];// 3. 循环接收完整数据(处理单次接收不完整的情况)while (packSize > 0) {nRecvNum = recv(sock, pack + offset, packSize, 0);if (nRecvNum > 0) {packSize -= nRecvNum;  // 剩余未接收长度offset += nRecvNum;    // 已接收数据偏移} else {cout << "TcpServer::recv data error:" << WSAGetLastError() << endl;delete[] pack;  // 接收失败,释放内存pack = nullptr;goto END_RECV;  // 退出循环}}// 4. 通过中介者转发接收的数据(sock作为客户端标识)m_pMediator->transmitData(pack, offset, sock);delete[] pack;  // 释放数据包内存pack = nullptr;END_RECV:break;}
}

6. 发送数据(sendData)

功能:向指定客户端发送数据,先发送长度再发送数据,避免粘包

// 发送数据(TCP协议)
// data: 要发送的数据缓冲区
// len: 数据长度
// to: 目标客户端套接字(TCP用套接字标识目标)
bool TcpServer::sendData(char* data, int len, long to) {// 1. 校验参数合法性if (data == nullptr || len <= 0) {cout << "TcpClient::sendData paramter error:" << endl;return false;}SOCKET destSock = (SOCKET)to;  // 转换为套接字类型// 2. 先发数据长度(4字节),解决粘包问题int nSendNum = send(destSock, (char*)&len, sizeof(len), 0);if (nSendNum == SOCKET_ERROR) {cout << "TcpClient::sendData send len error:" << WSAGetLastError() << endl;return false;}// 3. 再发送实际数据nSendNum = send(destSock, data, len, 0);if (nSendNum == SOCKET_ERROR) {cout << "TcpClient::send data error:" << WSAGetLastError() << endl;return false;}return true;
}

7. 关闭网络(unInitNet)

功能:回收线程资源、关闭所有套接字、卸载 Winsock 库n

1. 核心初始化函数(函数指针数组初始化)

cpp

运行

void Kernel::setTypeFunArr()
{cout << "__func__" << endl;// 初始化函数指针数组,将业务处理函数与协议类型绑定memset(m_typeFunArr, 0, sizeof(m_typeFunArr));m_typeFunArr[DEF_PROT_REGISTER_RQ - DEF_BASE] = &Kernel::dealRegisterRq;m_typeFunArr[DEF_PROT_LOGIN_RQ - DEF_BASE] = &Kernel::dealLoginRq;m_typeFunArr[DEF_PROT_FRIEND_OFFLINE - DEF_BASE] = &Kernel::dealOfflineRq;m_typeFunArr[DEF_PROT_CHAT_INFO_RQ - DEF_BASE] = &Kernel::dealChatRq;m_typeFunArr[DEF_PROT_PROT_ADD_FRIEND_RQ - DEF_BASE] = &Kernel::dealAddFriendRq;m_typeFunArr[DEF_PROT_PROT_ADD_FRIEND_RS - DEF_BASE] = &Kernel::dealAddFriendRs;
}

2. 数据分发核心函数(协议解析与函数回调)

void Kernel::dealData(char* data, int len, long from)
{cout << "__func__" << endl;// 解析协议类型prot_type type = *(prot_type*)data;// 计算函数指针数组下标int index = type - DEF_BASE;// 合法性校验if (index >= 0 && index < TYPE_FUN_LEN){deal_fun pFun = m_typeFunArr[index];if (pFun){// 回调对应业务处理函数(类成员函数指针调用关键点)(this->*pFun)(data, len, from);}else{cout << "type1" << type << endl;}}else{cout << "type2" << type << endl;}
}

3. 注册业务处理函数(数据库校验 + 结果响应)

void Kernel::dealRegisterRq(char* data, int len, long from)
{cout << "__func__" << endl;// 拆包PROT_REGISTER_RQ* rq = (PROT_REGISTER_RQ*)data;list<string>lstStr;char szSql[1024] = "";PROT_REGISTER_RS rs;// 校验昵称是否重复sprintf_s(szSql, "select name from t_user where name='%s';", rq->nick);if (!m_sql.SelectMySql(szSql, 1, lstStr)) {cout << "查询数据库失败" << szSql << endl;return;}if (lstStr.size() != 0) {rs.result = REGISTER_NAME_EXISTS;}else {// 校验手机号是否重复lstStr.clear();sprintf_s(szSql, "select tel from t_user where tel='%s';", rq->tel);if (!m_sql.SelectMySql(szSql, 1, lstStr)) {cout << "查询数据库失败" << szSql << endl;return;}if (lstStr.size() != 0) {rs.result = REGISTER_TEL_EXISTS;}else {// 插入注册数据sprintf_s(szSql,"insert into t_user (name,tel,password,feeling,iconid) values ('%s','%s','%s','写代码太难了',9);",rq->nick, rq->tel, rq->pass);if (m_sql.UpdateMySql(szSql)) {rs.result = REGISTER_SUCC;}}}// 响应客户端m_pMediator->sendData((char*)&rs, sizeof(rs), from);
}

4. 登录业务处理函数(数据库校验 + 在线状态维护)

void Kernel::dealLoginRq(char* data, int len, long from)
{cout << "__func__" << endl;PROT_LOGIN_RQ* rq = (PROT_LOGIN_RQ*)data;list<string>lstStr;char szSql[1024] = "";PROT_LOGIN_RS rs;// 查询手机号对应的密码和IDsprintf_s(szSql, "select password, id from t_user where tel='%s';", rq->tel);if (!m_sql.SelectMySql(szSql, 2, lstStr)) {cout << "查询数据库失败" << szSql << endl;return;}if (lstStr.size() == 0) {rs.result = LOGIN_NOTEXIST;}else {// 提取密码和IDstring pass = lstStr.front();lstStr.pop_front();int id = stoi(lstStr.front());// 密码校验if (pass == rq->pass) {rs.result = LOGIN_SUC;rs.id = id;// 维护用户ID与Socket映射(在线状态)m_mapIdToSocket[id] = from;// 推送用户及好友信息getUserAndFriendInfo(id);m_pMediator->sendData((char*)&rs, sizeof(rs), from);return;}else {rs.result = LOGIN_PASSERROR;}}m_pMediator->sendData((char*)&rs, sizeof(rs), from);
}

5. 聊天业务处理函数(在线判断 + 消息转发)

void Kernel::dealChatRq(char* data, int len, long from)
{cout << "__func__" << endl;PROT_CHAT_INFO_RQ* rq = (PROT_CHAT_INFO_RQ*)data;// 检查接收方是否在线(核心:map查找在线状态)if (m_mapIdToSocket.count(rq->friendid)) {// 在线则转发消息m_pMediator->sendData(data, len, m_mapIdToSocket[rq->friendid]);}else {// 不在线返回失败响应PROT_CHAT_INFO_RS rs;rs.friendid = rq->myid;rs.myid = rq->friendid;rs.result = CHAT_RESULT_FAIL;m_pMediator->sendData((char*)&rs, sizeof(rs), from);}
}

6. 添加好友回复处理函数(数据库双向绑定 + 列表更新)

void Kernel::dealAddFriendRs(char* data, int len, long from)
{cout << "__func__" << endl;PROT_ADD_FRIEND_RS* rs = (PROT_ADD_FRIEND_RS*)data;// 同意添加则写入双向好友关系if (ADD_FRIEND_ACCEPT == rs->result){char szSql[1024] = "";// A->B 好友关系sprintf_s(szSql, "insert into t_friend values (%d,%d);", rs->destid, rs->myid);if (!m_sql.UpdateMySql(szSql)) {cout << "插入数据库失败" << szSql << endl;return;}// B->A 好友关系(双向绑定)sprintf_s(szSql, "insert into t_friend values (%d,%d);", rs->myid, rs->destid);if (!m_sql.UpdateMySql(szSql)) {cout << "插入数据库失败" << szSql << endl;return;}// 更新双方好友列表getUserAndFriendInfo(rs->destid);}// 响应请求方if (m_mapIdToSocket.count(rs->destid) > 0){m_pMediator->sendData(data, len, m_mapIdToSocket[rs->destid]);}
}

http://www.dtcms.com/a/592741.html

相关文章:

  • C语言变量与输入输出详解——从printf到scanf的全掌握
  • MATLAB倍频转换效率分析与最佳匹配角模拟
  • Resilience4j 入门与实战
  • 智能投资,快速回本:复合机器人如何缩短你的投资回收期?
  • 5 Repository 层接口
  • 新乡网站优化平台id怎么打开wordpress
  • 小网站推荐会展官方网站建设
  • Springboot 启动过程及源码分析
  • STM32进行步进电机控制(PWM模式+翻转模式)
  • 信号系统常见的整体特性分类
  • PPT: Pre-trained Prompt Tuning - 预训练提示调优详解
  • 【RK3568】- 文件系统打包
  • 项目四:Dify智能开发与应用(零售企业基于Dify搭建会员智能运营平台)
  • 公司网站开发费计入什么科目迅当网络深圳外贸网站建设
  • 【C++11】右值引用+移动语义+完美转发
  • 商城系统的部署流程
  • 云朵课堂网站开发怎么收费装修公司口碑
  • python中numpy库学习笔记(2)
  • 【穿越Effective C++】条款16:成对使用new和delete时要采用相同形式——内存管理的精确匹配原则
  • 自己做的网站百度搜不到网站备案查询 工信部
  • 数据结构期中复习
  • TradingAgents-CN v1.0.0-preview 重磅发布!全新架构
  • 基于瑞萨 RA6M5 开发板的声源定位系统设计与实现
  • Vue 2 转 Vue 3, 差异不同点汇总, 快速上手vue3
  • 工业级环境传感器的网络通信与协议兼容性分析
  • 个人网站建设 免费下载一个公司备案两个网站
  • PR(1)11.10
  • 数据结构(19)
  • LWIP--以太网
  • 3分钟搞定,接口管理工具PostIn安装和配置