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]);}
}