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;
    //自己的id
    int 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;
    //好友的id
    int 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];
    //自己的id
    int userId;
    //好友的id
    int 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:
	//SOCKET
	SOCKET 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:
    //把聊天内容发给kernel
    void 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:
    //把注册信息发给kernel
    void SIG_registerMessage(QString name,QString tel,QString passW);
    //把登录信息发给kernel
    void SIG_loginMessage(QString tel,QString passW);
    //发送关闭程序的信号给kernel
    void 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使用GB2312
    void 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];
    //保存好友的useritem
    QMap<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:
    //发送下线信号给kernel
    void 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:接收成功,把数据传给中介者,在处理函数里面回收packBuf
			m_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) {

    //把接收到的数据传给kernel
    Q_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.发给kernel
    Q_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.通过信号把数据发给kernel
    Q_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.通过信号把数据发给kernel
    Q_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);
         //保存好友的useritem
         m_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;
    //自己的id
    int 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;
    //好友的id
    int 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];
    //自己的id
    int userId;
    //好友的id
    int 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:接收成功,把数据传给中介者,在处理函数里面回收packBuf
			m_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:
	//SOCKET
	SOCKET 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和对应的socket
			pThis->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中取出当前线程的socket
	SOCKET 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和对应socket
	map<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)
{
	//把数据发给Kernel
	CKernel::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];
	//保存登录成功客户端的socket
	map<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();

		//取出用户id
		int 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)
	{
		//取出每个好友的id
		friendId = 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.取出好友id
		friendId = 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
	{
		//好友存在,取出好友id
		int 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;
}
  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值