IM聊天代码

客户端

Headers

inet

inet.h
#pragma once 
#include<Winsock2.h>//#pragma comment(lib,"Ws2_32.lib")class INetMediator;
class INet {
public:INet(){}virtual ~INet(){}//初始化网络virtual bool initNet() = 0;//接收数据virtual void recvData() = 0;//发送数据(TCP由socket决定发送给谁,UDP由IP决定发送给谁)//SOCKET是UINT类型,IP是ULONG类型,long类型两个都可以包括virtual bool sendData(char* data, int len, long to) = 0;//关闭网络virtual void unInitNet() = 0;protected:INetMediator* m_pMediator;};
packDef.h
#pragma once#define  _DEF_TCP_PORT           (67890)
#define  _DEF_SERVER_IP          ("192.168.248.1")
#define  _DEF_NAME_MAX           (100)
#define  _DEF_CONTENT_LENGHT     (4096)
#define  _DEF_PROTOCOL_COUNT     (10)//协议头
#define _DFE_PROTOCOL_BASE       (1000)
// 注册请求
#define _DEF_TCP_REGISTER_RQ     (_DFE_PROTOCOL_BASE + 1)
// 注册回复
#define _DEF_TCP_REGISTER_RS     (_DFE_PROTOCOL_BASE + 2)
// 登录请求
#define _DEF_TCP_LOGIN_RQ        (_DFE_PROTOCOL_BASE + 3)
// 登录回复
#define _DEF_TCP_LOGIN_RS        (_DFE_PROTOCOL_BASE + 4)
// 聊天请求
#define _DEF_TCP_CHAT_RQ         (_DFE_PROTOCOL_BASE + 5)
// 聊天回复
#define _DEF_TCP_CHAT_RS         (_DFE_PROTOCOL_BASE + 6)
// 添加好友请求
#define _DEF_TCP_ADD_FRIEND_RQ   (_DFE_PROTOCOL_BASE + 7)
// 添加好友回复
#define _DEF_TCP_ADD_FRIEND_RS   (_DFE_PROTOCOL_BASE + 8)
// 下线请求
#define _DEF_TCP_OFFLINE_RQ      (_DFE_PROTOCOL_BASE + 9)
// 好友信息
#define _DEF_TCP_FRIEND_INFO     (_DFE_PROTOCOL_BASE + 10)// 重定义协议头变量
typedef int packType;//结果定义
//注册结果
#define register_success         (0)
#define register_name_repeat     (1)
#define register_tel_repeat      (2)
//登录结果
#define login_success            (0)
#define login_tel_not_exist      (1)
#define login_password_error     (2)
//聊天结果
#define send_success             (0)
#define send_fail                (1)
//添加好友结果
#define add_friend_success       (0)
#define add_friend_no_this_user  (1)
#define add_friend_user_refuse   (2)
#define add_friend_user_offline  (3)
//用户状态
#define _status_online           (0)
#define _status_offline          (1)//请求结构体
// 注册请求
typedef struct STRU_TCP_REGISTER_RQ{STRU_TCP_REGISTER_RQ():type(_DEF_TCP_REGISTER_RQ){memset(name,0,_DEF_NAME_MAX);memset(tel,0,_DEF_NAME_MAX);memset(password,0,_DEF_NAME_MAX);}//协议头packType type;//昵称char name[_DEF_NAME_MAX];//手机号char tel[_DEF_NAME_MAX];//密码char password[_DEF_NAME_MAX];}STRU_TCP_REGISTER_RQ;// 注册回复
typedef struct STRU_TCP_REGISTER_RS{STRU_TCP_REGISTER_RS():type(_DEF_TCP_REGISTER_RS),result(register_tel_repeat){}//协议头packType type;//注册结果int result;}STRU_TCP_REGISTER_RS;// 登录请求
typedef struct STRU_TCP_LOGIN_RQ{STRU_TCP_LOGIN_RQ():type(_DEF_TCP_LOGIN_RQ){memset(tel,0,_DEF_NAME_MAX);memset(password,0,_DEF_NAME_MAX);}//协议头packType type;//手机号char tel[_DEF_NAME_MAX];//密码char password[_DEF_NAME_MAX];}STRU_TCP_LOGIN_RQ;// 登录回复
typedef struct STRU_TCP_LOGIN_RS{STRU_TCP_LOGIN_RS():type(_DEF_TCP_LOGIN_RS),id(0),result(login_password_error){}//协议头packType type;//自己的idint id;//登录结果int result;}STRU_TCP_LOGIN_RS;// 聊天请求
typedef struct STRU_TCP_CHAT_RQ{STRU_TCP_CHAT_RQ():type(_DEF_TCP_CHAT_RQ),userId(0),friendId(0){memset(content,0,_DEF_CONTENT_LENGHT);}//协议头packType type;//聊天内容char content[_DEF_CONTENT_LENGHT];//自己是谁int userId;//和谁聊天int friendId;}STRU_TCP_CHAT_RQ;// 聊天回复
typedef struct STRU_TCP_CHAT_RS{STRU_TCP_CHAT_RS():type(_DEF_TCP_CHAT_RS),result(send_fail),friendId(0){}//协议头packType type;//聊天结果int result;//好友的idint friendId;}STRU_TCP_CHAT_RS;// 添加好友请求
typedef struct STRU_TCP_ADD_FRIEND_RQ{STRU_TCP_ADD_FRIEND_RQ():type(_DEF_TCP_ADD_FRIEND_RQ),userId(0){memset(friendName,0,_DEF_NAME_MAX);memset(userName,0,_DEF_NAME_MAX);}//协议头packType type;//自己是谁int userId;//对方昵称char friendName[_DEF_NAME_MAX];//我的昵称char userName[_DEF_NAME_MAX];}STRU_TCP_ADD_FRIEND_RQ;// 添加好友回复
typedef struct STRU_TCP_ADD_FRIEND_RS {STRU_TCP_ADD_FRIEND_RS() :type(_DEF_TCP_ADD_FRIEND_RS), result(add_friend_user_offline), userId(0), friendId(0){memset(friendName, 0, _DEF_NAME_MAX);}//协议头packType type;//添加好友结果int result;//好友昵称char friendName[_DEF_NAME_MAX];//自己的idint userId;//好友的idint friendId;}STRU_TCP_ADD_FRIEND_RS;// 下线请求
typedef struct STRU_TCP_OFFLINE_RQ{STRU_TCP_OFFLINE_RQ():type(_DEF_TCP_OFFLINE_RQ),userId(0){}//协议头packType type;//自己是谁int userId;}STRU_TCP_OFFLINE_RQ;//好友信息:协议头、头像id、好友id、昵称、签名、状态
typedef struct STRU_TCP_FRIEND_INFO {STRU_TCP_FRIEND_INFO() :type(_DEF_TCP_FRIEND_INFO),iconId(0),friendId(0),status(_status_offline){memset(name, 0, _DEF_NAME_MAX);memset(feeling, 0, _DEF_NAME_MAX);}//协议头packType type;int iconId;int friendId;char name[_DEF_NAME_MAX];char feeling[_DEF_NAME_MAX];int status;}STRU_TCP_FRIEND_INFO;
TcpClient.h
#pragma once
#include"inet.h"class TcpClient :public INet {
public:TcpClient(INetMediator* pMediator);~TcpClient();//初始化网络bool initNet();//接收数据void recvData();//发送数据(TCP由socket决定发送给谁,UDP由IP决定发送给谁)//SOCKET是UINT类型,IP是ULONG类型,long类型两个都可以包括bool sendData(char* data, int len, long to);//关闭网络void unInitNet();
private:static unsigned __stdcall recvThread(void* IpVoid);
private://SOCKETSOCKET m_sock;//句柄HANDLE m_handle;//退出标志位bool m_bStop;
};
UdpNet.h
#pragma once
#include"inet.h"class UdpNet :public INet {
public:UdpNet();~UdpNet();//初始化网络bool initNet();//接收数据void recvData();//发送数据(TCP由socket决定发送给谁,UDP由IP决定发送给谁)//SOCKET是UINT类型,IP是ULONG类型,long类型两个都可以包括bool sendData(char* data, int len, long to);//关闭网络void unInitNet();
};

inetmediator

inetMediator.h
#pragma once
#include<QObject>class INet;
class INetMediator:public QObject{Q_OBJECT
public:INetMediator();virtual ~INetMediator();//打开网络virtual bool openNet() = 0;//发送数据virtual bool sendData(char* data,int len,long to) = 0;//转发数据virtual void dealData(char* data, int len, long from) = 0;//关闭网络virtual void closeNet() = 0;
protected:INet* m_pNet;
};
TcpClientMediator.h
#include"inetMediator.h"class TcpClientMediator :public INetMediator {Q_OBJECT
signals:void SIG_dealData(char* data, int len, long from);
public:TcpClientMediator();~TcpClientMediator();//打开网络bool openNet();//发送数据bool sendData(char* data, int len, long to);//转发数据void dealData(char* data, int len, long from);//关闭网络void closeNet();
};
UdpNetTcpMediator.h

chat.h

#ifndef CHAT_H
#define CHAT_H#include <QDialog>namespace Ui {
class Chat;
}class Chat : public QDialog
{Q_OBJECT
signals://把聊天内容发给kernelvoid SIG_chatMessage(QString content,int friendId);
public:explicit Chat(QWidget *parent = 0);~Chat();//设置聊天窗口void setChatInfo(int friendId,QString friendName);//设置聊天内容到窗口void setChatContent(QString content);//设置好友不在线void setFriendOffline();private slots:void on_pb_send_clicked();private:Ui::Chat *ui;QString m_friendName;int m_friendId;
};#endif // CHAT_H

chatdialog.h

#ifndef CHATDIALOG_H
#define CHATDIALOG_H#include <QDialog>namespace Ui {
class ChatDialog;
}class ChatDialog : public QDialog
{Q_OBJECT
signals://把注册信息发给kernelvoid SIG_registerMessage(QString name,QString tel,QString passW);//把登录信息发给kernelvoid SIG_loginMessage(QString tel,QString passW);//发送关闭程序的信号给kernelvoid SIG_closeEvent();public:explicit ChatDialog(QWidget *parent = 0);~ChatDialog();//重写关闭窗口事件void closeEvent(QCloseEvent * event);private slots:void on_pb_clear_clicked();void on_pb_commit_clicked();void on_pb_clear_register_clicked();void on_pb_commit_register_clicked();private:Ui::ChatDialog *ui;
};#endif // CHATDIALOG_H

ckernel.h

#ifndef CKERNEL_H
#define CKERNEL_H#include <QObject>
#include"chatdialog.h"
#include"inetmediator/inetMediator.h"
#include"inet/packDef.h"
#include"friendlist.h"
#include<QMap>
#include"chat.h"//定义函数指针数组
class CKernel;
typedef void (CKernel::* PFun)(char* data, int len, long from);class CKernel : public QObject
{Q_OBJECT
public:explicit CKernel(QObject *parent = 0);~CKernel();QString name() const;signals:public slots://处理所有接收到的数据void slot_dealData(char* data, int len, long from);//处理注册信息,发给服务端void slot_registerMessage(QString name,QString tel,QString passW);//处理登录信息,发给服务端void slot_loginMessage(QString tel,QString passW);//处理聊天内容void slot_chatMessage(QString content,int friendId);//显示与好友的聊天窗口void slot_showChat(int friendId);//处理发送关闭程序的信号void slot_closeEvent();//处理发送下线信号void slot_offline();//通知kernel要添加好友void slot_addFriend();private://QT使用UTF-8,VS使用GB2312void utf8ToGb2312(QString utf8,char* gb,int len);QString gb2312ToUtf8(char* gb);//初始化协议头数组void setProtocolArr();//处理注册回复void dealRegisterRs(char* data, int len, long from);//处理登录回复void dealLoginRs(char* data, int len, long from);//处理好友信息void dealFriendInfo(char* data, int len, long from);//处理聊天请求void dealChatRq(char* data, int len, long from);//处理聊天回复void dealChatRs(char* data, int len, long from);//处理下线请求void dealOfflineRq(char* data, int len, long from);//处理添加好友请求void dealAddFriendRq(char* data, int len, long from);//处理添加好友回复void dealAddFriendRs(char* data, int len, long from);private:int m_id;QString m_name;INetMediator* m_pMediator;//登录&注册界面ChatDialog* m_pChatDlg;//好友列表界面FriendList* m_pFriendList;//协议头数组PFun m_pFun[_DEF_PROTOCOL_COUNT];//保存好友的useritemQMap<int,useritem*>m_mapFriendIdToUseritem;//保存聊天窗口QMap<int,Chat*>m_mapFriendIdToChat;};#endif // CKERNEL_H

friendlist.h

#ifndef FRIENDLIST_H
#define FRIENDLIST_H#include <QDialog>
#include<QVBoxLayout>
#include"useritem.h"
#include<QCloseEvent>
#include<QMenu>namespace Ui {
class FriendList;
}class FriendList : public QDialog
{Q_OBJECT
signals://发送下线信号给kernelvoid SIG_offline();//通知kernel要添加好友void SIG_addFriend();
public:explicit FriendList(QWidget *parent = 0);~FriendList();//设置自己的信息void setInfo(QString name,QString feeling,int iconId);//往界面上添加一个好友void addFriend(useritem* item);//重写关闭窗口事件void closeEvent(QCloseEvent *event);private slots:void on_pb_menu_clicked();//处理点击菜单项的信号void slot_triggered(QAction* action);private:Ui::FriendList *ui;//垂直布局的层QVBoxLayout* m_pLayout;//定义一个菜单栏指针QMenu* m_pMenu;
};#endif // FRIENDLIST_H

useritem.h

#ifndef USERITEM_H
#define USERITEM_H#include <QWidget>namespace Ui {
class useritem;
}class useritem : public QWidget
{Q_OBJECT
signals://显示与好友的聊天窗口void SIG_showChat(int friendId);
public:explicit useritem(QWidget *parent = 0);~useritem();//设置用户信息void setFriendInfo(int iconId,int friendId ,QString name,QString feeling,int status);//设置好友下线void setFriendOffline();const QString &name() const;private slots:void on_pb_icon_clicked();private:Ui::useritem *ui;int m_iconId;int m_friendId;QString m_name;QString m_feeling;int m_status;
};#endif // USERITEM_H

Sources

inet

TcpClient.cpp
#include"TcpClient.h"
#include"packDef.h"
#include<iostream>
#include"../inetmediator/TcpClientMediator.h"
#include<process.h>
using namespace std;TcpClient::TcpClient(INetMediator* pMediator):m_sock(INVALID_SOCKET),m_handle(NULL),m_bStop(false) {m_pMediator = pMediator;}
TcpClient::~TcpClient() {unInitNet();
}
//线程函数(调用recvData)
unsigned __stdcall TcpClient::recvThread(void* IpVoid) {TcpClient* const pThis = (TcpClient* const)IpVoid;pThis->recvData();return false;
}//初始化网络
bool TcpClient::initNet() {//1.加载库WORD wVersion = MAKEWORD(2, 2);WSAData data;int err = WSAStartup(wVersion, &data);if (err != 0){cout << "WSAStartup error" << err << endl;return false;}if (2 != LOBYTE(data.wVersion) || 2 != HIBYTE(data.wVersion)) {cout << "WSAStartup version error" << endl;return false;}else {cout << "WSAStartup success" << endl;}//2.创建套接字m_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (INVALID_SOCKET == m_sock) {cout << "socket error" << WSAGetLastError() << endl;return false;}else {cout << "socket success" << endl;}//3.连接服务端struct sockaddr_in addrServer;addrServer.sin_family = AF_INET;addrServer.sin_port = htons(_DEF_TCP_PORT);  //htons 转换成网络字节序addrServer.sin_addr.S_un.S_addr = inet_addr(_DEF_SERVER_IP);err = connect(m_sock, (sockaddr*)&addrServer, sizeof(addrServer));if (err == SOCKET_ERROR) {cout << "connect error " << WSAGetLastError() << endl;return false;}else {cout << "connect success" << endl;}// 4.创建接收数据的线程// CreateThread和ExitThread是一对,如果在线程里面使用C++运行时库(例如strcpy,在函数中申请空间不释放)// ExitThread也不会释放这个空间,就会造成内存泄漏。// _beginthreadex和_endthreadex是一对,_endthreadex会先回收空间,再调用ExitThread。// 创建线程的时候,操作系统分配: 句柄、线程id、内核对象// 回收线程:1、结束线程工作; 2、关闭句柄m_handle =(HANDLE)_beginthreadex(0,0,&recvThread,(void*)this,0,NULL);return true;
}//接收数据
void TcpClient::recvData() {int packSize = 0;int nRecvNum = 0;//偏移量int nOffset = 0;while (!m_bStop) {nOffset = 0;//先接收包大小nRecvNum = recv(m_sock, (char*)&packSize, sizeof(int), 0);if (nRecvNum > 0) {//new空间char* packBuf = new char[packSize];//再接收包内容while (packSize > 0) {nRecvNum = recv(m_sock, packBuf + nOffset, packSize, 0);if (nRecvNum > 0) {nOffset += nRecvNum;packSize -= nRecvNum;}else {cout << "recv error:" << WSAGetLastError() << endl;break;}}//TODO:接收成功,把数据传给中介者,在处理函数里面回收packBufm_pMediator->dealData(packBuf, nOffset, m_sock);}else {cout<<"recv error:"<< WSAGetLastError() << endl;break;}}
}//发送数据(TCP由socket决定发送给谁,UDP由IP决定发送给谁)
//SOCKET是UINT类型,IP是ULONG类型,long类型两个都可以包括
bool TcpClient::sendData(char* data, int len, long to) { //1.判断参数是否有效if (data == NULL || len <= 0) {cout << "paramater error" << endl;return false;}//2.先发包大小if (send(m_sock,(char*)& len,sizeof(int),0) <= 0) {cout << "send error:" << WSAGetLastError() << endl;return false;}//3.再发包内容if (send(m_sock,data,len,0) <= 0) {cout << "send error" << WSAGetLastError() << endl;return false;}return true;
}//关闭网络:回收线程、关闭套接字、卸载库
void TcpClient::unInitNet() {// 回收线程:1、结束线程工作; 2、关闭句柄m_bStop = true;if (m_handle) {if (WAIT_TIMEOUT == WaitForSingleObject(m_handle, 500)) {//如果等待超时,就强制杀死线程TerminateThread(m_handle, -1);}CloseHandle(m_handle);m_handle = NULL;}//关闭套接字、卸载库if (m_sock && INVALID_SOCKET != m_sock) {closesocket(m_sock);}WSACleanup();
}
UdpNet.cpp
#include"UdpNet.h"UdpNet::UdpNet() {}
UdpNet::~UdpNet() {}//初始化网络
bool UdpNet::initNet() {return false;
}//接收数据
void UdpNet::recvData() {}//发送数据(TCP由socket决定发送给谁,UDP由IP决定发送给谁)
//SOCKET是UINT类型,IP是ULONG类型,long类型两个都可以包括
bool UdpNet::sendData(char* data, int len, long to) {return false;
}//关闭网络
void UdpNet::unInitNet() {}

inetmediator

inetMediator.cpp
#include"inetMediator.h"INetMediator::INetMediator(){}INetMediator:: ~INetMediator(){}
TcpClientMediator.cpp
#include"TcpClientMediator.h"
#include"../inet/TcpClient.h"TcpClientMediator::TcpClientMediator() {m_pNet = new TcpClient(this);}
TcpClientMediator:: ~TcpClientMediator() {if (m_pNet) {m_pNet->unInitNet();delete m_pNet;m_pNet = NULL;}
}
//打开网络
bool TcpClientMediator::openNet() {return m_pNet->initNet();
}//发送数据
bool TcpClientMediator::sendData(char* data, int len, long to) {return m_pNet->sendData(data, len, to);}//转发数据
void TcpClientMediator::dealData(char* data, int len, long from) {//把接收到的数据传给kernelQ_EMIT SIG_dealData(data,len,from);}//关闭网络
void TcpClientMediator::closeNet() {return m_pNet->unInitNet();
}
UdpNetTcpMediator.cpp

chat.cpp

#include "chat.h"
#include "ui_chat.h"
#include<QTime>Chat::Chat(QWidget *parent) :QDialog(parent),ui(new Ui::Chat)
{ui->setupUi(this);
}Chat::~Chat()
{delete ui;
}//设置聊天窗口
void Chat::setChatInfo(int friendId, QString friendName)
{//保存m_friendId=friendId;m_friendName=friendName;//设置窗口标题setWindowTitle(QString("[%1]").arg(m_friendName));
}//设置聊天内容到窗口
void Chat::setChatContent(QString content)
{ui->tb_chat->append(QString("[%1] %2").arg(m_friendName).arg(QTime::currentTime().toString("hh:mm:ss")));ui->tb_chat->append(content);
}//设置好友不在线
void Chat::setFriendOffline()
{ui->tb_chat->append(QString("好友 [%1] 不在线").arg(m_friendName));
}void Chat::on_pb_send_clicked()
{//获取纯文本的输入内容QString content=ui->te_chat->toPlainText();if(content.isEmpty()||content.remove(" ").isEmpty()){ui->te_chat->setText("");return;}//2.获取带格式的内容content=ui->te_chat->toHtml();//3.清空输入窗口ui->te_chat->setText("");//4.把内容显示到浏览窗口上ui->tb_chat->append(QString("[我] %1").arg(QTime::currentTime().toString("hh:mm:ss")));ui->tb_chat->append(content);//5.发给kernelQ_EMIT SIG_chatMessage(content,m_friendId);
}

chatdialog.cpp

#include "chatdialog.h"
#include "ui_chatdialog.h"
#include<QMessageBox>ChatDialog::ChatDialog(QWidget *parent) :QDialog(parent),ui(new Ui::ChatDialog)
{ui->setupUi(this);
}ChatDialog::~ChatDialog()
{delete ui;
}//重写关闭窗口事件
void ChatDialog::closeEvent(QCloseEvent *event)
{Q_EMIT SIG_closeEvent();
}void ChatDialog::on_pb_clear_clicked()
{ui->le_tel->setText("");ui->le_password->setText("");
}void ChatDialog::on_pb_commit_clicked()
{//1.获取控件中的数据QString tel=ui->le_tel->text();QString passW=ui->le_password->text();QString telTmp=tel;QString passWTmp=passW;//2.校验数据合法性//2.1是否为空或者是全空格(判断全空格就是先移除全部空格,然后判断是否为空)if(tel.isEmpty()||passW.isEmpty()||telTmp.remove(" ").isEmpty()||passWTmp.remove(" ").isEmpty()){QMessageBox::about(this,"title","不能为空或全空格,请重新输入");return;}//2.2长度合法性(电话号长度为11,密码长度不能超过20)if(tel.length()!=11||passW.length()>20){QMessageBox::about(this,"title","长度错误,请重新输入");return;}//2.3内容合法性(电话号必须全是数字,昵称只允许中文、字母、数字、下划线,密码只允许字母、数字、下划线)——正则表达式//3.通过信号把数据发给kernelQ_EMIT SIG_loginMessage(tel,passW);}void ChatDialog::on_pb_clear_register_clicked()
{ui->le_name_register->setText("");ui->le_password_register->setText("");ui->le_tel_register->setText("");
}void ChatDialog::on_pb_commit_register_clicked()
{//1.获取控件中的数据QString name=ui->le_name_register->text();QString tel=ui->le_tel_register->text();QString passW=ui->le_password_register->text();QString nameTmp=name;QString telTmp=tel;QString passWTmp=passW;//2.校验数据合法性//2.1是否为空或者是全空格(判断全空格就是先移除全部空格,然后判断是否为空)if(name.isEmpty()||tel.isEmpty()||passW.isEmpty()||nameTmp.remove(" ").isEmpty()||telTmp.remove(" ").isEmpty()||passWTmp.remove(" ").isEmpty()){QMessageBox::about(this,"title","不能为空或全空格,请重新输入");return;}//2.2长度合法性(电话号长度为11,昵称和密码长度不能超过20)if(tel.length()!=11||name.length()>20||passW.length()>20){QMessageBox::about(this,"title","长度错误,请重新输入");return;}//2.3内容合法性(电话号必须全是数字,昵称只允许中文、字母、数字、下划线,密码只允许字母、数字、下划线)——正则表达式//3.通过信号把数据发给kernelQ_EMIT SIG_registerMessage(name,tel,passW);}

ckernel.cpp

#include "ckernel.h"
#include"inetmediator/TcpClientMediator.h"
#include<QDebug>
#include<QMessageBox>
#include"useritem.h"
#include<QTextCodec>
#include<QInputDialog>CKernel::CKernel(QObject *parent) : QObject(parent)
{qDebug()<<__func__;//初始化协议头数组setProtocolArr();//new好友列表界面m_pFriendList=new FriendList;//绑定下线的信号和槽函数connect(m_pFriendList,SIGNAL(SIG_offline()),this,SLOT(slot_offline()));//绑定添加好友的信号和槽函数connect(m_pFriendList,SIGNAL(SIG_addFriend()),this,SLOT(slot_addFriend()));//new窗口对象m_pChatDlg=new ChatDialog;m_pChatDlg->showNormal();//绑定发送注册信息的信号和槽函数connect(m_pChatDlg,SIGNAL(SIG_registerMessage(QString,QString,QString)),this,SLOT(slot_registerMessage(QString,QString,QString)));//绑定发送登录信息的信号和槽函数connect(m_pChatDlg,SIGNAL(SIG_loginMessage(QString,QString)),this,SLOT(slot_loginMessage(QString,QString)));//绑定关闭程序的信号和槽函数connect(m_pChatDlg,SIGNAL(SIG_closeEvent()),this,SLOT(slot_closeEvent()));//new中介者类的对象m_pMediator=new TcpClientMediator;//打开网络if(!m_pMediator->openNet()){QMessageBox::about(m_pChatDlg,"message","network error");exit(0);//退出程序}connect(m_pMediator,SIGNAL(SIG_dealData(char*, int, long)),this,SLOT(slot_dealData(char*,int,long)));//    //测试:给服务端发送数据
//    m_pMediator->sendData("I am Client",sizeof("I am Client"),123);}CKernel::~CKernel()
{qDebug()<<__func__;if(m_pMediator){m_pMediator->closeNet();delete m_pMediator;m_pMediator=nullptr;}if(m_pChatDlg){m_pChatDlg->hide();delete m_pChatDlg;m_pChatDlg=nullptr;}if(m_pFriendList){m_pFriendList->hide();delete m_pFriendList;m_pFriendList=nullptr;}for(auto ite=m_mapFriendIdToChat.begin();ite!=m_mapFriendIdToChat.end();){Chat* ch=*ite;if(ch){ch->hide();delete ch;ch=nullptr;}ite=m_mapFriendIdToChat.erase(ite);}}//处理所有接收到的数据
void CKernel::slot_dealData(char *data, int len, long from)
{qDebug()<<__func__;//qDebug()<<data;//1.取出协议头packType type = *(packType*)data;//2.计算数组下标int index = type - _DFE_PROTOCOL_BASE - 1;//3.判断协议头是否在有效范围内if (index >= 0 && index < _DEF_PROTOCOL_COUNT) {PFun pf = m_pFun[index];if (pf) {(this->*pf)(data, len, from);}else {//如果type错了,两个原因:1.发送的时候type不对(结构体初始化赋值的不对)2.绑定协议头数组没绑定这个协议qDebug()<< "type2 error:" << type << endl;}}else {//如果type错了,两个原因:1.发送的时候type不对(结构体初始化赋值的不对)2.接收函数里面nOffset没清零qDebug() << "type1 error:" << type << endl;}//4.回收空间delete[] data;}//处理注册信息,发给服务端
void CKernel::slot_registerMessage(QString name, QString tel, QString passW)
{qDebug()<<__func__;//1.装包STRU_TCP_REGISTER_RQ rq;//strcpy_s(rq.name,name.toStdString().c_str());utf8ToGb2312(name,rq.name,sizeof(rq.name));strcpy_s(rq.tel,tel.toStdString().c_str());strcpy_s(rq.password,passW.toStdString().c_str());//2.发给服务端m_pMediator->sendData((char*)&rq,sizeof(rq),27);}//处理登录信息,发给服务端
void CKernel::slot_loginMessage(QString tel, QString passW)
{qDebug()<<__func__;//1.装包STRU_TCP_LOGIN_RQ rq;strcpy_s(rq.tel,tel.toStdString().c_str());strcpy_s(rq.password,passW.toStdString().c_str());//2.发给服务端m_pMediator->sendData((char*)&rq,sizeof(rq),27);
}
//处理聊天内容
void CKernel::slot_chatMessage(QString content, int friendId)
{qDebug()<<__func__;//1.打包STRU_TCP_CHAT_RQ rq;strcpy_s(rq.content,content.toStdString().c_str());rq.friendId=friendId;rq.userId=m_id;//2.发给服务端m_pMediator->sendData((char*)&rq,sizeof(rq),213);}
//显示与好友的聊天窗口
void CKernel::slot_showChat(int friendId)
{qDebug()<<__func__;//1.判断是否有该好友的聊天窗口
//    for(auto ite=m_mapFriendIdToChat.begin();ite!=m_mapFriendIdToChat.end();ite++){
//        int id = ite.key();
//            qDebug()<<id;
//    }
//    qDebug()<< "friendId:" << friendId;if(m_mapFriendIdToChat.count(friendId)>0){Chat* ch=m_mapFriendIdToChat[friendId];if(ch){ch->showNormal();}}
}//处理发送关闭程序的信号
void CKernel::slot_closeEvent()
{qDebug()<<__func__;if(m_pMediator){m_pMediator->closeNet();delete m_pMediator;m_pMediator=nullptr;}if(m_pChatDlg){m_pChatDlg->hide();delete m_pChatDlg;m_pChatDlg=nullptr;}if(m_pFriendList){m_pFriendList->hide();delete m_pFriendList;m_pFriendList=nullptr;}for(auto ite=m_mapFriendIdToChat.begin();ite!=m_mapFriendIdToChat.end();){Chat* ch=*ite;if(ch){ch->hide();delete ch;ch=nullptr;}ite=m_mapFriendIdToChat.erase(ite);}}//处理发送下线信号
void CKernel::slot_offline()
{qDebug()<<__func__;//1.给服务端发送下线请求STRU_TCP_OFFLINE_RQ rq;rq.userId=m_id;m_pMediator->sendData((char*)&rq,sizeof(rq),789);//2.回收资源slot_closeEvent();
}void CKernel::slot_addFriend()
{qDebug()<<__func__;//1.弹出输入框,判断好友的昵称QString name=QInputDialog::getText(m_pFriendList,"添加好友","请输入姓名");QString nameTmp=name;//2.校验昵称合法性if(name.isEmpty()||nameTmp.remove(" ").isEmpty()||name.length()>20){QMessageBox::about(m_pFriendList,"title","无效的昵称");return;}//3.判断是不是自己if(m_name==name){QMessageBox::about(m_pFriendList,"title","昵称不能是自己");return;}//4.判断是不是已经是好友了for(auto ite=m_mapFriendIdToUseritem.begin();ite!=m_mapFriendIdToUseritem.end();ite++){useritem* item=*ite;if(item){if(name==item->name()){QMessageBox::about(m_pFriendList,"title",QString("[%1] 已经是你的好友").arg(name));return;}}}//5.打包,发给服务端STRU_TCP_ADD_FRIEND_RQ rq;rq.userId=m_id;strcpy_s(rq.userName,m_name.toStdString().c_str());utf8ToGb2312(name,rq.friendName,sizeof(rq.friendName));//strcpy_s(rq.friendName,name.toStdString().c_str());m_pMediator->sendData((char*)&rq,sizeof(rq),78);
}void CKernel::utf8ToGb2312(QString utf8, char *gb, int len)
{QTextCodec* gb2312=QTextCodec::codecForName("gb2312");QByteArray ba=gb2312->fromUnicode(utf8);strcpy_s(gb,len,ba.data());
}QString CKernel::gb2312ToUtf8(char *gb)
{QTextCodec* gb2312=QTextCodec::codecForName("gb2312");return gb2312->toUnicode(gb);
}//初始化协议头数组
void CKernel::setProtocolArr(){qDebug()<<__func__;//给数组初始化memset(m_pFun, 0, sizeof(m_pFun));//绑定协议头数组m_pFun[_DEF_TCP_REGISTER_RS - _DFE_PROTOCOL_BASE - 1] = &CKernel::dealRegisterRs;m_pFun[_DEF_TCP_LOGIN_RS - _DFE_PROTOCOL_BASE - 1] = &CKernel::dealLoginRs;m_pFun[_DEF_TCP_FRIEND_INFO - _DFE_PROTOCOL_BASE - 1] = &CKernel::dealFriendInfo;m_pFun[_DEF_TCP_CHAT_RQ - _DFE_PROTOCOL_BASE - 1] = &CKernel::dealChatRq;m_pFun[_DEF_TCP_CHAT_RS - _DFE_PROTOCOL_BASE - 1] = &CKernel::dealChatRs;m_pFun[_DEF_TCP_OFFLINE_RQ - _DFE_PROTOCOL_BASE - 1]=&CKernel::dealOfflineRq;m_pFun[_DEF_TCP_ADD_FRIEND_RQ - _DFE_PROTOCOL_BASE - 1]=&CKernel::dealAddFriendRq;m_pFun[_DEF_TCP_ADD_FRIEND_RS - _DFE_PROTOCOL_BASE - 1]=&CKernel::dealAddFriendRs;
}//处理注册回复
void CKernel::dealRegisterRs(char* data, int len, long from){qDebug()<<__func__;//1.拆包STRU_TCP_REGISTER_RS* rs=(STRU_TCP_REGISTER_RS*)data;//2.根据结果提示用户switch(rs->result){case register_success:QMessageBox::about(m_pChatDlg,"title","注册成功");break;case register_name_repeat:QMessageBox::about(m_pChatDlg,"title","昵称重复,注册失败");break;case register_tel_repeat:QMessageBox::about(m_pChatDlg,"title","电话号重复,注册失败");break;}
}//处理登录回复void CKernel::dealLoginRs(char* data, int len, long from){qDebug()<<__func__;//1.拆包STRU_TCP_LOGIN_RS* rs = (STRU_TCP_LOGIN_RS*)data;//2.根据结果显示switch(rs->result){case login_success:{//跳转到好友列表界面(隐藏当前界面,显示另一个界面)m_id=rs->id;m_pChatDlg->hide();m_pFriendList->showNormal();//              //测试代码,添加20个好友
//              for(int i=0;i<20;i++){
//                  useritem* item=new useritem;
//                  m_pFriendList->addFriend(item);
//              }break;}case login_tel_not_exist:QMessageBox::about(m_pChatDlg,"title","登录失败,用户不存在");break;case login_password_error:QMessageBox::about(m_pChatDlg,"title","登录失败,密码错误");break;default:break;}}//处理好友信息void CKernel::dealFriendInfo(char *data, int len, long from){qDebug()<<__func__;//1.拆包STRU_TCP_FRIEND_INFO* info=(STRU_TCP_FRIEND_INFO*)data;QString name=gb2312ToUtf8(info->name);QString feeling=gb2312ToUtf8(info->feeling);//2.判断是不是自己的信息if(m_id==info->friendId){//保存自己的昵称m_name=name;//设置自己的信息m_pFriendList->setInfo(name,feeling,info->iconId);return;}//3.是好友的信息,判断好友是否已经在列表上if(m_mapFriendIdToUseritem.count(info->friendId)>0){//4.如果好友在列表上,更新好友信息useritem* item=m_mapFriendIdToUseritem[info->friendId];item->setFriendInfo(info->iconId,info->friendId,name,feeling,info->status);}else{//5.如果好友不在列表上,new一个新的好友useritem* item=new useritem;//设置好友的信息item->setFriendInfo(info->iconId,info->friendId,name,feeling,info->status);//把好友添加到列表上m_pFriendList->addFriend(item);//保存好友的useritemm_mapFriendIdToUseritem[info->friendId]=item;//绑定显示好友的聊天窗口的信号和槽函数connect(item,SIGNAL(SIG_showChat(int)),this,SLOT(slot_showChat(int)));//创建一个与这个好友的聊天窗口Chat* ch=new Chat;//设置聊天窗口的信息ch->setChatInfo(info->friendId,name);//保存聊天窗口m_mapFriendIdToChat[info->friendId]=ch;//绑定发送聊天内容的信号和槽函数connect(ch,SIGNAL(SIG_chatMessage(QString,int)),this,SLOT(slot_chatMessage(QString,int)));}}//处理聊天请求void CKernel::dealChatRq(char *data, int len, long from){qDebug()<<__func__;//1.拆包STRU_TCP_CHAT_RQ* rq = (STRU_TCP_CHAT_RQ*)data;//2.找到聊天窗口,把聊天内容显示到聊天窗口上,显示窗口if(m_mapFriendIdToChat.count(rq->userId)>0){Chat* ch=m_mapFriendIdToChat[rq->userId];if(ch){ch->setChatContent(rq->content);ch->showNormal();}}}//处理聊天回复void CKernel::dealChatRs(char *data, int len, long from){qDebug()<<__func__;//1.拆包STRU_TCP_CHAT_RS* rs = (STRU_TCP_CHAT_RS*)data;//2.找到聊天窗口,在窗口上显示好友不在线if(m_mapFriendIdToChat.count(rs->friendId)>0){Chat* ch=m_mapFriendIdToChat[rs->friendId];if(ch){ch->setFriendOffline();ch->showNormal();}}}//处理下线请求void CKernel::dealOfflineRq(char *data, int len, long from){qDebug()<<__func__;//1.拆包STRU_TCP_OFFLINE_RQ* rq = (STRU_TCP_OFFLINE_RQ*)data;//2.找到好友的useritem,把头像设置成暗显if(m_mapFriendIdToUseritem.count(rq->userId)>0){useritem* item=m_mapFriendIdToUseritem[rq->userId];if(item){item->setFriendOffline();}}}//处理添加好友请求void CKernel::dealAddFriendRq(char *data, int len, long from){qDebug()<<__func__;//1.拆包STRU_TCP_ADD_FRIEND_RQ* rq=(STRU_TCP_ADD_FRIEND_RQ*)data;//2.弹出询问窗口STRU_TCP_ADD_FRIEND_RS rs;QString str=QString("用户 [%1] 想添加你为好友,是否同意?").arg(rq->userName);if (QMessageBox::Yes == QMessageBox::question(m_pFriendList, "add friend", str)) {rs.result=add_friend_success;}else {rs.result = add_friend_user_refuse;}rs.userId = rq->userId;rs.friendId=m_id;strcpy_s(rs.friendName , m_name.toStdString().c_str()) ;//3、把添加好友结果发给服务端m_pMediator->sendData((char*)&rs, sizeof(rs), 78);}//处理添加好友回复void CKernel::dealAddFriendRs(char *data, int len, long from){qDebug()<<__func__;//1.拆包STRU_TCP_ADD_FRIEND_RS* rs=(STRU_TCP_ADD_FRIEND_RS*)data;QString friendName=gb2312ToUtf8(rs->friendName);//2.根据结果提示用户switch(rs->result){case add_friend_success:QMessageBox::about(m_pFriendList,"title",QString("添加好友 [%1] 成功").arg(rs->friendName));break;case add_friend_no_this_user:QMessageBox::about(m_pFriendList,"title",QString("添加好友 [%1] 失败,用户不存在").arg(friendName));break;case add_friend_user_refuse:QMessageBox::about(m_pFriendList,"title",QString("[%1] 拒绝了你的好友请求").arg(rs->friendName));break;case add_friend_user_offline:QMessageBox::about(m_pFriendList,"title",QString("添加好友 [%1] 失败,好友不在线").arg(friendName));break;}}QString CKernel::name() const{return m_name;}//字符串:char*、std:string、QString//char* 是基础类型,std:string,QString都是封装的类
//char* 可以直接给std:string,QString赋值
//std::string.c_str()=>char*
//QString.toStdString()=>std::string

friendlist.cpp

#include "friendlist.h"
#include "ui_friendlist.h"
#include<QMessageBox>
#include<QDebug>FriendList::FriendList(QWidget *parent) :QDialog(parent),ui(new Ui::FriendList)
{ui->setupUi(this);//new一个垂直布局层的对象m_pLayout=new QVBoxLayout;//每个控件间的距离m_pLayout->setSpacing(3);//层距离外边框的距离m_pLayout->setContentsMargins(0,0,0,0);//把层设置到外面的大控件上ui->wdg_list->setLayout(m_pLayout);//new一个菜单栏的对象m_pMenu=new QMenu;//添加两个菜单项m_pMenu->addAction("添加好友");m_pMenu->addAction("系统设置");//绑定点击菜单项的信号和槽函数(信号是QMenu类发送的,我们只接收信号)connect(m_pMenu,SIGNAL(triggered(QAction*)),this,SLOT(slot_triggered(QAction*)));}FriendList::~FriendList()
{delete ui;
}
//设置自己的信息
void FriendList::setInfo(QString name, QString feeling, int iconId)
{ui->lb_name->setText(name);ui->le_feeling->setText(feeling);//拼接头像文件路径QString path=QString(":/tx/%1.png").arg(iconId);ui->pb_icon->setIcon(QIcon(path));
}//往界面上添加一个好友
void FriendList::addFriend(useritem *item)
{m_pLayout->addWidget(item);
}//重写关闭窗口事件
void FriendList::closeEvent(QCloseEvent *event)
{//忽略事件event->ignore();if(QMessageBox::Yes==QMessageBox::question(this,"title","确定关闭吗")){Q_EMIT SIG_offline();}
}//在鼠标点击位置向上显示菜单栏
void FriendList::on_pb_menu_clicked()
{//获取当前鼠标点击的位置QPoint pos=QCursor::pos();//获取菜单栏的绝对大小QSize size=m_pMenu->sizeHint();//显示菜单栏m_pMenu->exec(QPoint(pos.x(),pos.y()-size.height()));
}//处理点击菜单项的信号
void FriendList::slot_triggered(QAction *action)
{if("添加好友"==action->text()){//通知kernel要添加好友Q_EMIT SIG_addFriend();}else if("系统设置"==action->text()){qDebug()<<"系统设置";}
}

main.cpp

#include "ckernel.h"
#include <QApplication>int main(int argc, char *argv[])
{QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling);QApplication a(argc, argv);//ChatDialog w;//w.show();CKernel kernel;return a.exec();
}

useritem.cpp

#include "useritem.h"
#include "ui_useritem.h"
#include "./inet/packDef.h"
#include <QBitmap>
#include<QDebug>useritem::useritem(QWidget *parent) :QWidget(parent),ui(new Ui::useritem)
{ui->setupUi(this);
}useritem::~useritem()
{delete ui;
}//设置用户信息
void useritem::setFriendInfo(int iconId,int friendId, QString name, QString feeling, int status)
{//保存好友信息m_iconId=iconId;m_friendId=friendId;m_name=name;m_feeling=feeling;m_status=status;//设置到控件显示ui->lb_name->setText(m_name);ui->lb_feeling->setText(m_feeling);//拼接头像文件路径QString path=QString(":/tx/%3.png").arg(iconId);if(_status_online==m_status){//在线,亮显ui->pb_icon->setIcon(QIcon(path));}else{//不在线,暗显QBitmap bit;bit.load(path);ui->pb_icon->setIcon(bit);}//重绘this->repaint();
}//设置好友下线
void useritem::setFriendOffline()
{//设置为下线状态m_status=_status_offline;//设置头像暗显QString path=QString(":/tx/%1.png").arg(m_iconId);QBitmap bit;bit.load(path);ui->pb_icon->setIcon(bit);//重绘this->repaint();qDebug()<<"userItem::setFriendOffline()";
}void useritem::on_pb_icon_clicked()
{Q_EMIT SIG_showChat(m_friendId);
}const QString &useritem::name() const
{return m_name;
}

服务端

inet

inet.h
#pragma once 
#include<Winsock2.h>#pragma comment(lib,"Ws2_32.lib")class INetMediator;
class INet {
public:INet(){}virtual ~INet(){}//初始化网络virtual bool initNet() = 0;//接收数据virtual void recvData() = 0;//发送数据(TCP由socket决定发送给谁,UDP由IP决定发送给谁)//SOCKET是UINT类型,IP是ULONG类型,long类型两个都可以包括virtual bool sendData(char* data, int len, long to) = 0;//关闭网络virtual void unInitNet() = 0;
protected:INetMediator* m_pMediator;
};
packDef.h
#pragma once #define  _DEF_TCP_PORT           (67890)
#define  _DEF_SERVER_IP          ("192.168.248.1")
#define  _DEF_NAME_MAX           (100)
#define  _DEF_CONTENT_LENGHT     (4096)
#define  _DEF_PROTOCOL_COUNT     (10)//协议头
#define _DFE_PROTOCOL_BASE       (1000)
// 注册请求
#define _DEF_TCP_REGISTER_RQ     (_DFE_PROTOCOL_BASE + 1)
// 注册回复
#define _DEF_TCP_REGISTER_RS     (_DFE_PROTOCOL_BASE + 2)
// 登录请求
#define _DEF_TCP_LOGIN_RQ        (_DFE_PROTOCOL_BASE + 3)
// 登录回复
#define _DEF_TCP_LOGIN_RS        (_DFE_PROTOCOL_BASE + 4)
// 聊天请求
#define _DEF_TCP_CHAT_RQ         (_DFE_PROTOCOL_BASE + 5)
// 聊天回复
#define _DEF_TCP_CHAT_RS         (_DFE_PROTOCOL_BASE + 6)
// 添加好友请求
#define _DEF_TCP_ADD_FRIEND_RQ   (_DFE_PROTOCOL_BASE + 7)
// 添加好友回复
#define _DEF_TCP_ADD_FRIEND_RS   (_DFE_PROTOCOL_BASE + 8)
// 下线请求
#define _DEF_TCP_OFFLINE_RQ      (_DFE_PROTOCOL_BASE + 9)
// 好友信息
#define _DEF_TCP_FRIEND_INFO     (_DFE_PROTOCOL_BASE + 10)// 重定义协议头变量
typedef int packType;//结果定义
//注册结果
#define register_success         (0)
#define register_name_repeat     (1)
#define register_tel_repeat      (2)
//登录结果
#define login_success            (0)
#define login_tel_not_exist      (1)
#define login_password_error     (2)
//聊天结果
#define send_success             (0)
#define send_fail                (1)
//添加好友结果
#define add_friend_success       (0)
#define add_friend_no_this_user  (1)
#define add_friend_user_refuse   (2)
#define add_friend_user_offline  (3)
//用户状态
#define _status_online           (0)
#define _status_offline          (1)//请求结构体
// 注册请求
typedef struct STRU_TCP_REGISTER_RQ {STRU_TCP_REGISTER_RQ() :type(_DEF_TCP_REGISTER_RQ){memset(name, 0, _DEF_NAME_MAX);memset(tel, 0, _DEF_NAME_MAX);memset(password, 0, _DEF_NAME_MAX);}//协议头packType type;//昵称char name[_DEF_NAME_MAX];//手机号char tel[_DEF_NAME_MAX];//密码char password[_DEF_NAME_MAX];}STRU_TCP_REGISTER_RQ;// 注册回复
typedef struct STRU_TCP_REGISTER_RS {STRU_TCP_REGISTER_RS() :type(_DEF_TCP_REGISTER_RS), result(register_tel_repeat){}//协议头packType type;//注册结果int result;}STRU_TCP_REGISTER_RS;// 登录请求
typedef struct STRU_TCP_LOGIN_RQ {STRU_TCP_LOGIN_RQ() :type(_DEF_TCP_LOGIN_RQ){memset(tel, 0, _DEF_NAME_MAX);memset(password, 0, _DEF_NAME_MAX);}//协议头packType type;//手机号char tel[_DEF_NAME_MAX];//密码char password[_DEF_NAME_MAX];}STRU_TCP_LOGIN_RQ;// 登录回复
typedef struct STRU_TCP_LOGIN_RS {STRU_TCP_LOGIN_RS() :type(_DEF_TCP_LOGIN_RS), id(0), result(login_password_error){}//协议头packType type;//自己的idint id;//登录结果int result;}STRU_TCP_LOGIN_RS;// 聊天请求
typedef struct STRU_TCP_CHAT_RQ {STRU_TCP_CHAT_RQ() :type(_DEF_TCP_CHAT_RQ), userId(0), friendId(0){memset(content, 0, _DEF_CONTENT_LENGHT);}//协议头packType type;//聊天内容char content[_DEF_CONTENT_LENGHT];//自己是谁int userId;//和谁聊天int friendId;}STRU_TCP_CHAT_RQ;// 聊天回复
typedef struct STRU_TCP_CHAT_RS {STRU_TCP_CHAT_RS() :type(_DEF_TCP_CHAT_RS), result(send_fail), friendId(0){}//协议头packType type;//聊天结果int result;//好友的idint friendId;}STRU_TCP_CHAT_RS;// 添加好友请求
typedef struct STRU_TCP_ADD_FRIEND_RQ {STRU_TCP_ADD_FRIEND_RQ() :type(_DEF_TCP_ADD_FRIEND_RQ), userId(0){memset(friendName, 0, _DEF_NAME_MAX);memset(userName, 0, _DEF_NAME_MAX);}//协议头packType type;//自己是谁int userId;//对方昵称char friendName[_DEF_NAME_MAX];//我的昵称char userName[_DEF_NAME_MAX];}STRU_TCP_ADD_FRIEND_RQ;// 添加好友回复
typedef struct STRU_TCP_ADD_FRIEND_RS {STRU_TCP_ADD_FRIEND_RS() :type(_DEF_TCP_ADD_FRIEND_RS), result(add_friend_user_offline), userId(0), friendId(0){memset(friendName, 0, _DEF_NAME_MAX);}//协议头packType type;//添加好友结果int result;//好友昵称char friendName[_DEF_NAME_MAX];//自己的idint userId;//好友的idint friendId;}STRU_TCP_ADD_FRIEND_RS;// 下线请求
typedef struct STRU_TCP_OFFLINE_RQ {STRU_TCP_OFFLINE_RQ() :type(_DEF_TCP_OFFLINE_RQ), userId(0){}//协议头packType type;//自己是谁int userId;}STRU_TCP_OFFLINE_RQ;//好友信息:协议头、头像id、好友id、昵称、签名、状态
typedef struct STRU_TCP_FRIEND_INFO {STRU_TCP_FRIEND_INFO() :type(_DEF_TCP_FRIEND_INFO), iconId(0), friendId(0), status(_status_offline){memset(name, 0, _DEF_NAME_MAX);memset(feeling, 0, _DEF_NAME_MAX);}//协议头packType type;int iconId;int friendId;char name[_DEF_NAME_MAX];char feeling[_DEF_NAME_MAX];int status;}STRU_TCP_FRIEND_INFO;
TcpClient.cpp
#include"TcpClient.h"
#include"packDef.h"
#include<iostream>
#include<process.h>
#include"../inetmediator/TcpClientMediator.h"using namespace std;TcpClient::TcpClient(INetMediator* pMediator):m_sock(INVALID_SOCKET), m_handle(NULL),m_bStop(false){m_pMediator = pMediator;
}
TcpClient::~TcpClient() {unInitNet();
}//线程函数(调用recvData)
unsigned __stdcall TcpClient::recvThread(void* lpVoid)
{TcpClient* const pThis = (TcpClient* const)lpVoid;pThis->recvData();return false;
}//初始化网络
bool TcpClient::initNet() {//1.加载库WORD wVersion = MAKEWORD(2, 2);WSAData data;int err = WSAStartup(wVersion, &data);if (err != 0){cout << "WSAStartup error" << err << endl;return false;}if (2 != LOBYTE(data.wVersion) || 2 != HIBYTE(data.wVersion)) {cout << "WSAStartup version error" << endl;return false;}else {cout << "WSAStartup success" << endl;}//2.创建套接字m_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (INVALID_SOCKET == m_sock) {cout << "socket error" << WSAGetLastError() << endl;return false;}else {cout << "socket success" << endl;}//3.连接服务端struct sockaddr_in addrServer;addrServer.sin_family = AF_INET;addrServer.sin_port = htons(_DEF_TCP_PORT);  //htons 转换成网络字节序addrServer.sin_addr.S_un.S_addr = inet_addr(_DEF_SERVER_IP);err = connect(m_sock, (sockaddr*)&addrServer, sizeof(addrServer));if (err == SOCKET_ERROR) {cout << "connect error" << WSAGetLastError() << endl;return false;}else {cout << "connect success" << endl;}// 4.创建接收数据的线程// CreateThread和ExitThread是一对,如果在线程里面使用C++运行时库(例如strcpy,在函数中申请空间不释放)// ExitThread也不会释放这个空间,就会造成内存泄漏。// _beginthreadex和_endthreadex是一对,_endthreadex会先回收空间,再调用ExitThread。// 创建线程的时候,操作系统分配: 句柄、线程id、内核对象// 回收线程:1、结束线程工作; 2、关闭句柄m_handle =  (HANDLE)_beginthreadex(0, 0, &recvThread, (void*)this, 0, NULL);return true;
}//接收数据
void TcpClient::recvData() {int packSize = 0;int nRecvNum = 0;//偏移量int nOffset = 0;while (!m_bStop) {nOffset = 0;//先接收包大小nRecvNum = recv(m_sock, (char*)&packSize, sizeof(int), 0);if (nRecvNum > 0) {//new空间char* packBuf = new char[packSize];//再接收包内容while (packSize > 0) {nRecvNum = recv(m_sock, packBuf + nOffset, packSize, 0);if (nRecvNum > 0) {nOffset += nRecvNum;packSize -= nRecvNum;}else {cout << "recv error:" << WSAGetLastError() << endl;break;}}//TODO:接收成功,把数据传给中介者,在处理函数里面回收packBufm_pMediator->dealData(packBuf, nOffset, m_sock);}else {cout << "recv error:" << WSAGetLastError() << endl;break;}}
}//发送数据(TCP由socket决定发送给谁,UDP由IP决定发送给谁)
//SOCKET是UINT类型,IP是ULONG类型,long类型两个都可以包括
bool TcpClient::sendData(char* data, int len, long to) {//1.判断参数是否有效if (data == NULL || len <= 0) {cout << "paramater error" << endl;return false;}//2.先发包大小if (send(m_sock, (char*)&len, sizeof(int), 0) <= 0) {cout << "send error:" << WSAGetLastError() << endl;return false;}//3.再发包内容if (send(m_sock, data, len, 0) <= 0) {cout << "send error" << WSAGetLastError() << endl;return false;}return true;
}//关闭网络:回收线程,关闭套接字,卸载库
void TcpClient::unInitNet() {// 回收线程:1、结束线程工作; 2、关闭句柄m_bStop = true;if (m_handle) {if (WAIT_TIMEOUT == WaitForSingleObject(m_handle, 500)) {//如果等待超时,就强制杀死线程TerminateThread(m_handle, -1);}CloseHandle(m_handle);m_handle = NULL;}//关闭套接字、卸载库if (m_sock && INVALID_SOCKET != m_sock) {closesocket(m_sock);}WSACleanup();
}
TcpClient.h
#pragma once
#include"inet.h"class TcpClient :public INet {
public:TcpClient(INetMediator* pMediator);~TcpClient();//初始化网络bool initNet();//接收数据void recvData();//发送数据(TCP由socket决定发送给谁,UDP由IP决定发送给谁)//SOCKET是UINT类型,IP是ULONG类型,long类型两个都可以包括bool sendData(char* data, int len, long to);//关闭网络void unInitNet();private:/*需要声明成静态,否则_beginthreadex(0, 0, &recvData, (void*)this, 0, NULL) 中 &recvData会报错,原因是类的成员函数通过对象调用,编译时对象不存在,运行时对象才存在,要想让编译的时候就存在,需要变成静态*/static unsigned __stdcall recvThread(void* lpVoid);private://SOCKETSOCKET m_sock;HANDLE m_handle;bool m_bStop;
};
TcpServer.cpp
#include"TcpServer.h"
#include"packDef.h"
#include<process.h>
#include<iostream>
#include"../inetmediator/TcpServerMediator.h"using namespace std;TcpServer::TcpServer(INetMediator* pMediator):m_sock(INVALID_SOCKET),m_bStop(false) {m_pMediator = pMediator;
}
TcpServer::~TcpServer() {unInitNet();
}//初始化网络:加载库、创建套接字、绑定、监听、创建接受连接的线程
bool TcpServer::initNet() {//1.加载库WORD wVersion = MAKEWORD(2, 2);WSAData data;int err = WSAStartup(wVersion, &data);if (err != 0){cout << "WSAStartup error" << err << endl;return false;}if (2 != LOBYTE(data.wVersion) || 2 != HIBYTE(data.wVersion)) {cout << "WSAStartup version error" << endl;return false;}else {cout << "WSAStartup success" << endl;}//2.创建套接字m_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (INVALID_SOCKET == m_sock) {cout << "socket error" << WSAGetLastError() << endl;return false;}else {cout << "socket success" << endl;}//3.绑定struct sockaddr_in addrServer;addrServer.sin_family = AF_INET;addrServer.sin_port = htons(_DEF_TCP_PORT);  //htons 转换成网络字节序addrServer.sin_addr.S_un.S_addr = INADDR_ANY;  //绑定任意网卡err = bind(m_sock, (sockaddr*)&addrServer, sizeof(addrServer));if (err == SOCKET_ERROR) {cout << "bind error" << WSAGetLastError() << endl;return false;}else {cout << "bind success" << endl;}//4.监听err = listen(m_sock, 10);if (SOCKET_ERROR == err) {cout << "listen error" << WSAGetLastError() << endl;return false;}else {cout << "listen success" << endl;}//5.创建接受连接的线程HANDLE handle = (HANDLE)_beginthreadex(0, 0, &acceptThread, (void*)this, 0, NULL);if (handle){m_listHandle.push_back(handle);}return true;
}//接受连接的线程函数
unsigned __stdcall TcpServer::acceptThread(void* lpVoid)
{TcpServer* pThis = (TcpServer*)lpVoid;SOCKET sock = INVALID_SOCKET;struct sockaddr_in addrClient;int addrClientSize = sizeof(addrClient);unsigned int threadId = 0;while (!pThis->m_bStop){//接受连接sock = accept(pThis->m_sock, (sockaddr*)&addrClient, &addrClientSize);//判断是否连接成功if (SOCKET_ERROR != sock){//成功,先打印客户端的IP地址cout << "client ip:" << inet_ntoa(addrClient.sin_addr) << endl;//同时给客户端创建一个接收数据的线程HANDLE handle = (HANDLE)_beginthreadex(0, 0, &recvThread, (void*)pThis, 0, &threadId);//保存线程句柄if (handle){pThis->m_listHandle.push_back(handle);}//保存线程ID和对应的socketpThis->m_mapThreadIdToSocket[threadId] = sock;}else{cout << "accept error:" << WSAGetLastError() << endl;}}return false;
}unsigned __stdcall TcpServer::recvThread(void* lpVoid)
{TcpServer* pThis = (TcpServer*)lpVoid;pThis->recvData();return false;
}//接收数据
void TcpServer::recvData() {Sleep(1);//从map中取出当前线程的socketSOCKET sock = m_mapThreadIdToSocket[GetCurrentThreadId()];if (!sock || INVALID_SOCKET == sock){cout << "sock error" << endl;return;}int packSize = 0;int nRecvNum = 0;//偏移量int nOffset = 0;while (!m_bStop) {nOffset = 0;//先接收包大小nRecvNum = recv(sock, (char*)&packSize, sizeof(int), 0);//!!!这里的socket要用从map中取出来的sock!!!if (nRecvNum > 0) {//new空间char* packBuf = new char[packSize];//再接收包内容while (packSize > 0) {nRecvNum = recv(sock, packBuf + nOffset, packSize, 0);if (nRecvNum > 0) {nOffset += nRecvNum;packSize -= nRecvNum;}else {cout << "recv error:" << WSAGetLastError() << endl;break;}}//TODO:接收成功,把数据传给中介者,在处理函数里面回收packBuf//-----------------------测试代码------------------------m_pMediator->dealData(packBuf, nOffset, sock);}else {cout << "recv error:" << WSAGetLastError() << endl;break;}}
}//发送数据(TCP由socket决定发送给谁,UDP由IP决定发送给谁)
//SOCKET是UINT类型,IP是ULONG类型,long类型两个都可以包括
bool TcpServer::sendData(char* data, int len, long to) {//1.判断参数是否有效if (data == NULL || len <= 0) {cout << "paramater error" << endl;return false;}//2.先发包大小if (send(to, (char*)&len, sizeof(int), 0) <= 0) {cout << "send error:" << WSAGetLastError() << endl;return false;}//3.再发包内容if (send(to, data, len, 0) <= 0) {cout << "send error" << WSAGetLastError() << endl;return false;}return true;
}//关闭网络
void TcpServer::unInitNet() {// 回收线程:1、结束线程工作; 2、关闭句柄m_bStop = true;for (auto ite = m_listHandle.begin(); ite != m_listHandle.end();){HANDLE handle = *ite;if (handle) {if (WAIT_TIMEOUT == WaitForSingleObject(handle, 500)) {//如果等待超时,就强制杀死线程TerminateThread(handle, -1);}CloseHandle(handle);handle = NULL;}//移除无效节点ite = m_listHandle.erase(ite);}//关闭套接字、卸载库 !!!这里注意一个是关闭监听用的套接字(m_sock),一个是关闭map中存的sock!!!if (m_sock && INVALID_SOCKET != m_sock) {closesocket(m_sock);}for (auto ite = m_mapThreadIdToSocket.begin(); ite != m_mapThreadIdToSocket.end();){SOCKET sock = ite->second;if (sock && INVALID_SOCKET != sock){closesocket(sock);}//移除无效节点ite = m_mapThreadIdToSocket.erase(ite);}WSACleanup();
}
TcpServer.h
#pragma once
#include"inet.h"
#include<map>
#include<list>
using namespace std;class TcpServer :public INet {
public:TcpServer(INetMediator* pMediator);~TcpServer();//初始化网络bool initNet();//接收数据void recvData();//发送数据(TCP由socket决定发送给谁,UDP由IP决定发送给谁)//SOCKET是UINT类型,IP是ULONG类型,long类型两个都可以包括bool sendData(char* data, int len, long to);//关闭网络void unInitNet();
private://接受连接的线程函数static unsigned __stdcall acceptThread(void* lpVoid);//接收数据的线程函数static unsigned __stdcall recvThread(void* lpVoid);private:SOCKET m_sock;//退出线程循环标志位bool m_bStop;//保存线程id和对应socketmap<unsigned int, SOCKET>m_mapThreadIdToSocket;//保存线程句柄list<HANDLE>m_listHandle;};
UdpNet.cpp
#include"UdpNet.h"UdpNet::UdpNet() {}
UdpNet::~UdpNet() {}//初始化网络
bool UdpNet::initNet() {return false;
}//接收数据
void UdpNet::recvData() {}//发送数据(TCP由socket决定发送给谁,UDP由IP决定发送给谁)
//SOCKET是UINT类型,IP是ULONG类型,long类型两个都可以包括
bool UdpNet::sendData(char* data, int len, long to) {return false;
}//关闭网络
void UdpNet::unInitNet() {}
UdpNet.h
#pragma once
#include"inet.h"class UdpNet :public INet {
public:UdpNet();~UdpNet();//初始化网络bool initNet();//接收数据void recvData();//发送数据(TCP由socket决定发送给谁,UDP由IP决定发送给谁)//SOCKET是UINT类型,IP是ULONG类型,long类型两个都可以包括bool sendData(char* data, int len, long to);//关闭网络void unInitNet();
};

inetMediator

inetMediator.h
#pragma once class INet;
class INetMediator {
public:INetMediator() {}virtual ~INetMediator() {}//打开网络virtual bool openNet() = 0;//发送数据(TCP由socket决定发送给谁,UDP由IP决定发送给谁)//SOCKET是UINT类型,IP是ULONG类型,long类型两个都可以包括virtual bool sendData(char* data, int len, long to) = 0;//转发数据virtual void dealData(char* data, int len, long from) = 0;//关闭网络virtual void closeNet() = 0;
protected:INet* m_pNet;
};
TcpClientMediator.cpp
#include"TcpClientMediator.h"
#include"../inet/TcpClient.h"TcpClientMediator::TcpClientMediator()
{m_pNet = new TcpClient(this);
}
TcpClientMediator::~TcpClientMediator()
{if (m_pNet){m_pNet->unInitNet();delete m_pNet;m_pNet = NULL;}
}//打开网络
bool TcpClientMediator::openNet()
{return m_pNet->initNet();
}//发送数据(TCP由socket决定发送给谁,UDP由IP决定发送给谁)
//SOCKET是UINT类型,IP是ULONG类型,long类型两个都可以包括
bool TcpClientMediator::sendData(char* data, int len, long to)
{return m_pNet->sendData(data, len, to);
}//转发数据
void TcpClientMediator::dealData(char* data, int len, long from)
{}//关闭网络
void TcpClientMediator::closeNet()
{m_pNet->unInitNet();
}
TcpClientMediator.h
#pragma once 
#include"inetMediator.h"class TcpClientMediator:public INetMediator {
public:TcpClientMediator();~TcpClientMediator();//打开网络bool openNet();//发送数据(TCP由socket决定发送给谁,UDP由IP决定发送给谁)//SOCKET是UINT类型,IP是ULONG类型,long类型两个都可以包括bool sendData(char* data, int len, long to);//转发数据void dealData(char* data, int len, long from);//关闭网络void closeNet();};
TcpServerMediator.cpp
#include"TcpServerMediator.h"
#include"../inet/TcpServer.h"
#include"../CKernel.h"TcpServerMediator::TcpServerMediator()
{m_pNet = new TcpServer(this);
}
TcpServerMediator::~TcpServerMediator()
{if (m_pNet){m_pNet->unInitNet();delete m_pNet;m_pNet = NULL;}
}//打开网络
bool TcpServerMediator::openNet()
{return m_pNet->initNet();
}//发送数据(TCP由socket决定发送给谁,UDP由IP决定发送给谁)
//SOCKET是UINT类型,IP是ULONG类型,long类型两个都可以包括
bool TcpServerMediator::sendData(char* data, int len, long to)
{return m_pNet->sendData(data,len,to);
}//转发数据
void TcpServerMediator::dealData(char* data, int len, long from)
{//把数据发给KernelCKernel::pKernel->dealData(data, len, from);
}//关闭网络
void TcpServerMediator::closeNet()
{m_pNet->unInitNet();
}
TcpServerMediator.h
#pragma once 
#include"inetMediator.h"class TcpServerMediator :public INetMediator {
public:TcpServerMediator();~TcpServerMediator();//打开网络bool openNet();//发送数据(TCP由socket决定发送给谁,UDP由IP决定发送给谁)//SOCKET是UINT类型,IP是ULONG类型,long类型两个都可以包括bool sendData(char* data, int len, long to);//转发数据void dealData(char* data, int len, long from);//关闭网络void closeNet();};
UdpNetMediator.cpp
UdpNetMediator.h

mysql

CMySql.cpp
//#include "stdafx.h"
#include "CMySql.h"CMySql::CMySql(void)
{/*这个函数用来分配或者初始化一个MYSQL对象,用于连接mysql服务端。如果你传入的参数是NULL指针,它将自动为你分配一个MYSQL对象,如果这个MYSQL对象是它自动分配的,那么在调用mysql_close的时候,会释放这个对象*/m_sock = new MYSQL;mysql_init(m_sock);  mysql_set_character_set(m_sock, "gb2312"); //gb2312 中华人民共和国简体字标准
}CMySql::~CMySql(void)
{if(m_sock) {delete m_sock;m_sock = NULL;}
}void CMySql::DisConnect()
{mysql_close(m_sock);
}bool CMySql::ConnectMySql(char *host, char *user, char *pass, char *db, short nport)
{if (!mysql_real_connect(m_sock, host, user, pass, db, nport, NULL, CLIENT_MULTI_STATEMENTS)) {cout << "连接数据库失败,失败错原因:" << mysql_error(m_sock);//连接错误return false;}return true;
}bool  CMySql::GetTables(char* szSql, list<string>& lstStr){if(mysql_query(m_sock, szSql)) {return false;}m_results = mysql_store_result(m_sock);if(NULL == m_results) {return false;}while (m_record = mysql_fetch_row(m_results)) {lstStr.push_back(m_record[0]);}return true;}
bool CMySql::SelectMySql(char* szSql, int nColumn, list<string>& lstStr)
{//mysql_query() 函数用于向 MySQL 发送并执行 SQL 语句if(mysql_query(m_sock, szSql)) {return false;}/*·mysql_store_result 对于成功检索了数据的每个查询(SELECT、SHOW、DESCRIBE、EXPLAIN、CHECK TABLE等)返回值:. CR_COMMANDS_OUT_OF_SYNC   以不恰当的顺序执行了命令。· CR_OUT_OF_MEMORY   内存溢出。· CR_SERVER_GONE_ERROR   MySQL服务器不可用。· CR_SERVER_LOST   在查询过程中,与服务器的连接丢失。· CR_UNKNOWN_ERROR   出现未知错误。*/m_results = mysql_store_result(m_sock);if(NULL == m_results)return false;//遍历表中的下一行,取出内容放入m_record 结果集while (m_record = mysql_fetch_row(m_results)) {for(int i = 0; i < nColumn; i++) {if(!m_record[i]) {lstStr.push_back("");} else {lstStr.push_back(m_record[i]);}}}return true;
}bool  CMySql::UpdateMySql(char* szSql){if(!szSql) {return false;}if(mysql_query(m_sock, szSql)) {return false;}return true;}
CMySql.h
#pragma once#include <mysql.h>
#include <string>
#include <iostream>#pragma comment(lib,"libmysql.lib")
//
#include <list>
using namespace std;class CMySql
{
public:CMySql(void);~CMySql(void);
public:                    //ip,用户名,密码,数据库,端口号bool  ConnectMySql(char *host,char *user,char *pass,char *db,short nport = 3306);void  DisConnect();bool  SelectMySql(char* szSql,int nColumn,list<string>& lstStr);//获得数据库中的表bool GetTables(char* szSql,list<string>& lstStr);//更新:删除、插入、修改bool  UpdateMySql(char* szSql);private:MYSQL *m_sock;   MYSQL_RES *m_results;   MYSQL_ROW m_record; };

头文件

CKernel.h
#pragma once
#include"inetmediator/inetMediator.h"
#include<iostream>
#include"MySQL/CMySql.h"
#include"inet/packDef.h"
#include<map>using namespace std;//定义函数指针函数组
class CKernel;
typedef void(CKernel::* PFun)(char* data, int len, long from);class CKernel
{
public:CKernel();~CKernel();//打开服务器bool startServer();//关闭服务器void closeServer();//初始化协议头数组void setProtocolArr();//处理所有接收到的数据void dealData(char* data, int len, long from);//处理注册请求void dealRegisterRq(char* data, int len, long from);//处理登录请求void dealLoginRq(char* data, int len, long from);//获取登录用户的好友信息void getFriendInfo(int userId);//根据用户id查询用户信息void getUserInfo(int userId, STRU_TCP_FRIEND_INFO* info);//处理聊天请求void dealChatRq(char* data, int len, long from);//处理下线请求void dealOfflineRq(char* data, int len, long from);//处理添加好友请求void dealAddFriendRq(char* data, int len, long from);//处理添加好友回复void dealAddFriendRs(char* data, int len, long from);public:static CKernel* pKernel;
private:INetMediator* m_pMediator;CMySql m_sql;//协议头数组PFun m_pFun[_DEF_PROTOCOL_COUNT];//保存登录成功客户端的socketmap<int, SOCKET>m_mapIdToSocket;
};

源文件

CKernel.cpp
#include "CKernel.h"
#include"inetmediator/TcpServerMediator.h"CKernel* CKernel::pKernel = nullptr;CKernel::CKernel()
{setProtocolArr();pKernel = this;m_pMediator = new TcpServerMediator;
}
CKernel::~CKernel()
{closeServer();
}//打开服务器
bool CKernel::startServer()
{//连接数据库if (!m_sql.ConnectMySql("127.0.0.1", "root", "123456", "20230320im")){cout << "connect mysql fail" << endl;return false;}//初始化网络if (!m_pMediator->openNet()){return false;}return true;
}
//初始化协议头数组
void CKernel::setProtocolArr()
{//给数组初始化memset(m_pFun, 0, sizeof(m_pFun));//绑定协议头数组m_pFun[_DEF_TCP_REGISTER_RQ   - _DFE_PROTOCOL_BASE - 1] = &CKernel::dealRegisterRq;m_pFun[_DEF_TCP_LOGIN_RQ      - _DFE_PROTOCOL_BASE - 1] = &CKernel::dealLoginRq;m_pFun[_DEF_TCP_CHAT_RQ       - _DFE_PROTOCOL_BASE - 1] = &CKernel::dealChatRq;m_pFun[_DEF_TCP_OFFLINE_RQ    - _DFE_PROTOCOL_BASE - 1] = &CKernel::dealOfflineRq;m_pFun[_DEF_TCP_ADD_FRIEND_RQ - _DFE_PROTOCOL_BASE - 1] = &CKernel::dealAddFriendRq;m_pFun[_DEF_TCP_ADD_FRIEND_RS - _DFE_PROTOCOL_BASE - 1] = &CKernel::dealAddFriendRs;}//处理所有接收到的数据
void CKernel::dealData(char* data, int len, long from)
{cout << "CKernel::dealData" << endl;//1.取出协议头packType type = *(packType*)data;//2.计算数组下标int index = type - _DFE_PROTOCOL_BASE - 1;//3.判断协议头是否在有效范围内if (index >= 0 && index < _DEF_PROTOCOL_COUNT) {PFun pf = m_pFun[index];if (pf) {(this->*pf)(data, len, from);}else {//如果type错了,两个原因:1.发送的时候type不对(结构体初始化赋值的不对)2.绑定协议头数组没绑定这个协议cout << "type2 error:" << type << endl;}}else {//如果type错了,两个原因:1.发送的时候type不对(结构体初始化赋值的不对)2.接收函数里面nOffset没清零cout << "type1 error:" << type << endl;}//4.回收空间delete[] data;
}//处理注册请求
void CKernel::dealRegisterRq(char* data, int len, long from)
{cout << "CKernel::dealRegisterRq" << endl;//1.拆包STRU_TCP_REGISTER_RQ* rq = (STRU_TCP_REGISTER_RQ*)data;//2.校验合法性//3.根据昵称查询list<string>lstRes;char sqlBuf[1024] = "";sprintf_s(sqlBuf,"select name from t_user where name = '%s';",rq->name);if (!m_sql.SelectMySql(sqlBuf, 1, lstRes)){cout << "查询数据库失败" << sqlBuf << endl;return;}//4.判断查询结果STRU_TCP_REGISTER_RS rs;if (lstRes.size() > 0){//5.如果查询结果不为空,说明昵称重复,注册失败rs.result = register_name_repeat;}else{//6.如果查询结果为空,根据电话号查询sprintf_s(sqlBuf, "select tel from t_user where tel = %s;", rq->tel);if (!m_sql.SelectMySql(sqlBuf, 1, lstRes)){cout << "查询数据库失败" << sqlBuf << endl;return;}if (lstRes.size() > 0){//7.如果查询结果不为空,说明电话号码重复,注册失败rs.result = register_tel_repeat;}else{//8.如果查询结果为空,把注册信息写入数据库sprintf_s(sqlBuf, "insert into t_user(name,tel,password,icon,feeling)values('%s','%s','%s',1,'这个人很懒,什么也没留下');",rq->name,rq->tel,rq->password);if (!m_sql.UpdateMySql(sqlBuf)){cout << "插入数据库失败" << sqlBuf << endl;return;}//9.注册成功rs.result = register_success;}}//10.不管成功还是失败,都要给客户端回复注册结果m_pMediator->sendData((char*)&rs,sizeof(rs),from);
}//处理登录请求
void CKernel::dealLoginRq(char* data, int len, long from)
{cout << "CKernel::dealLoginRq" << endl;//1.拆包STRU_TCP_LOGIN_RQ* rq = (STRU_TCP_LOGIN_RQ*)data;//2. 校验合法性//3.根据电话号码查密码list<string>lstRes;char sqlBuf[1024] = "";sprintf_s(sqlBuf, "select password,id from t_user where tel = %s;", rq->tel);if (!m_sql.SelectMySql(sqlBuf, 2, lstRes)){cout << "查询数据库失败" << sqlBuf << endl;return;}//4.判断查询结果STRU_TCP_LOGIN_RS rs;if (0 == lstRes.size()){//5.如果结果为空,登录失败,用户不存在rs.result = login_tel_not_exist;}else{//6.如果结果不为空,取出密码string pass = lstRes.front();lstRes.pop_front();//取出用户idint userId = stoi(lstRes.front());lstRes.pop_front();//7.比较用户输入的密码和查询到的密码if (0 == strcmp(rq->password, pass.c_str())){//8.如果相等,登录成功rs.id = userId;//!!!登录成功保存id 客户端也一样 后续登录成功回发送自己信息和好友信息 到时候我们根据id判断!!!rs.result = login_success;//把登录成功的客户端的socket保存起来m_mapIdToSocket[userId] = from;m_pMediator->sendData((char*)&rs, sizeof(rs), from);//查询登录用户的好友信息getFriendInfo(userId);return;}else{//9.不相等,登录失败,密码错误rs.result = login_password_error;}}//10.不管成功还是失败,都要给客户端回复登录结果m_pMediator->sendData((char*)&rs, sizeof(rs), from);
}//获取登录用户的好友信息
void CKernel::getFriendInfo(int userId)
{cout << "CKernel::getFriendInfo" << endl;//1.查询自己的用户信息STRU_TCP_FRIEND_INFO userInfo;getUserInfo(userId,&userInfo);//2.把自己的信息发给客户端if (m_mapIdToSocket.count(userId) > 0){m_pMediator->sendData((char*)&userInfo, sizeof(userInfo), m_mapIdToSocket[userId]);}//3.查询好友id列表list<string>lstRes;char sqlBuf[1024] = "";sprintf_s(sqlBuf, "select idB from t_friend where idA = '%d';", userId);if (!m_sql.SelectMySql(sqlBuf, 1, lstRes)){cout << "查询数据库失败" << sqlBuf << endl;return;}//遍历好友id列表int friendId = 0;STRU_TCP_FRIEND_INFO friendInfo;while (lstRes.size() > 0){//取出每个好友的idfriendId = stoi(lstRes.front());lstRes.pop_front();//根据好友id查询好友信息getUserInfo(friendId,&friendInfo);//把好友信息发送给登录用户客户端if (m_mapIdToSocket.count(userId) > 0){m_pMediator->sendData((char*)&friendInfo, sizeof(friendInfo), m_mapIdToSocket[userId]);}//通知所有在线好友,自己上线了if (m_mapIdToSocket.count(friendId) > 0){m_pMediator->sendData((char*)&userInfo, sizeof(userInfo), m_mapIdToSocket[friendId]);}}
}//根据用户id查询用户信息
void CKernel::getUserInfo(int userId, STRU_TCP_FRIEND_INFO* info)
{cout << "CKernel::getUserInfo" << endl;info->friendId = userId;if (m_mapIdToSocket.count(userId) > 0){//在线info->status = _status_online;}else{//不在线info->status = _status_offline;}//从数据库中查询用户的昵称、iconId、签名list<string>lstRes;char sqlBuf[1024] = "";sprintf_s(sqlBuf, "select name,icon,feeling from t_user where id = '%d';", userId);if (!m_sql.SelectMySql(sqlBuf, 3, lstRes)){cout << "查询数据库失败" << sqlBuf << endl;return;}if (3 == lstRes.size()){strcpy(info->name,lstRes.front().c_str());lstRes.pop_front();info->iconId = stoi(lstRes.front());lstRes.pop_front();strcpy(info->feeling, lstRes.front().c_str());lstRes.pop_front();}
}//处理聊天请求
void CKernel::dealChatRq(char* data,int len,long from)
{cout << "CKernel::dealChatRq" << endl;//1.拆包STRU_TCP_CHAT_RQ* rq = (STRU_TCP_CHAT_RQ*)data;//2.判断好友是否在线if (m_mapIdToSocket.count(rq->friendId) > 0){//如果好友在线,转发聊天请求m_pMediator->sendData(data, len, m_mapIdToSocket[rq->friendId]);}else{//如果好友不在线,直接回复客户端发送失败STRU_TCP_CHAT_RS rs;rs.result = send_fail;rs.friendId = rq->friendId;m_pMediator->sendData((char*)&rs, sizeof(rs), from);}
}//处理下线请求
void CKernel::dealOfflineRq(char* data, int len, long from)
{cout << "CKernel::dealOfflineRq" << endl;//1.拆包STRU_TCP_OFFLINE_RQ* rq = (STRU_TCP_OFFLINE_RQ*)data;//2.获取好友id列表list<string>lstRes;char sqlBuf[1024] = "";sprintf_s(sqlBuf, "select idB from t_friend where idA = '%d';", rq->userId);if (!m_sql.SelectMySql(sqlBuf, 1, lstRes)){cout << "查询数据库失败" << sqlBuf << endl;return;}//3.遍历好友列表int friendId = 0;while (lstRes.size() > 0){//4.取出好友idfriendId = stoi(lstRes.front());lstRes.pop_front();//5.判断好友是否在线,在线就转发下线请求给好友if (m_mapIdToSocket.find(friendId) != m_mapIdToSocket.end()){m_pMediator->sendData(data, len, m_mapIdToSocket[friendId]);}}//6.处理自己的信息:关闭socket,把节点从map中移除auto ite = m_mapIdToSocket.find(rq->userId);if (ite != m_mapIdToSocket.end()){closesocket(from);m_mapIdToSocket.erase(ite);}
}//处理添加好友请求
void CKernel::dealAddFriendRq(char* data, int len, long from)
{cout << "CKernel::dealAddFriendRq" << endl;//1.拆包STRU_TCP_ADD_FRIEND_RQ* rq = (STRU_TCP_ADD_FRIEND_RQ*)data;//2.判断是否有好友这个人list<string>lstRes;char sqlBuf[1024] = "";sprintf_s(sqlBuf, "select id from t_user where name = '%s';", rq->friendName);if (!m_sql.SelectMySql(sqlBuf, 1, lstRes)){cout << "查询数据库失败" << sqlBuf << endl;return;}//3.判断查询结果STRU_TCP_ADD_FRIEND_RS rs;if (0 == lstRes.size()){//4.好友不存在,添加好友失败rs.result = add_friend_no_this_user;rs.userId = rq->userId;//---------------------------------------------------------------------------------strcpy_s(rs.friendName, rq->friendName);//5.发回给客户端m_pMediator->sendData((char*)&rs, sizeof(rs), from);}else{//好友存在,取出好友idint friendId = stoi(lstRes.front());lstRes.pop_front();//7.判断好友在不在线if (m_mapIdToSocket.count(friendId) > 0){//8.好友在线,转发添加好友请求m_pMediator->sendData(data, len, m_mapIdToSocket[friendId]);}else{//9.好友不在线,添加好友失败rs.result = add_friend_user_offline;rs.userId = rq->userId;//----------------------------------------------------------------------strcpy_s(rs.friendName, rq->friendName);//10.发回给客户端m_pMediator->sendData((char*)&rs, sizeof(rs), from);}}
}//处理添加好友回复
void CKernel::dealAddFriendRs(char* data, int len, long from)
{cout << "CKernel::dealAddFriendRs" << endl;//1.拆包STRU_TCP_ADD_FRIEND_RS* rs = (STRU_TCP_ADD_FRIEND_RS*)data;//2.判断添加结果是否成功if (add_friend_success == rs->result){//3.把好友关系写入数据库(好友关系双向存储,需要写入两次,两次必须成功使用事务保证)char sqlBuf[1024] = "";sprintf_s(sqlBuf, "insert into t_friend values (%d,%d);", rs->friendId, rs->userId);if (!m_sql.UpdateMySql(sqlBuf)){cout << "插入数据库失败" << sqlBuf << endl;return;}sprintf_s(sqlBuf, "insert into t_friend values (%d,%d);", rs->userId, rs->friendId);if (!m_sql.UpdateMySql(sqlBuf)){cout << "插入数据库失败" << sqlBuf << endl;return;}//4.更新好友列表getFriendInfo(rs->userId);}//5.把结果发给A客户端m_pMediator->sendData(data, len, m_mapIdToSocket[rs->userId]);
}//关闭服务器
void CKernel::closeServer()
{//回收资源if (m_pMediator){m_pMediator->closeNet();delete m_pMediator;m_pMediator = nullptr;}
}
network_5_01_IMServer.cpp
#include<iostream>
#include<Windows.h>
#include"CKernel.h"using namespace std;int main() {//new一个kernel的对象CKernel kernel;//打开服务器if (!kernel.startServer()){cout << "start server error" << endl;return 1;}while (true){cout << "server is running" << endl;Sleep(5000);}测试连接数据库//CMySql sql;//if (!sql.ConnectMySql("127.0.0.1", "root", "123456", "20230320im"))//{//	cout << "connect mysql error" << endl;//	return 1;//}查询数据库//list<string>lstStr;//char sqlBuf[1024] = "";//sprintf(sqlBuf,"select number,name,age,sex from studentinfo;");//if (!sql.SelectMySql(sqlBuf, 4, lstStr))//{//	cout << "select fail" << endl;//}//else//{//	//打印查询结果//	while (lstStr.size() > 0)//	{//		//取出number//		int num = stoi(lstStr.front());//		lstStr.pop_front();//		//取出name//		string name = lstStr.front();//		lstStr.pop_front();//		//取出age//		int age = stoi(lstStr.front());//		lstStr.pop_front();//		//取出sex//		string sex = lstStr.front();//		lstStr.pop_front();//		cout << "num=" << num << ",name=" << name << ",age=" << age << ",sex=" << sex << endl;//	}//}//sql.DisConnect();return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/50085.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

每日一知识点 - Java Lambda 表达式

目录 &#x1f4dd; 每日一知识点Lambda 表达式1、基本概念2、使用示例 &#x1f4ce; 参考文章 &#x1f600; 准备好了吗&#xff1f;让我们一起步入这座Java神奇的城堡&#xff0c;揭开Java Lambda 表达式的神秘面纱&#xff0c;探索其中的奥秘。 &#x1f4dd; 每日一知识点…

PlatformIO+ESP32S3学习:驱动WS2812矩阵彩灯显示FFT音律拾音灯

本文继承自之前的彩灯驱动文章&#xff1a;https://blog.csdn.net/qq_51930953/article/details/140736628 本文完成的效果&#xff1a; 1. 硬件准备 1.1. WS2812矩阵彩灯 购买地址&#xff1a;WS2812B全彩软像素屏8X8 8X32 16X16幻彩5V显示可编程像素软屏 1.2. 麦克风模块 购…

Ip2region - 基于xdb离线库的Java IP查询工具提供给脚本调用

文章目录 Pre效果实现git clone编译测试程序将ip2region.xdb放到指定目录使用改进最终效果 Pre OpenSource - Ip2region 离线IP地址定位库和IP定位数据管理框架 Ip2region - xdb java 查询客户端实现 效果 最终效果 实现 git clone git clone https://github.com/lionsou…

YOLOV8源码解读-C2f模块-以及总结c2模块、Bottleneck

c2f模块是对c2模块的改进 c2模块图解解读 先给出YOLOV8中卷积的定义模块一键三连-卷积-BN-激活函数 def autopad(k, pNone, d1): # kernel, padding, dilation"""Pad to same shape outputs."""if d > 1:k d * (k - 1) 1 if isinstance…

洛谷 P7771:【模板】欧拉路径

【题目来源】https://www.luogu.com.cn/problem/P7771【题目描述】 求有向图字典序最小的欧拉路径。【输入格式】 第一行两个整数 n,m 表示有向图的点数和边数。 接下来 m 行每行两个整数 u,v 表示存在一条 u→v 的有向边。【输出格式】 如果不存在欧拉路径&#xff0c;输出一行…

在模型中bert和transform讲解

在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;BERT 和 Transformer 是两个非常重要的概念。下面是它们的简要解释&#xff1a; 一 、BERT BERT&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;是由Google提出的一种预训练语言…

Docker与Kubernetes在Java微服务中的应用

引言 随着微服务架构的普及,容器化技术成为部署和管理微服务的重要手段。Docker 提供了一种轻量级的容器解决方案,而 Kubernetes 则成为了容器编排和管理的事实标准。本文将深入探讨如何将 Java 微服务容器化,并在 Kubernetes 上部署和管理这些服务。 容器化概述 1. 容器…

Linux:进程信号(二.信号的保存与处理、递达、volatile关键字、SIGCHLD信号)

上次介绍了&#xff1a;(Linux&#xff1a;进程信号&#xff08;一.认识信号、信号的产生及深层理解、Term与Core&#xff09;)[https://blog.csdn.net/qq_74415153/article/details/140624810] 文章目录 1.信号保存1.1递达、未决、阻塞等概念1.2再次理解信号产生与保存1.3信号…

Pytorch深度学习实践(9)卷积神经网络

卷积神经网络 全连接神经网络 神经网络中全部是线性模型&#xff0c;是由线性模型串联起来的 全连接网络又叫全连接层 卷积神经网络 在全连接神经网络中&#xff0c;由于输入必须是一维向量&#xff0c;因此在处理图像时必须要对图像矩阵进行拉伸成一维的形式&#xff0c;…

构建npm组件包并打包上传到npm官网

vitejsv3简略搭建&#xff08;笔记用&#xff09; 原作者&#xff1a;https://juejin.cn/post/7119827361092108301?searchId20240724094258A72138D981DC0419C33E 1.npm create vitelatest # 使用npm安装vite脚手架2.先install项目&#xff0c;然后改造&#xff1a;修改&…

【算法】布隆过滤器

一、引言 在现实世界的计算机科学问题中&#xff0c;我们经常需要判断一个元素是否属于一个集合。传统的做法是使用哈希表或者直接遍历集合&#xff0c;但这些方法在数据量较大时效率低下。布隆过滤器&#xff08;Bloom Filter&#xff09;是一种空间效率极高的概率型数据结构&…

【NPU 系列专栏 2.8 -- 特斯拉 FDS NPU 详细介绍 】

请阅读【嵌入式及芯片开发学必备专栏】 文章目录 特斯拉 NPU 芯片介绍FSD(Full Self-Driving)芯片 简介FSD主要特点FSD 详细参数FSD 应用场景特斯拉 Hardware 3.0 芯片 简介Hardware 3.0主要特点Hardware 3.0 详细参数Hardware 3.0应用场景特斯拉自研 NPU 的优势优化设计高度…

【数学建模】——matplotlib简单应用

目录 1.绘制带有中文标签和图例的正弦和余弦曲线 2. 绘制散点图 1.修改散点符号与大小 2.修改颜色 3.绘制饼状图 4.在图例中显示公式 5.多个图形单独显示 6.绘制有描边和填充效果的柱状图 7.使用雷达图展示学生成绩 8.绘制三维曲面 9.绘制三维曲线 10.设置…

定制化即时通讯企业级移动门户解决方案,WorkPlus IM系统让工作事半功倍

随着移动设备的普及和移动办公的兴起&#xff0c;企业越来越需要一种定制化的即时通讯企业级移动门户解决方案来提高工作效率和团队协作效果。WorkPlus IM系统作为一种创新的解决方案&#xff0c;为企业提供了一个个性化定制、高度安全和高效便捷的移动门户平台。本文将对定制化…

BFF:优化前后端协作设计模式

BFF&#xff1a;优化前后端协作设计模式 BFF是什么 BFF即 Backends For Frontends (服务于前端的后端)。是一种介于前端和后端之间一种重要的通信设计模式。它旨在解决前端与后端协作中的复杂性问题。 背景 行业背景&#xff1a;传统前端应用&#xff08;如Web应用、移动应…

微服务-MybatisPlus下

微服务-MybatisPlus下 文章目录 微服务-MybatisPlus下1 MybatisPlus扩展功能1.1 代码生成1.2 静态工具1.3 逻辑删除1.4 枚举处理器1.5 JSON处理器**1.5.1.定义实体****1.5.2.使用类型处理器** **1.6 配置加密&#xff08;选学&#xff09;**1.6.1.生成秘钥**1.6.2.修改配置****…

网络安全防御【IPsec VPN搭建】

目录 一、实验拓扑图 二、实验要求 三、实验思路 四、实验步骤&#xff1a; 修改双机热备的为主备模式&#xff1a; 2、配置交换机LSW6新增的配置&#xff1a; 3、防火墙&#xff08;FW4&#xff09;做相关的基础配置&#xff1a; 4、搭建IPsec VPN通道 &#xff08;1…

GLSL教程 第10章:高级渲染技术

目录 10.1 后处理效果 10.1.1 色彩校正 示例代码&#xff1a;色彩校正 解释&#xff1a; 10.1.2 亮度对比度调整 示例代码&#xff1a;亮度对比度调整 解释&#xff1a; 10.1.3 模糊效果 示例代码&#xff1a;高斯模糊 解释&#xff1a; 10.1.4 边缘锐化 示例代码&a…

Java代码基础算法练习-求杨辉三角第n行的值-2024.07.27

任务描述&#xff1a; 给定一个非负整数n&#xff0c;生成「杨辉三角」的第n行。&#xff08;1<n<10&#xff09;在「杨辉三角」中&#xff0c;每 个数是它左上方和右上方的数的和。 &#xff08;提示&#xff0c;第一列数值为1&#xff0c;如数组下标用i,j表示&#xf…

VoIP所在的协议层次

VoIP&#xff08;Voice over Internet Protocol&#xff09;本身不是一种协议&#xff0c;而是一种技术或通信方式。虽然VoIP技术本身不是协议&#xff0c;但它依赖于多种协议来实现其功能。所以&#xff0c;其并不严格地工作在网络通信的某一层&#xff0c;而是跨越了多个层次…