1. 构造函数与析构函数
cpp
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include"TcpServer.h"
// 构造函数:初始化中介者指针
TcpServer::TcpServer(INetMediator* pTcpS) {
m_pMediator = pTcpS;
}
// 析构函数:默认空实现
TcpServer::~TcpServer() {
}
2. 初始化网络(initNet)
功能:加载 Winsock 库、创建 TCP 套接字、绑定 IP 端口、监听连接、创建接受连接线程
cpp
// 初始化网络(加载库,创建套接字,绑定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; // 绑定所有网卡IP
err = 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)
功能:循环接受客户端连接,为每个客户端创建独立的数据接收线程
cpp
// 接收连接的线程函数(静态成员函数)
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;
}
// 连接成功,打印客户端IP
cout << "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 处理实际数据接收
cpp
// 接收数据线程函数(静态成员函数)
unsigned __stdcall TcpServer::recvThread(void* IpVoid) {
TcpServer* pThis = (TcpServer*)IpVoid; // 转换为当前类实例指针
pThis->recvData(); // 调用成员函数接收数据
return 1;
}
5. 接收数据(recvData)
功能:从客户端套接字接收数据,处理粘包问题,通过中介者转发数据
cpp
// 接收数据核心函数
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)
功能:向指定客户端发送数据,先发送长度再发送数据,避免粘包
cpp
// 发送数据(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
运行
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. 数据分发核心函数(协议解析与函数回调)
cpp
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. 注册业务处理函数(数据库校验 + 结果响应)
cpp
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. 登录业务处理函数(数据库校验 + 在线状态维护)
cpp
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;
// 查询手机号对应的密码和ID
sprintf_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 {
// 提取密码和ID
string 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. 聊天业务处理函数(在线判断 + 消息转发)
cpp
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. 添加好友回复处理函数(数据库双向绑定 + 列表更新)
cpp
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]);
}
}