C++ High Concurrency Network Architecture and Implementation-Part Six

table of Contents

One, change the client to multi-thread

Second, add multi-threaded processing messages to the server


Achieve the task today

  1. Change the client to multi-threaded
  2. Add multi-threaded message processing to the server
  3. Add client exit event, and annotate, supplement, and adjust the code
  4. To achieve the stage goal, send 2 million packets per second, which can be processed stably, and the code has also been optimized, etc.

1. Client architecture

That is to start multiple threads, and then establish a client in each thread, and then connect with the server to send messages.

eg:

The previous method is to establish 1000 clients in the main thread, then connect and send messages.

Now create four threads, and then disperse 1,000 clients into four threads, each of which processes 250 to reduce the pressure and increase the speed.

2. Server architecture

Start multiple threads. After the client is connected, the newly connected client is allocated to a relatively small number of threads, and then the activity of receiving messages with the client is performed in each thread.

eg:

The previous method was to process the connected 1000 clients in the main thread, then connect and send messages.

Now create four threads, and then disperse the 1000 connected clients into four threads, and each thread handles 250 to reduce the pressure and increase the speed.

3. Overall structure

The place to be improved is the process of connecting the server to the client, increasing the number of client connections received in one second.

One, change the client to multi-thread

  • EasyTcpClient.hpp
#include "EasyTcpClient.hpp"
#include<thread>
#include<mutex>
bool g_bRun = true;
mutex _mutexx;
void cmdThread()//(EasyTcpClient* client)
{
	while (1)
	{
		char cmdBuf[256] = {};

		cin >> cmdBuf;

		if (strcmp(cmdBuf, "exit") == 0){
			g_bRun = false;
			cout << "退出子线程" << endl;
			return;
		}
		else{
			cout << "不支持的命令" << endl;
		}
	}
}

//客户端数量
const int cCount = 100;
//发送线程数量
const int tCount = 4;
//客户端数组
EasyTcpClient* client[cCount];

void sendThread(int id)
{
	{
		lock_guard<mutex> lock(_mutexx);
		cout << "thread " << id << " start" << endl;
	}
	//四个线程ID
	int c = cCount / tCount;
	int begin = (id - 1)*c;
	int end = id*c;

	for (int i = begin; i < end; i++)
	{
		client[i] = new EasyTcpClient();
	}
	for (int i = begin; i < end; i++)
	{
		client[i]->Connect((char *)"127.0.0.1", 4567); //192.168.247.128   
	}

	cout << "thread=" << id << "  Connect=" << begin << "  end= " << end << endl;


	chrono::milliseconds t(3000);
	this_thread::sleep_for(t);

	Login login[1];
	for (int i = 0; i < 10; i++)
	{
		strcpy(login[i].userName, "lyd");
		strcpy(login[i].PassWord, "lydmm");
	}
	const int nLen = sizeof(login);
	int _true = 1;
	while (g_bRun && _true)
	{
		for (int i = begin; i < end; i++)
		{
			if (client[i]->SendData(login,nLen) == SOCKET_ERROR)
			{
				_true = 0;
				break;
			}
			client[i]->OnRun();
		}
	}

	for (int i = begin; i < end; i++)
	{
		delete client[i];
		client[i]->Close();
	}
	g_bRun = false;
	cout << "thread  " << id << "  exit" << endl;
	
}

int main()
{
	//启动UI线程
	thread t1(cmdThread);         //启动线程函数
	t1.detach();

	//启动发送线程
	for (int n = 0; n < tCount; n++)
	{
		thread t1(sendThread, n + 1);
		t1.detach();
	}

	while (g_bRun)
	{
		Sleep(100);
	}

	cout << "已退出" << endl;
	system("pause");
	return 0;
}
  • MessageHeader.hpp

enum CMD
{
	CMD_LOGIN,       //登入
	CMD_LOGIN_RESULT,
	CMD_LOGOUT,      //登出
	CMD_LOGOUT_RESULT,
	CMD_NEW_USER_JOIN,      //新的用户加入
	CMD_ERROR,       //错误
};

struct DataHeader
{
	DataHeader()
	{
		dataLength = sizeof(DataHeader);
		cmd = CMD_ERROR;
	}
	short dataLength;
	short cmd;
};

//匹配四个消息结构体
struct Login : public DataHeader
{
	Login()
	{
		dataLength = sizeof(Login);
		cmd = CMD_LOGIN;
	}
	char userName[32];
	char PassWord[32];
	//char data[932];
};

struct LoginResult : public DataHeader
{
	LoginResult()
	{
		dataLength = sizeof(LoginResult);
		cmd = CMD_LOGIN_RESULT;
		result = 0;
	}
	int result;
	//char data[992];
};

struct Logout : public DataHeader
{
	Logout()
	{
		dataLength = sizeof(Logout);
		cmd = CMD_LOGOUT;
	}
	char userName[32];
};

struct LogoutResult : public DataHeader
{
	LogoutResult()
	{
		dataLength = sizeof(LogoutResult);
		cmd = CMD_LOGOUT_RESULT;
		result = 0;
	}
	int result;
};

struct NewUserJoin :public DataHeader
{
	NewUserJoin()
	{
		dataLength = sizeof(NewUserJoin);
		cmd = CMD_NEW_USER_JOIN;
		sock = 0;
	}
	int sock;
};
  • client.cpp
#include "EasyTcpClient.hpp"
#include<thread>
#include<mutex>
bool g_bRun = true;
mutex _mutexx;
void cmdThread()//(EasyTcpClient* client)
{
	while (1)
	{
		char cmdBuf[256] = {};

		cin >> cmdBuf;

		if (strcmp(cmdBuf, "exit") == 0){
			g_bRun = false;
			cout << "退出子线程" << endl;
			return;
		}
		else{
			cout << "不支持的命令" << endl;
		}
	}
}

//客户端数量
const int cCount = 100;
//发送线程数量
const int tCount = 4;
//客户端数组
EasyTcpClient* client[cCount];

void sendThread(int id)
{
	{
		lock_guard<mutex> lock(_mutexx);
		cout << "thread " << id << " start" << endl;
	}
	//四个线程ID
	int c = cCount / tCount;
	int begin = (id - 1)*c;
	int end = id*c;

	for (int i = begin; i < end; i++)
	{
		client[i] = new EasyTcpClient();
	}
	for (int i = begin; i < end; i++)
	{
		client[i]->Connect((char *)"127.0.0.1", 4567); //192.168.247.128   
	}

	cout << "thread=" << id << "  Connect=" << begin << "  end= " << end << endl;


	chrono::milliseconds t(3000);
	this_thread::sleep_for(t);

	Login login[1];
	for (int i = 0; i < 10; i++)
	{
		strcpy(login[i].userName, "lyd");
		strcpy(login[i].PassWord, "lydmm");
	}
	const int nLen = sizeof(login);
	int _true = 1;
	while (g_bRun && _true)
	{
		for (int i = begin; i < end; i++)
		{
			if (client[i]->SendData(login,nLen) == SOCKET_ERROR)
			{
				_true = 0;
				break;
			}
			client[i]->OnRun();
		}
	}

	for (int i = begin; i < end; i++)
	{
		delete client[i];
		client[i]->Close();
	}
	g_bRun = false;
	cout << "thread  " << id << "  exit" << endl;
	
}

int main()
{
	//启动UI线程
	thread t1(cmdThread);         //启动线程函数
	t1.detach();

	//启动发送线程
	for (int n = 0; n < tCount; n++)
	{
		thread t1(sendThread, n + 1);
		t1.detach();
	}

	while (g_bRun)
	{
		Sleep(100);
	}

	cout << "已退出" << endl;
	system("pause");
	return 0;
}

Second, add multi-threaded processing messages to the server

  • CELLTimestamp.hpp
#ifndef CELLTimestamp_hpp_
#define CELLTimestamp_hpp_

#pragma once
#include<chrono>
using namespace std::chrono;

class CELLTimestamp
{
public:
	CELLTimestamp()
	{
		update();
	}
	~CELLTimestamp()
	{

	}

	void update()
	{
		_begin = high_resolution_clock::now();
	}

	//获取当前秒
	double getElapsedSecond()
	{
		return this->getElapsedTimeInMicroSec() * 0.000001;
	}
	//获取毫秒
	double getElapsedTimeInMilliSec()
	{
		return this->getElapsedTimeInMicroSec() * 0.001;
	}
	//获取微秒
	long long getElapsedTimeInMicroSec()
	{
		return duration_cast<microseconds>(high_resolution_clock::now() - _begin).count();
	}
protected:
	time_point<high_resolution_clock> _begin;
};

#endif
  • EasyTcpServer.hpp
#ifndef _EasyTcpServer_hpp_
#define _EasyTcpServer_hpp_

#ifdef _WIN32
#define FD_SETSIZE 10240
#define WIN32_LEAN_AND_MEAN
#include<windows.h>
#include<Winsock2.h>
#pragma comment(lib,"ws2_32.lib")
#else
#include<unistd.h>
#include<arpa/inet.h>
#include<string.h>

#define SOCKET int
#define INVALID_SOCKET       (SOCKET)(~0)
#define SOCKET_ERROR                 (-1)
#endif

#include<iostream>
#include<vector>
#include<thread>
#include<mutex>
#include<atomic>

#include"MessageHeader.hpp"
#include"CELLTimestamp.hpp"
using namespace std;

//缓冲区最小单元大小
#ifndef RECV_BUFF_SIZE
#define RECV_BUFF_SIZE 10240
#endif

//#define _CellServer_THREAD_COUNT 4

//客户端数据类型
class ClientSocket
{
public:
	ClientSocket(SOCKET sockfd = INVALID_SOCKET)
	{
		_sockfd = sockfd;
		memset(_szMsgBuf, 0, sizeof(_szMsgBuf));
		_lastPos = 0;
	}

	SOCKET sockfd()
	{
		return _sockfd;
	}

	char *msgBuf()
	{
		return _szMsgBuf;
	}

	int getLastPos()
	{
		return _lastPos;
	}

	void setLastPos(int pos)
	{
		_lastPos = pos;
	}

	//发送数据
	int SendData(DataHeader *header)
	{
		if (header)
		{
			return (int)send(_sockfd, (const char *)header, header->dataLength, 0);
		}
		return SOCKET_ERROR;
	}
private:
	SOCKET _sockfd;
	//第二缓冲区,消息缓冲区
	char _szMsgBuf[RECV_BUFF_SIZE * 10];
	//消息缓冲区的数据尾部位置
	int _lastPos = 0;
};

//网络事件接口
class INetEvent
{
public:
	//客户端加入事件
	virtual void OnNetJoin(ClientSocket *pClient) = 0;
	//客户端离开事件
	virtual void OnNetLeave(ClientSocket *pClient) = 0;
	//客户端消息事件
	virtual void OnNetMsg(ClientSocket *pClient, DataHeader *header) = 0;
private:

};


class CellServer
{
public:
	CellServer(SOCKET sock = INVALID_SOCKET)
	{
		_sock = sock;
		_pNetEvent = nullptr;
	}

	~CellServer()
	{
		Close();
		_sock = INVALID_SOCKET;
	}

	//是否工作中
	bool isRun()
	{
		return _sock != INVALID_SOCKET;
	}

	void setEventObj(INetEvent *event)
	{
		_pNetEvent = event;
	}

	//关闭Socket
	void Close()
	{
		if (_sock != INVALID_SOCKET)
		{
#ifdef _WIN32
			for (int n = (int)_clients.size() - 1; n >= 0; n--)
			{
				closesocket(_clients[n]->sockfd());
				delete _clients[n];
			}

			//关闭套接字closesocket
			closesocket(_sock);
			//-----------
			//清除Windows socket环境
			//WSACleanup();
#else
			for (int n = (int)_clients.size() - 1; n >= 0; n--)
			{
				close(_clients[n]->sockfd());
				delete _clients[n];
			}
			//关闭套接字closesocket
			close(_sock);
#endif
			_clients.clear();
		}
	}

	//int mun = 0;
	//处理网络消息
	bool OnRun()
	{
		while (isRun())
		{
			//从缓冲区中取出客户数据
			if (_clientsBuff.size() >0)
			{
				lock_guard<mutex> lock(_mutex);
				for (auto pClient : _clientsBuff)
				{
					_clients.push_back(pClient);
				}
				_clientsBuff.clear();
			}

			//如果没有需要处理的客户端就跳过
			if (_clients.empty())
			{
				chrono::milliseconds t(1);
				this_thread::sleep_for(t);
				continue;
				//return true;
			}

			//伯克利套接字
			fd_set fdRead;
			//fd_set fdWrite;
			//fd_set fdExp;

			//清理集合
			FD_ZERO(&fdRead);
			//FD_ZERO(&fdWrite);
			//FD_ZERO(&fdExp);
			
			//将描述符(socket)加入集合
			//FD_SET(_sock, &fdRead);
			//FD_SET(_sock, &fdWrite);
			//FD_SET(_sock, &fdExp);

			//将描述符(socket)加入集合
			SOCKET maxSock = _clients[0]->sockfd();
			int nn = (int)_clients.size() - 1;
			for (int n = (int)_clients.size() - 1; n >= 0; n--)
			{
				FD_SET(_clients[n]->sockfd(), &fdRead);
				if (maxSock < _clients[n]->sockfd())
				{
					maxSock = _clients[n]->sockfd();
				}
			}

			//nfds 是一个整数值,是指fd_set集合中所有描述符(socket)的范围,而不是数量
			//即是所有文件描述符最大值+1,在Windows中这个参数可以写0

			//添加非阻塞
			//timeval t = { 1, 0 };

			int ret = select(maxSock + 1, &fdRead, 0, 0, nullptr);

			if (ret < 0)
			{
				//cout << "select任务结束  " <<nn<< endl;
				//Close();
				continue;
			}

			for (int n = (int)_clients.size() - 1; n >= 0; n--)
			{
				if (FD_ISSET(_clients[n]->sockfd(), &fdRead))
				{ 
					if (RecvData(_clients[n]) == -1)
					{
						auto iter = _clients.begin() + n;
						if (iter != _clients.end())
						{
							if (_pNetEvent)
							{
								_pNetEvent->OnNetLeave(_clients[n]);
							}
							///顺序不能变
							delete _clients[n];
							_clients.erase(iter);
						}
					}
				}
			}
		}
		return true;
	}

	//缓冲区
	char _szRecv[RECV_BUFF_SIZE];// = {};
	//接收数据 处理粘包哦拆分包
	int RecvData(ClientSocket* pClient)
	{
		//5,接受客户端的请求数据
		int nLen = (int)recv(pClient->sockfd(), (char*)&_szRecv, RECV_BUFF_SIZE, 0);
		if (nLen <= 0)
		{
			//cout <<pClient->sockfd()<< " 客户端已经退出,任务结束" << endl;
			return -1;
		}
		//将收取到的消息拷贝到消息缓冲区
		memcpy(pClient->msgBuf() + pClient->getLastPos(), _szRecv, nLen);
		//消息缓冲区的数据尾部后移
		pClient->setLastPos(pClient->getLastPos() + nLen);

		//判断消息缓冲区的数据长度大于消息头DataHeader长度
		while (pClient->getLastPos() >= sizeof(DataHeader))
		{
			//这个时候就知道当前消息长度
			DataHeader *header = (DataHeader*)pClient->msgBuf();
			//判断消息缓冲区的长度大于消息长度
			if (pClient->getLastPos() >= header->dataLength)
			{
				//消息缓冲区剩余未处理数据的长度
				int nSize = pClient->getLastPos() - header->dataLength;
				//处理网络消息
				OnNetMsg(pClient, header);
				//将消息缓冲区剩余未处理数据前移
				memcpy(pClient->msgBuf(), pClient->msgBuf() + header->dataLength, nSize);
				pClient->setLastPos(nSize);
			}
			else{
				//消息缓冲区剩余数据不够一条完整消息
				break;
			}
		}
		return 0;
	}

	//响应网络消息
	virtual void OnNetMsg(ClientSocket *pClient, DataHeader *header)
	{
		_pNetEvent->OnNetMsg(pClient, header);
	}

	void addClient(ClientSocket *pClient)
	{
		lock_guard<mutex> lock(_mutex);
		//_mutex.lock();
		_clientsBuff.push_back(pClient);
		//_mutex.unlock();
	}
	
	void Start()
	{
		_thread = thread(mem_fn(&CellServer::OnRun), this);
	}

	size_t getClientCount()
	{
		return _clients.size()+_clientsBuff.size();
	}
private:
	SOCKET _sock;
	//正式客户队列
	vector<ClientSocket*> _clients;
	//客户缓冲队列
	vector<ClientSocket*> _clientsBuff;
	//缓冲队列的锁
	mutex _mutex;
	thread _thread;
	//网络事件对象
	INetEvent *_pNetEvent;
};


class EasyTcpServer:public INetEvent
{
private:
	SOCKET _sock;
	//vector<ClientSocket*> _clients;
	//消息处理对象,内部会创建线程
	vector<CellServer*> _cellServers;
	//每秒消息计时
	CELLTimestamp _tTime;
	//收到消息计数
public:
	atomic<int> _recvCount;
	//客户端计数
	atomic<int> _clientCount;

public:
	EasyTcpServer()
	{
		_sock = INVALID_SOCKET;
		_recvCount = 0;
		_clientCount = 0;
	}

	virtual ~EasyTcpServer()
	{
		Close();
	}

	//初始化Socket
	SOCKET InitSocket()
	{
#ifdef _WIN32
		//启动Windows socket 2.x环境
		WORD ver = MAKEWORD(2, 2);
		WSADATA dat;
		WSAStartup(ver, &dat);
#endif
		//1,用Socket API建立建立TCP客户端
		if (_sock != INVALID_SOCKET)
		{
			cout << "_sock=" << (int)_sock << "关闭旧连接" << endl;
			Close();
		}
		_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

		if (_sock == INVALID_SOCKET)
		{
			cout << "建立socket失败" << endl;
		}
		else
		{
			cout << (int)_sock << " 建立socket成功" << endl;
		}
		return _sock;
	}

	//绑定IP和端口号
	int Bind(const char *ip, unsigned short port)
	{
		//if (_sock != INVALID_SOCKET)
		//{
		//    InitSocket();
		//}
		//2,bind 绑定用于接受客户端连接的网络接口
		sockaddr_in _sin = {};
		_sin.sin_family = AF_INET;
		_sin.sin_port = htons(port);
#ifdef _WIN32
		if (ip){
			_sin.sin_addr.S_un.S_addr = inet_addr(ip);
		}
		else{
			_sin.sin_addr.S_un.S_addr = INADDR_ANY;
		}
#else
		if (ip){
			_sin.sin_addr.s_addr = inet_addr(ip);
		}
		else{
			_sin.sin_addr.s_addr = INADDR_ANY;
		}
#endif


		int ret = ::bind(_sock, (sockaddr*)&_sin, sizeof(_sin));
		if (SOCKET_ERROR == ret)
		{
			cout << (int)_sock << "错误,绑定网络端口失败" << endl;
		}
		else
		{
			cout << "绑定网络端口成功" << port << endl;
		}
		return ret;
	}

	//监听端口号
	int Listen(int n)
	{
		int ret = listen(_sock, n);
		if (SOCKET_ERROR == ret)
		{
			cout << (int)_sock << " 错误,监听网络端口失败" << endl;
		}
		else
		{
			cout << (int)_sock << " 监听网络端口成功" << endl;
		}
		return ret;
	}

	//接受客户端连接
	SOCKET Accept()
	{
		//4,accept 等待客户端连接
		sockaddr_in clientAddr = {};
		int nAddrlen = sizeof(clientAddr);
		SOCKET cSock = INVALID_SOCKET;

#ifdef _WIN32
		cSock = accept(_sock, (sockaddr *)&clientAddr, &nAddrlen);
#else
		cSock = accept(_sock, (sockaddr *)&clientAddr, (socklen_t *)&nAddrlen);
#endif
		if (INVALID_SOCKET == cSock)
		{
			cout << (int)_sock << " 错误,接受到无效的客户端连接" << endl;
		}
		else
		{
			//NewUserJoin userJoin;
			//SendDataToAll(&userJoin);
			//将新客户端分配给客户端数量最少的cellServer
			addClientToCellServer(new ClientSocket(cSock));
			//获取IP地址 inet_ntoa(clientAddr.sin_addr)
		}
		return _sock;
	}


	void addClientToCellServer(ClientSocket *pClient)
	{
		//_clients.push_back(pClient);
		//查找客户数量最少的CellServer消息处理对象
		auto pMinServer = _cellServers[0];
		for (auto pCellServer : _cellServers)
		{
			if (pMinServer->getClientCount() > pCellServer->getClientCount())
			{
				pMinServer = pCellServer;
			}
		}
		pMinServer->addClient(pClient);
		OnNetJoin(pClient);
	}


	void Start(int _CellServer_THREAD_COUNT)
	{
		for (int n = 0; n < _CellServer_THREAD_COUNT; n++)
		{
			auto ser = new CellServer(_sock);
			_cellServers.push_back(ser);
			//注册网络事件接收对象
			ser->setEventObj(this);
			//启动消息处理线程
			ser->Start();
		}
	}

	//关闭Socket
	void Close()
	{
		if (_sock != INVALID_SOCKET)
		{
#ifdef _WIN32
			//for (int n = (int)_clients.size() - 1; n >= 0; n--)
			//{
			//	closesocket(_clients[n]->sockfd());
			//	delete _clients[n];
			//}

			//关闭套接字closesocket
			closesocket(_sock);
			//-------------
			//清除Windows socket环境
			WSACleanup();
#else
			for (int n = (int)_clients.size() - 1; n >= 0; n--)
			{
				close(_clients[n]->sockfd());
				delete _clients[n];
			}
			//8,关闭套接字closesocket
			close(_sock);
#endif
			//_clients.clear();
		}
	}

	//处理网络消息
	bool OnRun()
	{
		if (isRun())
		{
			time4msg();
			//伯克利套接字
			fd_set fdRead;
			//fd_set fdWrite;
			//fd_set fdExp;

			FD_ZERO(&fdRead);
			//FD_ZERO(&fdWrite);
			//FD_ZERO(&fdExp);

			FD_SET(_sock, &fdRead);
			//FD_SET(_sock, &fdWrite);
			//FD_SET(_sock, &fdExp);

			//nfds 是一个整数值,是指fd_set集合中所有描述符(socket)的范围,而不是数量
			//即是所有文件描述符最大值+1,在Windows中这个参数可以写0

			//添加非阻塞
			timeval t = { 0, 10 };

			int ret = select(_sock + 1, &fdRead, 0, 0, &t);  //

			if (ret < 0)
			{
				cout << "Accept Select任务结束" << endl;
				Close();
				return false;
			}

			if (FD_ISSET(_sock, &fdRead))
			{
				FD_CLR(_sock, &fdRead);
				Accept();
				return true;
			}

			return true;
		}
		return false;
	}

	//是否工作中
	bool isRun()
	{
		return _sock != INVALID_SOCKET;
	}

	//响应网络消息
	void time4msg()
	{
		auto t1 = _tTime.getElapsedSecond();
		if (_tTime.getElapsedSecond() >= 1.0)
		{
			//cout << "thread   "<<_cellServers.size()<<"   tTime    " << t1 <<"    client    " <<(int)_clients.size()<<"	socket	" << _sock << "	_recvCount	" << _recvCount<< endl;
			cout << "thread   " << _cellServers.size() << "   tTime    " << t1 << "    client    " << (int)_clientCount << "	socket	" << _sock << "	_recvCount	" << _recvCount << endl;
			_recvCount = 0;
			_tTime.update();
		}
	}

	//群发消息
	//void SendDataToAll(DataHeader *header)
	//{
	//	for (int n = (int)_clients.size() - 1; n >= 0; n--)
	//	{
	//		SendData(_clients[n]->sockfd(), header);
	//	}
	//}
	

	//只会被一个线程调用 安全
	virtual void OnNetJoin(ClientSocket *pClient)
	{
		_clientCount++;
	}
	//4 多个线程触发不安全
	//如果只开启一个cellServer就是安全的
	virtual void OnNetLeave(ClientSocket *pClient)
	{
		_clientCount--;
		//for (int n = (int)_clients.size() - 1; n >= 0; n--)
		//{
		//	if (_clients[n] == pClient)
		//	{
		//		auto iter = _clients.begin() + n;
		//		if (iter != _clients.end())
		//		{
		//			_clients.erase(iter);
		//		}
		//	}
		//}
	}
	//4 多个线程触发不安全
	//如果只开启一个cellServer就是安全的
	virtual void OnNetMsg(ClientSocket *pClient, DataHeader *header)
	{
		_recvCount++;
	}
};

#endif
  • MessageHeader.hpp
enum CMD
{
	CMD_LOGIN,       //登入
	CMD_LOGIN_RESULT,
	CMD_LOGOUT,      //登出
	CMD_LOGOUT_RESULT,
	CMD_NEW_USER_JOIN,      //新的用户加入
	CMD_ERROR,       //错误
};

struct DataHeader
{
	DataHeader()
	{
		dataLength = sizeof(DataHeader);
		cmd = CMD_ERROR;
	}
	short dataLength;
	short cmd;
};

//匹配四个消息结构体
struct Login : public DataHeader
{
	Login()
	{
		dataLength = sizeof(Login);
		cmd = CMD_LOGIN;
	}
	char userName[32];
	char PassWord[32];
	//char data[932];
};

struct LoginResult : public DataHeader
{
	LoginResult()
	{
		dataLength = sizeof(LoginResult);
		cmd = CMD_LOGIN_RESULT;
		result = 0;
	}
	int result;
	//char data[992];
};

struct Logout : public DataHeader
{
	Logout()
	{
		dataLength = sizeof(Logout);
		cmd = CMD_LOGOUT;
	}
	char userName[32];
};

struct LogoutResult : public DataHeader
{
	LogoutResult()
	{
		dataLength = sizeof(LogoutResult);
		cmd = CMD_LOGOUT_RESULT;
		result = 0;
	}
	int result;
};

struct NewUserJoin :public DataHeader
{
	NewUserJoin()
	{
		dataLength = sizeof(NewUserJoin);
		cmd = CMD_NEW_USER_JOIN;
		sock = 0;
	}
	int sock;
};
  • server.cpp
#include"EasyTcpServer.hpp"
#include<thread>

class MyServer :public EasyTcpServer
{
public:
	//只会被一个线程调用 安全
	virtual void OnNetJoin(ClientSocket *pClient)
	{
		_clientCount++;
		//cout << "client " << pClient->sockfd() << "join" << endl;
	}
	//4 多个线程触发不安全
	//如果只开启一个cellServer就是安全的
	virtual void OnNetLeave(ClientSocket *pClient)
	{
		_clientCount--;
		//cout << "client " << pClient->sockfd() << "leave" << endl;
	}
	//4 多个线程触发不安全
	//如果只开启一个cellServer就是安全的
	virtual void OnNetMsg(ClientSocket *pClient, DataHeader *header)
	{
		_recvCount++;
		switch (header->cmd)
		{
		case CMD_LOGIN:
		{
						  //做数据偏移
						  Login *login = (Login*)header;
						  //cout << "收到命令:CMD_LOGIN,  数据长度:" << login->dataLength;
						  //cout << "  UserName:" << login->userName << "  PassWord:" << login->PassWord << endl;
						  //忽略判断用户名密码是否正确的过程
						  LoginResult ret;
						  pClient->SendData(&ret);
		}
			break;
		case CMD_LOGOUT:
		{
						   Login *logout = (Login*)header;
						   cout << "收到命令:CMD_LOGIN,  数据长度:" << logout->dataLength;
						   cout << "  UserName:" << logout->userName << endl;
						   //忽略判断用户名密码是否正确的过程
						   LogoutResult ret;
						   //SendData(_cSock, (DataHeader *)&ret);
		}
			break;
		default:
		{
				   cout << pClient->sockfd() << "  收到未定义的消息,数据长度  " << header->dataLength;
				   DataHeader ret;
				   //SendData(_cSock, (DataHeader *)&ret);
		}
			break;
		}
	}
private:

};
int main()
{
	MyServer server;
	server.InitSocket();
	server.Bind(nullptr, 4567);
	server.Listen(5);
	server.Start(4);

	while (server.isRun())
	{
		server.OnRun();
	}

	server.Close();
	cout << "已退出" << endl;
	system("pause");
	return 0;
}

 

Guess you like

Origin blog.csdn.net/qq_46423166/article/details/111239592