客户端
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;
}