C++ Socket网络编程1.2版本 多次收发报文升级为一次收发

在目前的版本中,将网络数据传输方式改进成为网络数据报文(结构体)的形式传输,但比较不合理的一点是,传输数据的过程中总是要先传输 包头,再传输包体。因此,本节将多次收发报文改进为一次收发报文。

再回顾一下上一节的关键内容:
1、定义了四个网络数据包结构体

//DataPackge
struct Login
{
	char userName[32];
	char Password[32];
};
struct Logout
{
	char userName[32];
};
struct LoginResult
{
	int result;			
};
struct LogoutResult
{
	int result;
};

2、定义了一个包头结构体 和 命令枚举

enum CMD
{
	CMD_LOGIN,
	CMD_LOGIN_RESULT,
	CMD_LOGINOUT,
	CMD_LOGOUT_RESULT,
	CMD_ERROR
};
//消息头
struct DataHeader
{
	short dataLength;    //数据长度 32767字节
	short cmd;
};

通过定义包头和包体结构体,那么每次传输数据均需要两次据收发传输工作。

接下来进行对数据包的改进:

1、首先将名命令枚举扩充完整 消息头结构不变

enum CMD
{
	CMD_LOGIN,
	CMD_LOGIN_RESULT,
	CMD_LOGINOUT,
	CMD_LOGOUT_RESULT,
	CMD_ERROR
};
//消息头
struct DataHeader
{
	short dataLength;    //数据长度 32767字节
	short cmd;
};

2、改变数据包DataPackage结构体,使它继承于消息头。这么做的优点是,不需要在每次定义数据包之前,还要定义一个消息头,也避免了产生不必要的错误。

struct Login: public DataHeader
{
	Login()
	{
		dataLength = sizeof(Login);
		cmd = CMD_LOGIN;
	}
	char userName[32];
	char Password[32];
};
struct Logout:public DataHeader
{
	Logout()
	{
		dataLength = sizeof(Logout);
		cmd = CMD_LOGINOUT;
	}
	char userName[32];
};
struct LoginResult :public DataHeader
{
	LoginResult()
	{
		dataLength = sizeof(LoginResult);
		cmd = CMD_LOGIN_RESULT;
	}
	int result;			
};
struct LogoutResult :public DataHeader
{
	LogoutResult()
	{
		dataLength = sizeof(LogoutResult);
		cmd = CMD_LOGOUT_RESULT;
	}
	int result;
};

3、修改服务端收发数据的逻辑

while (true)
	{
		DataHeader header = {};
		//5 首先接收数据包头
		int nlen = recv(_clientSock, (char*)&header, sizeof(DataHeader), 0); //经过改进 包头信息已经继承到了包体中 按照内存对齐 首先读取sizeof(DataHeader)的字节序列
		if (nlen <= 0)
		{
			//客户端退出
			cout << "客户端已退出,任务结束" << endl;
			break;
		}
		
		switch (header.cmd)
		{
			case CMD_LOGIN:
			{
				Login _login;
				recv(_clientSock, (char*)&_login + sizeof(DataHeader), sizeof(Login) - sizeof(DataHeader),0); //这里要注意 程序已经读取了Dataheader了,因此这里应该从_login + sizeof(DataHeader) 开始读取,读取的长度也需要剪掉包头的长度  实现传输的数据内存对齐
				cout << "收到命令: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;
				recv(_clientSock, (char*)&_logout + sizeof(DataHeader), sizeof(Logout) - sizeof(DataHeader), 0);
				cout << "收到命令: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;
		}
	}

改进客户端收发数据逻辑:


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

			//6. 接受服务器信息 recv
			LoginResult _lgRes;
			recv(_sock, (char*)&_lgRes, sizeof(LoginResult), 0);
			cout<<"LoginResult: " << _lgRes.result << endl;
		}
		else if (0 == strcmp(cmdBuf, "logout"))
		{
			Logout _logout;
			strcpy(_logout.userName, "Evila");
			// 5 向服务器发送请求命令 
			send(_sock, (const char*)&_logout, sizeof(Logout), 0);

			//6. 接受服务器信息 recv
			LogoutResult _lgRes;
			//返回数据
			recv(_sock, (char*)&_lgRes, sizeof(LogoutResult), 0);
			cout << "LogoutResult: " << _lgRes.result << endl;
		}
		else
		{
			cout << "不支持的命令,请重新输入。" << endl;
		}
	}

猜你喜欢

转载自blog.csdn.net/La745739773/article/details/89052288
今日推荐