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