C ++ 높은 동시성 네트워크 아키텍처 및 구현-3 부

목차

첫째, 정보의 단순한 구조적 전송을 실현하지만 그것이 구조인지 구별 할 수 없다.

둘째, 네트워크 데이터 메시지의 형식을 통해 정의 된 전송을 실현합니다.

셋째, 여러 번 전송 된 패킷 데이터를 하나의 송수신으로 업그레이드하여 주로 오류를 방지하기 위해 구조를 통합합니다. (데이터 오프셋이 필요합니다)

넷째, 수신 버퍼를 추가합니다.

다섯째, 여러 클라이언트 처리 목표를 달성하기 위해 서버 측을 선택 모델로 변경하십시오.

여섯째, 서버가 선택 처리 멀티 클라이언트 모델로 업그레이드되고 클라이언트가 참여할 때 연결된 클라이언트에게 경고 할 수 있습니다.


오늘의 작업이 실현되었습니다. 서버가 다중 클라이언트 모델을 처리하도록 선택하도록 업그레이드되고 클라이언트가 참여할 때 연결된 클라이언트에게 알릴 수 있습니다.

  1. 정보의 단순한 구조적 전송을 달성하지만 그것이 구조인지 구별 할 수 없습니다.
  2. 네트워크 데이터 메시지 형식을 통해 정의 전송을 실현합니다.
  3. 패킷 데이터의 다중 송수신을 하나의 송수신으로 업그레이드하고 주로 구조를 통합하여 오류를 방지하십시오. (데이터 오프셋이 필요합니다)
  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;
}

둘째, 네트워크 데이터 메시지의 형식을 통해 정의 된 전송을 실현합니다.

첫 번째부터는 구조 만 전송에 사용하면 오류가 발생하므로 네트워크 데이터 메시지 형식을 사용해야한다는 결론을 내릴 수 있습니다.

메시지는 네트워크 메시지의 기본 단위 인 헤더와 본문의 두 부분으로 구성됩니다.
헤더 : 메시지의 크기와 데이터의 역할을 설명합니다.
본문 : 데이터

따라서 이때 데이터
enum 의 역할을 설명하기 위해 공용체를 정의하십시오. enum 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;
}

셋째, 여러 번 전송 된 패킷 데이터를 하나의 송수신으로 업그레이드하여 주로 오류를 방지하기 위해 구조를 통합합니다. (데이터 오프셋이 필요합니다)

위의 방법에서는 전송 된 구조의 기능과 설명 데이터 (헤더와 본문)가 분리되어 오류가 발생하기 쉬우므로 이때 두 가지를 결합

스크린 샷 실행 :

서버 코드 :

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

다섯째, 여러 클라이언트 처리 목표를 달성하기 위해 서버 측을 선택 모델로 변경하십시오.

이전 모델을 선택 모델로 전환하여 높은 동시성과 교차 플랫폼을 달성 할 수 있습니다.

스크린 샷 실행 :

클라이언트는 변경되지 않습니다.

서비스 터미널 :

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

여섯째, 서버가 선택 처리 멀티 클라이언트 모델로 업그레이드되고 클라이언트가 참여할 때 연결된 클라이언트에게 경고 할 수 있습니다.

새로운 기능을 추가하면 클라이언트가 클라이언트에 연결되면 서버는 연결된 클라이언트를 여기에 보내 누군가가 채팅 시스템에서 온라인 상태가 될 때 알림 기능을 시뮬레이션합니다.

실행중인 프로그램의 스크린 샷 :

이 장소에서 여전히 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