C ++の同時実行性の高いネットワークアーキテクチャと実装-パート3-

目次

まず、情報の単純な構造化された伝達を実現しますが、それが構造であるかどうかを区別することはできません。

次に、ネットワークデータメッセージの形式を介して定義された送信を実現します。

第三に、主にエラーを回避するために構造を統合するために、複数回送信されるパケットデータを1つの送信と受信にアップグレードします。(データオフセットが必要です)

第四に、受信バッファを追加します。

第5に、サーバー側を選択モデルに変更して、複数のクライアントを処理するという目標を達成します。

第6に、サーバーは選択処理マルチクライアントモデルにアップグレードされ、クライアントが参加したときに接続されたクライアントに警告できます。


今日のタスクが実現されました。サーバーは、マルチクライアントモデルを処理するように選択するようにアップグレードされ、クライアントが参加したときに接続されたクライアントに通知することができます。

  1. 単純な構造化された情報の伝達を実現しますが、それが構造であるかどうかを区別することはできません。
  2. ネットワークデータメッセージ形式による定義送信を実現します。
  3. パケットデータの複数の送受信を1つの送受信にアップグレードし、主に構造を統合してエラーを回避します。(データオフセットが必要です)
  4. 受信バッファを追加します。
  5. サーバー側を選択モデルに変更して、複数のクライアントを処理できるようにするという目標を達成します。
  6. サーバーは選択処理マルチクライアントモデルにアップグレードされ、クライアントが参加したときに接続されたクライアントに警告できます。

まず、情報の単純な構造化された伝達を実現しますが、それが構造であるかどうかを区別することはできません。

情報を構造モードに設定して転送します。

クライアントはサーバーから返送されたメッセージを構造モードで受信するため、返されるデータが構造モードでない場合はエラーが発生するという問題があります。

struct DataPackage
{     int age;     文字名[32]; };


手順の結果:

サーバ:

#define WIN32_LEAN_AND_MEAN
#include<iostream>
#include<windows.h>
#include<Winsock2.h>

using namespace std;

struct DataPackage
{
	int age;
	char name[32];
};

int main()
{
	//启动Windows socket 2.x环境
	WORD ver = MAKEWORD(2, 2);
	WSADATA dat;
	WSAStartup(ver, &dat);
	//---------------------------------
	//1,建立一个socket
	SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	//2,bind 绑定用于接受客户端连接的网络接口
	sockaddr_in _sin = {};
	_sin.sin_family = AF_INET;
	_sin.sin_port = htons(4567);
	_sin.sin_addr.S_un.S_addr = INADDR_ANY;
	if (SOCKET_ERROR == bind(_sock, (sockaddr*)&_sin, sizeof(_sin)))
	{
		cout << "错误,绑定网络端口失败" << endl;
	}
	else
	{
		cout << "绑定网络端口成功" << endl;
	}

	//3,listen 监听网络端口
	if (SOCKET_ERROR == listen(_sock, 5))
	{
		cout << "错误,监听网络端口失败" << endl;
	}
	else
	{
		cout << "监听网络端口成功" << endl;
	}

	//4,accept 等待客户端连接
	sockaddr_in clientAddr;
	int nAddrlen = sizeof(clientAddr);
	SOCKET _cSock = INVALID_SOCKET;

	_cSock = accept(_sock, (sockaddr *)&clientAddr, &nAddrlen);
	if (INVALID_SOCKET == _cSock)
	{
		cout << "错误,接受到无效的客户端连接" << endl;
	}

	cout << "新的客户端加入:" << inet_ntoa(clientAddr.sin_addr) << endl;

	char _recvBuf[128] = {};
	while (true)
	{
		int nLen = recv(_cSock, _recvBuf, 128, 0);
		//5,接受客户端的请求数据
		if (nLen <= 0)
		{
			cout << "客户端已经退出,任务结束" << endl;
			break;
		}
		cout << "收到消息" << _recvBuf << endl;
		//6,处理请求
		if (0 == strcmp(_recvBuf, "getInfo"))
		{
			DataPackage dp = { 20, "小明" };
			//7.1,send 向客户端发送一条数据
			send(_cSock, (const char*)&dp, sizeof(DataPackage), 0);
			memset(_recvBuf, '\n',128);
		}
		else
		{
			char msgBuf[] = "???";
			//7.3,send 向客户端发送一条数据
			send(_cSock, msgBuf, strlen(msgBuf) + 1, 0);
		}
	}

	//8,关闭套接字closesocket
	closesocket(_sock);

	//-----------------------------------
	//清除Windows socket环境
	WSACleanup();
	system("pause");
	return 0;
}

クライアント:

#define WIN32_LEAN_AND_MEAN
#include<iostream>
#include<windows.h>
#include<Winsock2.h>

using namespace std;

struct DataPackage
{
	int age;
	char name[32];
};

int main()
{
	//启动Windows socket 2.x环境
	WORD ver = MAKEWORD(2, 2);
	WSADATA dat;
	WSAStartup(ver, &dat);
	//---------------------------------
	//1,用Socket API建立建立TCP客户端
	SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	//2,连接服务器 connect
	sockaddr_in _sin = {};
	_sin.sin_family = AF_INET;
	_sin.sin_port = htons(4567);
	_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	int ret = connect(_sock, (sockaddr*)&_sin, sizeof(_sin));
	if (SOCKET_ERROR == ret)
	{
		cout << "错误,建立Socket失败" << endl;
	}
	else
	{
		cout << "建立Socket成功" << endl;
	}

	while (true)
	{
		//3,输入请求
		char cmdBuf[128] = {};
		cin >> cmdBuf;
		//4,处理请求
		if (0 == strcmp(cmdBuf, "exit"))
		{
			break;
		}
		else
		{
			//5,向服务器发送请求
			send(_sock, cmdBuf, strlen(cmdBuf) + 1, 0);
		}
		//6,接受服务器消息
		char recvBuf[128] = {};
		int nlen = recv(_sock, recvBuf, 256, 0);
		if (nlen > 0)
		{
			DataPackage *dp = (DataPackage *)recvBuf;
			cout << "接收到数据: " << "年纪:" << dp->age << "  姓名:" << dp->name << endl;
		}
	}
	//7,关闭套接字closesocket
	closesocket(_sock);
	//-----------------------------------
	//清除Windows socket环境
	WSACleanup();
	system("pause");
	return 0;
}

次に、ネットワークデータメッセージの形式を介して定義された送信を実現します。

最初のものから、構造だけを送信に使用するとエラーが発生するため、ネットワークデータメッセージ形式を使用する必要があると結論付けることができます。

メッセージには、ネットワークメッセージの基本単位であるヘッダーと本文の2つの部分があります。
ヘッダー:メッセージのサイズとデータの役割を説明します。
本文:データ

したがって、この時点でユニオンを定義して、データ
列挙型の役割を記述しますCMD
{     CMD_LOGIN、// login     CMD_LOGOUT、// logout     CMD_ERROR、// error };



スクリーンショットを実行します。

サーバー側コード:

#define WIN32_LEAN_AND_MEAN
#include<iostream>
#include<windows.h>
#include<Winsock2.h>

using namespace std;

enum CMD
{
	CMD_LOGIN,       //登入
	CMD_LOGOUT,      //登出
	CMD_ERROR,       //错误
};

struct DataHeader
{
	short dataLength;
	short cmd;
};

//匹配四个消息结构体
struct Login
{
	char useName[32];
	char PassWord[32];
};

struct LoginResult
{
	int result;
};

struct Logout
{
	char userName[32];
};

struct LogoutResult
{
	int result;
};

int main()
{
	//启动Windows socket 2.x环境
	WORD ver = MAKEWORD(2, 2);
	WSADATA dat;
	WSAStartup(ver, &dat);
	//---------------------------------
	//1,建立一个socket
	SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	//2,bind 绑定用于接受客户端连接的网络接口
	sockaddr_in _sin = {};
	_sin.sin_family = AF_INET;
	_sin.sin_port = htons(4567);
	_sin.sin_addr.S_un.S_addr = INADDR_ANY;
	if (SOCKET_ERROR == bind(_sock, (sockaddr*)&_sin, sizeof(_sin)))
	{
		cout << "错误,绑定网络端口失败" << endl;
	}
	else
	{
		cout << "绑定网络端口成功" << endl;
	}

	//3,listen 监听网络端口
	if (SOCKET_ERROR == listen(_sock, 5))
	{
		cout << "错误,监听网络端口失败" << endl;
	}
	else
	{
		cout << "监听网络端口成功" << endl;
	}

	//4,accept 等待客户端连接
	sockaddr_in clientAddr;
	int nAddrlen = sizeof(clientAddr);
	SOCKET _cSock = INVALID_SOCKET;

	_cSock = accept(_sock, (sockaddr *)&clientAddr, &nAddrlen);
	if (INVALID_SOCKET == _cSock)
	{
		cout << "错误,接受到无效的客户端连接" << endl;
	}

	cout << "新的客户端加入:" << inet_ntoa(clientAddr.sin_addr) << endl;

	while (true)
	{
		DataHeader header = {};
		int nLen = recv(_cSock, (char*)&header, sizeof(DataHeader), 0);
		//5,接受客户端的请求数据
		if (nLen <= 0)
		{
			cout << "客户端已经退出,任务结束" << endl;
			break;
		}
		cout << "收到命令:" << header.cmd << "  数据长度:" << header.dataLength << endl;

		switch (header.cmd)
		{
		case CMD_LOGIN:
		{
						  Login login = {};
						  recv(_cSock, (char*)&login, sizeof(Login), 0);
						  //忽略判断用户名密码是否正确的过程
						  LoginResult ret = { 1 };
						  DataHeader hd = { CMD_LOGIN };
						  send(_cSock, (const char*)&header, sizeof(DataHeader), 0);
						  send(_cSock, (const char*)&ret, sizeof(LoginResult), 0);
		}
			break;
		case CMD_LOGOUT:
		{
						   Logout logout = {};
						   recv(_cSock, (char*)&logout, sizeof(Logout), 0);
						   //忽略判断用户名密码是否正确的过程
						   LogoutResult ret = { 1 };
						   send(_cSock, (const char*)&header, sizeof(DataHeader), 0);
						   send(_cSock, (const char*)&ret, sizeof(Logout), 0);
		}
			break;
		default:
			header.cmd = CMD_ERROR;
			header.dataLength = 0;
			send(_cSock, (const char*)&header, sizeof(DataHeader), 0);
			break;
		}
	}

	//8,关闭套接字closesocket
	closesocket(_sock);

	//-----------------------------------
	//清除Windows socket环境
	WSACleanup();
	system("pause");
	return 0;
}

クライアントコード:

#define WIN32_LEAN_AND_MEAN
#include<iostream>
#include<windows.h>
#include<Winsock2.h>

using namespace std;

enum CMD
{
	CMD_LOGIN,       //登入
	CMD_LOGOUT,      //登出
	CMD_ERROR,       //错误
};

struct DataHeader
{
	short dataLength;
	short cmd;
};

//匹配四个消息结构体
struct Login
{
	char useName[32];
	char PassWord[32];
};

struct LoginResult
{
	int result;
};

struct Logout
{
	char userName[32];
};

struct LogoutResult
{
	int result;
};

int main()
{
	//启动Windows socket 2.x环境
	WORD ver = MAKEWORD(2, 2);
	WSADATA dat;
	WSAStartup(ver, &dat);
	//---------------------------------
	//1,用Socket API建立建立TCP客户端
	SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	//2,连接服务器 connect
	sockaddr_in _sin = {};
	_sin.sin_family = AF_INET;
	_sin.sin_port = htons(4567);
	_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	int ret = connect(_sock, (sockaddr*)&_sin, sizeof(_sin));
	if (SOCKET_ERROR == ret)
	{
		cout << "错误,建立Socket失败" << endl;
	}
	else
	{
		cout << "建立Socket成功" << endl;
	}

	while (true)
	{
		//3,输入请求
		char cmdBuf[128] = {};
		cin >> cmdBuf;
		//4,处理请求
		if (0 == strcmp(cmdBuf, "exit")){
			break;
		}
		else if (0 == strcmp(cmdBuf, "login")){
			Login login = { "lyd", "lydmm" };
			DataHeader dh = { sizeof(Login), CMD_LOGIN };
			//5,向服务器发送请求
			send(_sock, (const char *)&dh, sizeof(DataHeader), 0);
			send(_sock, (const char *)&login, sizeof(Login), 0);
			//接收服务器返回数据
			DataHeader retHeader = {};
			LoginResult loginRet = {};
			recv(_sock, (char *)&retHeader, sizeof(retHeader), 0);
			recv(_sock, (char *)&loginRet, sizeof(LoginResult), 0);
			cout << "LoginResult:" << loginRet.result << endl;
		}
		else if (0 == strcmp(cmdBuf, "logout")){
			Logout logout = { "lyb" };
			DataHeader dh = { sizeof(Login), CMD_LOGOUT };
			//5,向服务器发送请求
			send(_sock, (const char *)&dh, sizeof(DataHeader), 0);
			send(_sock, (const char *)&logout, sizeof(Logout), 0);
			//接收服务器返回数据
			DataHeader retHeader = {};
			LogoutResult logoutRet = {};
			recv(_sock, (char *)&retHeader, sizeof(retHeader), 0);
			recv(_sock, (char *)&logoutRet, sizeof(LogoutResult), 0);
			cout << "LogoutResult:" << logoutRet.result << endl;
		}
		else{
			cout << "不支持的命令,请重新输入" << endl;
		}
	}
	//7,关闭套接字closesocket
	closesocket(_sock);
	//-----------------------------------
	//清除Windows socket环境
	WSACleanup();
	system("pause");
	return 0;
}

第三に、主にエラーを回避するために構造を統合するために、複数回送信されるパケットデータを1つの送信と受信にアップグレードします。(データオフセットが必要です)

上記の方法では、転送された構造の機能と記述データ(ヘッダーと本文)が分離されているため、エラーが発生しやすいため、この時点で2つを組み合わせます。

スクリーンショットを実行します。

サーバーコード:

#define WIN32_LEAN_AND_MEAN
#include<iostream>
#include<windows.h>
#include<Winsock2.h>

using namespace std;

enum CMD
{
	CMD_LOGIN,       //登入
	CMD_LOGIN_RESULT,
	CMD_LOGOUT,      //登出
	CMD_LOGOUT_RESULT,
	CMD_ERROR,       //错误
};

struct DataHeader
{
	short dataLength;
	short cmd;
};

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

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

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;
};

int main()
{
	//启动Windows socket 2.x环境
	WORD ver = MAKEWORD(2, 2);
	WSADATA dat;
	WSAStartup(ver, &dat);
	//---------------------------------
	//1,建立一个socket
	SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	//2,bind 绑定用于接受客户端连接的网络接口
	sockaddr_in _sin = {};
	_sin.sin_family = AF_INET;
	_sin.sin_port = htons(4567);
	_sin.sin_addr.S_un.S_addr = INADDR_ANY;
	if (SOCKET_ERROR == bind(_sock, (sockaddr*)&_sin, sizeof(_sin)))
	{
		cout << "错误,绑定网络端口失败" << endl;
	}
	else
	{
		cout << "绑定网络端口成功" << endl;
	}

	//3,listen 监听网络端口
	if (SOCKET_ERROR == listen(_sock, 5))
	{
		cout << "错误,监听网络端口失败" << endl;
	}
	else
	{
		cout << "监听网络端口成功" << endl;
	}

	//4,accept 等待客户端连接
	sockaddr_in clientAddr;
	int nAddrlen = sizeof(clientAddr);
	SOCKET _cSock = INVALID_SOCKET;

	_cSock = accept(_sock, (sockaddr *)&clientAddr, &nAddrlen);
	if (INVALID_SOCKET == _cSock)
	{
		cout << "错误,接受到无效的客户端连接" << endl;
	}

	cout << "新的客户端加入:" << inet_ntoa(clientAddr.sin_addr) << endl;

	while (true)
	{
		DataHeader header = {};
		int nLen = recv(_cSock, (char*)&header, sizeof(DataHeader), 0);
		//5,接受客户端的请求数据
		if (nLen <= 0)
		{
			cout << "客户端已经退出,任务结束" << endl;
			break;
		}

		switch (header.cmd)
		{
		case CMD_LOGIN:
		{
						  Login login = {};
						  //做数据偏移
						  recv(_cSock, (char*)&login+sizeof(DataHeader), sizeof(Login)-sizeof(DataHeader), 0);
						  cout << "收到命令:CMD_LOGIN,  数据长度:" << login.dataLength;
						  cout << "  UserName:" << login.userName<<"  PassWord:"<<login.PassWord<< endl;
						  //忽略判断用户名密码是否正确的过程
						  LoginResult ret;
						  send(_cSock, (const char*)&ret, sizeof(LoginResult), 0);
		}
			break;
		case CMD_LOGOUT:
		{
						   Logout logout = {};
						   recv(_cSock, (char*)&logout + sizeof(DataHeader), sizeof(Logout)-sizeof(DataHeader), 0);
						   cout << "收到命令:CMD_LOGIN,  数据长度:" << logout.dataLength;
						   cout << "  UserName:" << logout.userName <<endl;
						   //忽略判断用户名密码是否正确的过程
						   LogoutResult ret;
						   send(_cSock, (const char*)&ret, sizeof(Logout), 0);
		}
			break;
		default:
			header.cmd = CMD_ERROR;
			header.dataLength = 0;
			send(_cSock, (const char*)&header, sizeof(DataHeader), 0);
			break;
		}
	}

	//8,关闭套接字closesocket
	closesocket(_sock);

	//-----------------------------------
	//清除Windows socket环境
	WSACleanup();
	system("pause");
	return 0;
}

クライアントコード:

#define WIN32_LEAN_AND_MEAN
#include<iostream>
#include<windows.h>
#include<Winsock2.h>

using namespace std;

enum CMD
{
	CMD_LOGIN,       //登入
	CMD_LOGIN_RESULT,
	CMD_LOGOUT,      //登出
	CMD_LOGOUT_RESULT,
	CMD_ERROR,       //错误
};

struct DataHeader
{
	short dataLength;
	short cmd;
};

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

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

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;
};

int main()
{
	//启动Windows socket 2.x环境
	WORD ver = MAKEWORD(2, 2);
	WSADATA dat;
	WSAStartup(ver, &dat);
	//---------------------------------
	//1,用Socket API建立建立TCP客户端
	SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	//2,连接服务器 connect
	sockaddr_in _sin = {};
	_sin.sin_family = AF_INET;
	_sin.sin_port = htons(4567);
	_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	int ret = connect(_sock, (sockaddr*)&_sin, sizeof(_sin));
	if (SOCKET_ERROR == ret)
	{
		cout << "错误,建立Socket失败" << endl;
	}
	else
	{
		cout << "建立Socket成功" << endl;
	}

	while (true)
	{
		//3,输入请求
		char cmdBuf[128] = {};
		cin >> cmdBuf;
		//4,处理请求
		if (0 == strcmp(cmdBuf, "exit")){
			break;
		}
		else if (0 == strcmp(cmdBuf, "login")){
			Login login;
			strcpy(login.userName, "lyd");
			strcpy(login.PassWord, "lydmima");
			
			//5,向服务器发送请求
			send(_sock, (const char *)&login, sizeof(Login), 0);

			//接收服务器返回数据
			LoginResult loginRet = {};
			recv(_sock, (char *)&loginRet, sizeof(LoginResult), 0);
			cout << "LoginResult:" << loginRet.result << endl;
		}
		else if (0 == strcmp(cmdBuf, "logout")){
			Logout logout;
			strcpy(logout.userName, "lyb");

			//5,向服务器发送请求
			send(_sock, (const char *)&logout, sizeof(Logout), 0);

			//接收服务器返回数据
			LogoutResult logoutRet = {};
			recv(_sock, (char *)&logoutRet, sizeof(LogoutResult), 0);
			cout << "LogoutResult:" << logoutRet.result << endl;
		}
		else{
			cout << "不支持的命令,请重新输入" << endl;
		}
	}
	//7,关闭套接字closesocket
	closesocket(_sock);
	//-----------------------------------
	//清除Windows socket环境
	WSACleanup();
	system("pause");
	return 0;
}

第四に、受信バッファを追加します。

ここで受信バッファを追加する機能は、同時実行性が高い場合、送信データが大きすぎるとすべてを受信できない可能性があるため、現時点ではバッファを使用して受信することです。

クライアントは変更されません。

サーバ:

#define WIN32_LEAN_AND_MEAN
#include<iostream>
#include<windows.h>
#include<Winsock2.h>

using namespace std;

enum CMD
{
	CMD_LOGIN,       //登入
	CMD_LOGIN_RESULT,
	CMD_LOGOUT,      //登出
	CMD_LOGOUT_RESULT,
	CMD_ERROR,       //错误
};

struct DataHeader
{
	short dataLength;
	short cmd;
};

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

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

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;
};

int main()
{
	//启动Windows socket 2.x环境
	WORD ver = MAKEWORD(2, 2);
	WSADATA dat;
	WSAStartup(ver, &dat);
	//---------------------------------
	//1,建立一个socket
	SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	//2,bind 绑定用于接受客户端连接的网络接口
	sockaddr_in _sin = {};
	_sin.sin_family = AF_INET;
	_sin.sin_port = htons(4567);
	_sin.sin_addr.S_un.S_addr = INADDR_ANY;
	if (SOCKET_ERROR == bind(_sock, (sockaddr*)&_sin, sizeof(_sin)))
	{
		cout << "错误,绑定网络端口失败" << endl;
	}
	else
	{
		cout << "绑定网络端口成功" << endl;
	}

	//3,listen 监听网络端口
	if (SOCKET_ERROR == listen(_sock, 5))
	{
		cout << "错误,监听网络端口失败" << endl;
	}
	else
	{
		cout << "监听网络端口成功" << endl;
	}

	//4,accept 等待客户端连接
	sockaddr_in clientAddr;
	int nAddrlen = sizeof(clientAddr);
	SOCKET _cSock = INVALID_SOCKET;

	_cSock = accept(_sock, (sockaddr *)&clientAddr, &nAddrlen);
	if (INVALID_SOCKET == _cSock)
	{
		cout << "错误,接受到无效的客户端连接" << endl;
	}

	cout << "新的客户端加入:" << inet_ntoa(clientAddr.sin_addr) << endl;

	while (true)
	{
		//缓冲区
		char szRecv[1024] = {};
		//5,接受客户端的请求数据
		int nLen = recv(_cSock, (char*)&szRecv, sizeof(DataHeader), 0);
		DataHeader *header = (DataHeader*)szRecv;
		if (nLen <= 0)
		{
			cout << "客户端已经退出,任务结束" << endl;
			break;
		}

		switch (header->cmd)
		{
		case CMD_LOGIN:
		{
						  //做数据偏移
						  recv(_cSock, szRecv + sizeof(DataHeader), header->dataLength-sizeof(DataHeader), 0);
						  Login *login = (Login*)szRecv;
						  cout << "收到命令:CMD_LOGIN,  数据长度:" << login->dataLength;
						  cout << "  UserName:" << login->userName<<"  PassWord:"<<login->PassWord<< endl;
						  //忽略判断用户名密码是否正确的过程
						  LoginResult ret;
						  send(_cSock, (const char*)&ret, sizeof(LoginResult), 0);
		}
			break;
		case CMD_LOGOUT:
		{
						   recv(_cSock, szRecv + sizeof(DataHeader), header->dataLength-sizeof(DataHeader), 0);
						   Login *logout = (Login*)szRecv;
						   cout << "收到命令:CMD_LOGIN,  数据长度:" << logout->dataLength;
						   cout << "  UserName:" << logout->userName <<endl;
						   //忽略判断用户名密码是否正确的过程
						   LogoutResult ret;
						   send(_cSock, (const char*)&ret, sizeof(Logout), 0);
		}
			break;
		default:
		{
				   DataHeader header = { 0, CMD_ERROR };
				   send(_cSock, (const char*)&header, sizeof(DataHeader), 0);
		}
			break;
		}
	}

	//8,关闭套接字closesocket
	closesocket(_sock);

	//-----------------------------------
	//清除Windows socket环境
	WSACleanup();
	system("pause");
	return 0;
}

第5に、サーバー側を選択モデルに変更して、複数のクライアントを処理するという目標を達成します。

前のモデルを選択モデルに変えます。これにより、高い同時実行性とクロスプラットフォームを実現できます。

スクリーンショットを実行します。

クライアントは変更されません。

サービスターミナル:

#define WIN32_LEAN_AND_MEAN
#include<iostream>
#include<windows.h>
#include<Winsock2.h>

#include<vector>

using namespace std;

enum CMD
{
	CMD_LOGIN,       //登入
	CMD_LOGIN_RESULT,
	CMD_LOGOUT,      //登出
	CMD_LOGOUT_RESULT,
	CMD_ERROR,       //错误
};

struct DataHeader
{
	short dataLength;
	short cmd;
};

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

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

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;
};

vector<SOCKET> g_clients;

int processor(SOCKET _cSock)
{
	//缓冲区
	char szRecv[1024] = {};
	//5,接受客户端的请求数据
	int nLen = recv(_cSock, (char*)&szRecv, sizeof(DataHeader), 0);
	DataHeader *header = (DataHeader*)szRecv;
	if (nLen <= 0)
	{
		cout << "客户端已经退出,任务结束" << endl;
		return -1;
	}
	switch (header->cmd)
	{
	case CMD_LOGIN:
	{
					  //做数据偏移
					  recv(_cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);
					  Login *login = (Login*)szRecv;
					  cout << "收到命令:CMD_LOGIN,  数据长度:" << login->dataLength;
					  cout << "  UserName:" << login->userName << "  PassWord:" << login->PassWord << endl;
					  //忽略判断用户名密码是否正确的过程
					  LoginResult ret;
					  send(_cSock, (const char*)&ret, sizeof(LoginResult), 0);
	}
		break;
	case CMD_LOGOUT:
	{
					   recv(_cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);
					   Login *logout = (Login*)szRecv;
					   cout << "收到命令:CMD_LOGIN,  数据长度:" << logout->dataLength;
					   cout << "  UserName:" << logout->userName << endl;
					   //忽略判断用户名密码是否正确的过程
					   LogoutResult ret;
					   send(_cSock, (const char*)&ret, sizeof(Logout), 0);
	}
		break;
	default:
	{
			   DataHeader header = { 0, CMD_ERROR };
			   send(_cSock, (const char*)&header, sizeof(DataHeader), 0);
	}
		break;
	}
}

int main()
{
	//启动Windows socket 2.x环境
	WORD ver = MAKEWORD(2, 2);
	WSADATA dat;
	WSAStartup(ver, &dat);
	//---------------------------------
	//1,建立一个socket
	SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	//2,bind 绑定用于接受客户端连接的网络接口
	sockaddr_in _sin = {};
	_sin.sin_family = AF_INET;
	_sin.sin_port = htons(4567);
	_sin.sin_addr.S_un.S_addr = INADDR_ANY;
	if (SOCKET_ERROR == bind(_sock, (sockaddr*)&_sin, sizeof(_sin)))
	{
		cout << "错误,绑定网络端口失败" << endl;
	}
	else
	{
		cout << "绑定网络端口成功" << endl;
	}

	//3,listen 监听网络端口
	if (SOCKET_ERROR == listen(_sock, 5))
	{
		cout << "错误,监听网络端口失败" << endl;
	}
	else
	{
		cout << "监听网络端口成功" << endl;
	}

	while (true)
	{
		//伯克利套接字
		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);

		for (int n = (int)g_clients.size() - 1; n >= 0; n--)
		{
			FD_SET(g_clients[n],&fdRead);
		}

		//nfds 是一个整数值,是指fd_set集合中所有描述符(socket)的范围,而不是数量
		//即是所有文件描述符最大值+1,在Windows中这个参数可以写0
		int ret = select(_sock + 1, &fdRead, &fdWrite, &fdExp, NULL);
		if (ret < 0)
		{
			cout << "select任务结束" << endl;
			break;
		}

		if (FD_ISSET(_sock, &fdRead))
		{
			FD_CLR(_sock, &fdRead);

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

			_cSock = accept(_sock, (sockaddr *)&clientAddr, &nAddrlen);
			if (INVALID_SOCKET == _cSock)
			{
				cout << "错误,接受到无效的客户端连接" << endl;
			}

			g_clients.push_back(_cSock);
			cout << "新的客户端加入:"<<(int)_cSock<<"        "<< inet_ntoa(clientAddr.sin_addr) << endl;
		}

		for (size_t n = 0; n < fdRead.fd_count; n++)
		{
			if (processor(fdRead.fd_array[n]) == -1)
			{
				auto iter = find(g_clients.begin(), g_clients.end(), fdRead.fd_array[n]);
				if (iter != g_clients.end())
				{
					g_clients.erase(iter);
				}
			}
		}
	}

	for (size_t n = g_clients.size() - 1; n >= 0; n--)
	{
		closesocket(g_clients[n]);
	}

	//8,关闭套接字closesocket
	closesocket(_sock);

	//-----------------------------------
	//清除Windows socket环境
	WSACleanup();
	system("pause");
	return 0;
}

第6に、サーバーは選択処理マルチクライアントモデルにアップグレードされ、クライアントが参加したときに接続されたクライアントに警告できます。

新しい機能を追加します。クライアントがクライアントに接続すると、サーバーは接続されたクライアントをクライアントに送信し、チャットシステムで誰かがオンラインになったときのリマインダー機能をシミュレートします。

実行中のプログラムのスクリーンショット:

この場所でまだcin送信が使用されている場合、cin関数によってブロッキングが発生し、その結果、接続されたクライアントに、新しいクライアントが参加したことを通知できなくなります。

したがって、この場所では、クライアントとサーバーが自動的にメッセージを送受信するため、常にメッセージを送信し、新しいクライアント接続の受信をシミュレートできます。

サービスターミナル:

#define WIN32_LEAN_AND_MEAN
#include<iostream>
#include<windows.h>
#include<Winsock2.h>

#include<vector>

using namespace std;

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

struct DataHeader
{
	short dataLength;
	short cmd;
};

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

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

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;
};

vector<SOCKET> g_clients;

int processor(SOCKET _cSock)
{
	//缓冲区
	char szRecv[1024] = {};
	//5,接受客户端的请求数据
	int nLen = recv(_cSock, (char*)&szRecv, sizeof(DataHeader), 0);
	DataHeader *header = (DataHeader*)szRecv;
	if (nLen <= 0)
	{
		cout << "客户端已经退出,任务结束" << endl;
		return -1;
	}
	switch (header->cmd)
	{
	case CMD_LOGIN:
	{
					  //做数据偏移
					  recv(_cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);
					  Login *login = (Login*)szRecv;
					  cout << "收到命令:CMD_LOGIN,  数据长度:" << login->dataLength;
					  cout << "  UserName:" << login->userName << "  PassWord:" << login->PassWord << endl;
					  //忽略判断用户名密码是否正确的过程
					  LoginResult ret;
					  send(_cSock, (const char*)&ret, sizeof(LoginResult), 0);
	}
		break;
	case CMD_LOGOUT:
	{
					   recv(_cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);
					   Login *logout = (Login*)szRecv;
					   cout << "收到命令:CMD_LOGIN,  数据长度:" << logout->dataLength;
					   cout << "  UserName:" << logout->userName << endl;
					   //忽略判断用户名密码是否正确的过程
					   LogoutResult ret;
					   send(_cSock, (const char*)&ret, sizeof(Logout), 0);
	}
		break;
	default:
	{
			   DataHeader header = { 0, CMD_ERROR };
			   send(_cSock, (const char*)&header, sizeof(DataHeader), 0);
	}
		break;
	}
}

int main()
{
	//启动Windows socket 2.x环境
	WORD ver = MAKEWORD(2, 2);
	WSADATA dat;
	WSAStartup(ver, &dat);
	//---------------------------------
	//1,建立一个socket
	SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	//2,bind 绑定用于接受客户端连接的网络接口
	sockaddr_in _sin = {};
	_sin.sin_family = AF_INET;
	_sin.sin_port = htons(4567);
	_sin.sin_addr.S_un.S_addr = INADDR_ANY;
	if (SOCKET_ERROR == bind(_sock, (sockaddr*)&_sin, sizeof(_sin)))
	{
		cout << "错误,绑定网络端口失败" << endl;
	}
	else
	{
		cout << "绑定网络端口成功" << endl;
	}

	//3,listen 监听网络端口
	if (SOCKET_ERROR == listen(_sock, 5))
	{
		cout << "错误,监听网络端口失败" << endl;
	}
	else
	{
		cout << "监听网络端口成功" << endl;
	}

	while (true)
	{
		//伯克利套接字
		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);

		for (int n = (int)g_clients.size() - 1; n >= 0; n--)
		{
			FD_SET(g_clients[n], &fdRead);
		}

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

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

		int ret = select(_sock + 1, &fdRead, &fdWrite, &fdExp, NULL);

		if (ret < 0)
		{
			cout << "select任务结束" << endl;
			break;
		}

		if (FD_ISSET(_sock, &fdRead))
		{
			FD_CLR(_sock, &fdRead);

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

			_cSock = accept(_sock, (sockaddr *)&clientAddr, &nAddrlen);
			if (INVALID_SOCKET == _cSock)
			{
				cout << "错误,接受到无效的客户端连接" << endl;
			}

			for (int n = (int)g_clients.size() - 1; n >= 0; n--)
			{
				NewUserJoin userjoin;
				send(g_clients[n], (const char*)&userjoin, sizeof(NewUserJoin), 0);
			}

			g_clients.push_back(_cSock);
			cout << "新的客户端加入:" << (int)_cSock << "        " << inet_ntoa(clientAddr.sin_addr) << endl;
		}

		for (size_t n = 0; n < fdRead.fd_count; n++)
		{
			if (processor(fdRead.fd_array[n]) == -1)
			{
				auto iter = find(g_clients.begin(), g_clients.end(), fdRead.fd_array[n]);
				if (iter != g_clients.end())
				{
					g_clients.erase(iter);
				}
			}
		}
	}

	for (size_t n = g_clients.size() - 1; n >= 0; n--)
	{
		closesocket(g_clients[n]);
	}

	//8,关闭套接字closesocket
	closesocket(_sock);

	//-----------------------------------
	//清除Windows socket环境
	WSACleanup();
	system("pause");
	return 0;
}

クライアント:

#define WIN32_LEAN_AND_MEAN
#include<iostream>
#include<windows.h>
#include<Winsock2.h>

using namespace std;

enum CMD
{
	CMD_LOGIN,       //登入
	CMD_LOGIN_RESULT,
	CMD_LOGOUT,      //登出
	CMD_LOGOUT_RESULT,
	CMD_NEW_USER_JOIN,
	CMD_ERROR,       //错误
};

struct DataHeader
{
	short dataLength;
	short cmd;
};

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

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

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;
};


int processor(SOCKET _cSock)
{
	//缓冲区
	char szRecv[1024] = {};
	//5,接受客户端的请求数据
	int nLen = recv(_cSock, (char*)&szRecv, sizeof(DataHeader), 0);
	DataHeader *header = (DataHeader*)szRecv;
	if (nLen <= 0)
	{
		cout << "与服务器断开连接,任务结束" << endl;
		return -1;
	}
	switch (header->cmd)
	{
	case CMD_LOGIN_RESULT:
	{
							 recv(_cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);
							 LoginResult *login = (LoginResult*)szRecv;
							 cout << "收到服务端消息:CMD_LOGIN_RESULT  " << _cSock << "  数据长度:" << login->dataLength << endl;
	}
		break;
	case CMD_LOGOUT_RESULT:
	{
							  recv(_cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);
							  LogoutResult *logout = (LogoutResult*)szRecv;
							  cout << "收到服务端消息:CMD_LOGOUT_RESULT  " << _cSock << "  数据长度:" << logout->dataLength << endl;
	}
		break;
	case CMD_NEW_USER_JOIN:
	{
							  recv(_cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);
							  NewUserJoin *userJoin = (NewUserJoin*)szRecv;
							  cout << "收到服务端消息:CMD_NEW_USER_JOIN  " << _cSock << "  数据长度:" << userJoin->dataLength << endl;
	}
		break;
	}
}

int main()
{
	//启动Windows socket 2.x环境
	WORD ver = MAKEWORD(2, 2);
	WSADATA dat;
	WSAStartup(ver, &dat);
	//---------------------------------
	//1,用Socket API建立建立TCP客户端
	SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	//2,连接服务器 connect
	sockaddr_in _sin = {};
	_sin.sin_family = AF_INET;
	_sin.sin_port = htons(4567);
	_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	int ret = connect(_sock, (sockaddr*)&_sin, sizeof(_sin));
	if (SOCKET_ERROR == ret)
	{
		cout << "错误,建立Socket失败" << endl;
	}
	else
	{
		cout << "建立Socket成功" << endl;
	}

	while (true)
	{
		fd_set fdReads;
		FD_ZERO(&fdReads);
		FD_SET(_sock, &fdReads);

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

		int ret = select(_sock, &fdReads, 0, 0, NULL);
		if (ret < 0)
		{
			cout << "select 任务结束1" << endl;
			break;
		}
		if (FD_ISSET(_sock, &fdReads))
		{
			FD_CLR(_sock, &fdReads);

			if (-1 == processor(_sock))
			{
				cout << "select 任务结束2" << endl;
				break;
			}
		}

		Login login;
		strcpy(login.userName, "lyd");
		strcpy(login.PassWord, "lydmima");

		//5,向服务器发送请求
		send(_sock, (const char *)&login, sizeof(Login), 0);
		Sleep(1000);
	}

	//7,关闭套接字closesocket
	closesocket(_sock);
	//-----------------------------------
	//清除Windows socket环境
	WSACleanup();
	cout << "已退出" << endl;
	system("pause");
	return 0;
}

 

おすすめ

転載: blog.csdn.net/qq_46423166/article/details/110294956