C++ Socket网络编程 1.5版本 使用C++面向对象编程对服务端进行改进

本节实现用C++的面向对象编程来封装服务端的业务逻辑。面向对象编程的优点这里就不赘述了,封装后的类可以使业务逻辑更清晰,完成复杂的功能更容易。
基本实现的方法没有太多改变,只是讲服务端Socket通信的每个步骤封装到了类中。OnRun函数用于Select网络模型请求判断。
在后续的改进中,可以将为类添加更多的成员变量或成员函数,或者构造派生类,实现不同的业务逻辑。

class EasyTcpServer
{
public:
	SOCKET _sock;
	std::vector<SOCKET> g_clinets;
	EasyTcpServer()
	{
		_sock = INVALID_SOCKET;
	}
	virtual ~EasyTcpServer()
	{
		_closeSocket();
	}
	void initSocket()
	{
#ifdef _WIN32
		//启动Windows sock 2.x环境
		WORD versionCode = MAKEWORD(2, 2);	//创建一个版本号 
		WSADATA data;
		WSAStartup(versionCode, &data);  //启动Socket网络API的函数
#endif
										 //1. 建立一个Socket
		if (_sock != INVALID_SOCKET)
		{
			cout << "<socket=>" << _sock << " 关闭了已有连接并重新建立连接" << endl;
			_closeSocket();
		}
		_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //用于网络链接的ipv4的socket TCP连接
		if (INVALID_SOCKET == _sock)	//SIocket 每一步都可以判断是否成功建立
		{
			cout << "Socket = <" << _sock << ">" << " ERROR: SOCKET 建立失败" << endl;
		}
		else
		{
			cout << "Socket = <" << _sock << ">" << " SUCCESS: SOCKET 建立成功" << endl;
		}
		return ;
	}

	//绑定端口
	int Bind(const char* ip,unsigned short port)
	{
		if (_sock == INVALID_SOCKET)
		{
			initSocket();
		}
		sockaddr_in _sin = {};
		_sin.sin_family = AF_INET;
		_sin.sin_port = htons(port);
		if (ip)
		{
#ifdef _WIN32
			_sin.sin_addr.S_un.S_addr = inet_addr(ip);
#else
			_sin.sin_addr.S_addr = inet_addr(ip);
#endif
		}
		else
		{
#ifdef _WIN32
			_sin.sin_addr.S_un.S_addr = INADDR_ANY;
#else
			_sin.sin_addr.S_addr = INADDR_ANY;
#endif
		}
		if (bind(_sock, (const sockaddr*)&_sin, sizeof(sockaddr_in)) == SOCKET_ERROR)  //sockaddr 不利于编码  
		{
			cout << "Socket = <" << _sock << ">" << " ERROR: 绑定用于接受客户端连接的网络端口<"<<port<<">失败..." << endl;
			return -1;
		}
		else
		{
			cout << "Socket = <" << _sock << ">" << " SUCCESS: 绑定端口成功..." << endl;
		}
		return 0;
	}
	//监听端口
	int Listen(int _maxNumber)
	{
		if (listen(_sock, _maxNumber) == SOCKET_ERROR)//第二个参数 backbag 最大允许连接数量
		{
			cout << "Socket = <"<<_sock<<">"<<" ERROR: 监听用于接受客户端连接的网络端口失败..." << endl;
			return SOCKET_ERROR;
		}
		else
		{
			cout << "Socket = <" << _sock << ">" << " SUCCESS: 监听端口成功..." << endl;
			return 1;
		}
	}
	//接收连接
	SOCKET Accept()
	{
		//	4. 等待接受客户端连接 accept
		sockaddr_in _clientAddr = {};
		int cliendAddrLen = sizeof(_clientAddr);
		SOCKET _clientSock = INVALID_SOCKET; // 初始化无效的socket 用来存储接入的客户端

		_clientSock = accept(_sock, (sockaddr*)&_clientAddr, &cliendAddrLen);//当客户端接入时 会得到连入客户端的socket地址和长度
		if (INVALID_SOCKET == _clientSock) //接受到无效接入
		{
			cout << "Socket = <" << _sock << " ERROR: 接受到无效客户端SOCKET..." << endl;
		}
		else
		{
			cout << "新Client加入:" << "socket = " << _clientSock << " IP = " << inet_ntoa(_clientAddr.sin_addr) << endl;  //inet_ntoa 将ip地址转换成可读的字符串
			for (int n = g_clinets.size() - 1; n >= 0; n--)
			{
				NewUserJoin userJoin;
				userJoin.cmd = CMD_NEWUSERJOIN;
				userJoin.sockId = _clientSock;
				SendData2All(&userJoin);
				//send(g_clinets[n], (const char*)&userJoin, userJoin.dataLength, 0);
			}
			g_clinets.push_back(_clientSock);
		}
		return _clientSock;
	}
	//处理网络消息
	bool OnRun()
	{
		if (isRun())
		{
			fd_set fdRead;
			fd_set fdWrite;
			fd_set fdExpect;

			FD_ZERO(&fdRead);		//清空fd集合的数据
			FD_ZERO(&fdWrite);
			FD_ZERO(&fdExpect);
			//这个宏的功能是 将服务端的_sock 放到fdRead这个集合中 
			//当socket在listen状态,如果已经接收一个连接请求,这个socket会被标记为可读,例如一个accept会确保不会阻塞的完成
			//对于其他的socket,可读性意味着队列中的数据适合读,当调用recv后不会阻塞。
			FD_SET(_sock, &fdRead);  //将服务端的socket放入可读列表,确保accept不阻塞
			FD_SET(_sock, &fdWrite);
			FD_SET(_sock, &fdExpect);

			for (size_t n = 0; n < g_clinets.size(); n++)
			{
				FD_SET(g_clinets[n], &fdRead);		//所有连入的客户端放入可读列表 保证recv不阻塞
			}

			//nfds第一个参数 是一个整数值 是指fd_set集合中所有socket值的范围 不是数量 
			timeval t = { 1,0 }; //select查询超时的时间  windows下的计时器 目前没有计算微秒  0表示select函数如果查询没有需要处理,立即返回
			int ret = select(_sock + 1, &fdRead, &fdWrite, &fdExpect, &t);
			if (ret < 0)
			{
				cout << "select任务结束" << endl;
				return false;
			}
			if (FD_ISSET(_sock, &fdRead))	//判断_sock是否在fdRead中, 如果在 表明有客户端连接请求
			{
				FD_CLR(_sock, &fdRead);
				Accept();
			}
			for (size_t n = 0; n < fdRead.fd_count; n++)
			{
				if (RecvData(fdRead.fd_array[n]) == -1)//processor函数是处理命令的逻辑 recv接到的数据并做出相应的判断和输出日志
				{
					auto it = find(g_clinets.begin(), g_clinets.end(), fdRead.fd_array[n]);
					if (it != g_clinets.end())
						g_clinets.erase(it);
				}
			}
			return true;
		}
		return false;
	}
	bool isRun()
	{
		return _sock != INVALID_SOCKET;
	}

	//select网络模型
	//接收数据
	int RecvData(SOCKET _clientSock)
	{
		char *szRecv = new char[1024];
		//5 首先接收数据包头
		int nlen = recv(_clientSock, szRecv, sizeof(DataHeader), 0); //接受客户端的数据 第一个参数应该是客户端的socket对象
		if (nlen <= 0)
		{
			//客户端退出
			cout << "客户端:Socket = " << _clientSock << " 已退出,任务结束" << endl;
			return -1;
		}
		DataHeader* header = (DataHeader*)szRecv;
		recv(_clientSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);
		OnNetMsg(_clientSock, header);
		return 0;
	}

	//响应网络消息
	virtual void OnNetMsg(SOCKET _clientSock, DataHeader *header)
	{
		switch (header->cmd)
		{
			case CMD_LOGIN:
			{
				Login* _login;
				_login = (Login*)header;
				cout << "收到" << "socket = " << _clientSock << " 命令:CMD_LOGIN" << " 数据长度 = " << header->dataLength << " UserName = " << _login->userName << " Password = " << _login->Password << endl;
				//忽略了判断用户名密码是否正确的过程
				LoginResult _loginres;
				send(_clientSock, (char*)&_loginres, sizeof(LoginResult), 0);
			}break;
			case CMD_LOGINOUT:
			{
				Logout *_logout;
				_logout = (Logout*)header;
				cout << "收到" << "socket = " << _clientSock << " 命令:CMD_LOGOUT" << " 数据长度 = " << header->dataLength << " UserName = " << _logout->userName << endl;
				LogoutResult _logoutres;
				send(_clientSock, (char*)&_logoutres, sizeof(LogoutResult), 0);
			}break;
			default:
			{
				header->cmd = CMD_ERROR;
				header->dataLength = 0;
				send(_clientSock, (char*)&header, sizeof(DataHeader), 0);
			}
			break;
		}
	}

	//发送到指定的socket数据
	int SendData(SOCKET _clinetSock, DataHeader* header)
	{
		if (isRun() && header)
		{
			//cout << "发送数据给服务端" << endl;
			int ret = send(_clinetSock, (const char*)header, header->dataLength, 0);
			return ret;
		}
		return SOCKET_ERROR;
	}

	void SendData2All(DataHeader *header)
	{
		if (isRun() && header)
		{
			//cout << "发送数据给服务端" << endl;
			for (int n = g_clinets.size() - 1; n >= 0; n--)
			{
				NewUserJoin userJoin;
				userJoin.cmd = CMD_NEWUSERJOIN;
				userJoin.sockId = g_clinets[n];
				//send(g_clinets[n], (const char*)&userJoin, userJoin.dataLength, 0);
				SendData(g_clinets[n], header);
			}
		}
		return;
	}
	void _closeSocket()
	{
		if (_sock == INVALID_SOCKET)
			return;
		//7. 关闭 socket
#ifdef _WIN32
		//清理win sock环境
		closesocket(_sock);
		for (int n = g_clinets.size() - 1; n >= 0; n--)
		{
			closesocket(g_clinets[n]);
		}
		// 清除Windows socket环境
		WSACleanup();
#else
		close(_sock);
#endif
		cout << "任务结束" << endl;
		getchar();
		_sock = INVALID_SOCKET;
	}
};

猜你喜欢

转载自blog.csdn.net/La745739773/article/details/89136996