C++ Socket网络编程1.2版本 发送结构化的网络消息数据和网络数据报文

目前的版本中,客户端和服务端的网络通信 实现了简单的逻辑:(1)客户端发送请求(字符串)到服务端 (2)服务端处理请求(字符串) (3)服务端返回处理结果(字符串)

本节没有改进客户端和服务端的业务逻辑,而是改进字符串的消息传递,构建结构化的网络消息,使网络传输功能更复杂。

在这里插入图片描述

**

使用结构体定义结构化的网络消息(1.2版本)

在这里插入图片描述
**
在客户端和服务端程序代码中定义结构体
//一定要保证服务端和客户端(操作系统)中 数据结构字节顺序和大小保证一致 内存对齐

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

在服务端中 更改 send语句

if (0 == strcmp(_recvBuf, "getInfo"))
		{
			DataPackage dp = {24,"Evila"};
			send(_clientSock, (const char*)&dp, sizeof(DataPackage), 0); 
		}

在客户端中 更改recv语句

		char recvBuf[256] = {};
		int nlen = recv(_sock, recvBuf, sizeof(recvBuf), 0); //返回值是接收数据的长度
		if (nlen > 0)
		{
			DataPackage* pDP = (DataPackage*)recvBuf;
			cout << "接收到数据: 年龄=" << pDP->age << ",名字=" << pDP->name << endl;
		}

在这里插入图片描述
可以看到,当客户端输入正确的请求时,服务端可以返回逻辑更清晰的结果。
但是当客户端输入的请求不正确时,这是服务端返回的结果是乱的。

网络数据报文

报文有两个部分,包头和包体,是网络消息的基本单元

包头:描述本次消息包的大小
包体:数据内容

传输数据时,客户端发送请求应先发送 包头 再发送包体; 服务器端接收请求时,先接收请求头,再接收请求体;同样的,服务器端返回时和客户端接收时。

扫描二维码关注公众号,回复: 5820344 查看本文章

在服务端和客户端中增加如下结构体:
缺点:分次发送包头和包体 下节进行优化

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

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

服务器端等待接收和返回更新代码:

while (true)
	{
		DataHeader header = {};
		//5 首先接收数据包头
		int nlen = recv(_clientSock, (char*)&header, sizeof(header), 0); //接受客户端的数据 第一个参数应该是客户端的socket对象
		if (nlen <= 0)
		{
			//客户端退出
			cout << "客户端已退出,任务结束" << endl;
			break;
		}
		cout << "收到命令:" << header.cmd << "数据长度" <<header.dataLength<< endl;

		switch (header.cmd)
		{
			case CMD_LOGIN:
			{
				Login _login = {};
				recv(_clientSock, (char*)&_login, sizeof(Login),0);
				//忽略判断用户名密码是否正确的过程
				LoginResult _loginres = { 1 };
				send(_clientSock, (char*)&header, sizeof(DataHeader), 0);
				send(_clientSock, (char*)&_loginres, sizeof(LoginResult), 0);
				cout << "登陆用户: " << _login.userName << endl;
			}break;
			case CMD_LOGINOUT:
			{
				Logout _logout = {};
				recv(_clientSock, (char*)&_logout, sizeof(Logout), 0);
				LogoutResult _logoutres = { 1 };
				send(_clientSock, (char*)&header, sizeof(DataHeader), 0);
				send(_clientSock, (char*)&_logoutres, sizeof(LogoutResult), 0);
				cout << "登出用户: " << _logout.userName << endl;
			}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 = {"Evila","Evila_Password"};
			DataHeader _header = {};
			_header.dataLength = sizeof(Login);
			_header.cmd = CMD_LOGIN;
			// 5 向服务器发送请求命令 先发送包头
			send(_sock,(const char*)&_header, sizeof(DataHeader), 0);
			//再发送包体
			send(_sock, (const char*)&_login, sizeof(Login), 0);

			//6. 接受服务器信息 recv
			//先接收 返回数据的包头
			DataHeader returnHeader = {};
			LoginResult _lgRes = {};
			recv(_sock, (char*)&returnHeader, sizeof(DataHeader), 0);
			//再接收 返回数据的包体
			recv(_sock, (char*)&_lgRes, sizeof(LoginResult), 0);
			cout<<"LoginResult: " << _lgRes.result << endl;
		}
		else if (0 == strcmp(cmdBuf, "logout"))
		{
			Logout _logout = {"Evila"};
			DataHeader _header = {};
			_header.dataLength = sizeof(Logout);
			_header.cmd = CMD_LOGINOUT;
			// 5 向服务器发送请求命令 先发送包头
			send(_sock, (const char*)&_header, sizeof(DataHeader), 0);
			//再发送包体
			send(_sock, (const char*)&_logout, sizeof(Logout), 0);

			//6. 接受服务器信息 recv
			//先接收 返回数据的包头
			DataHeader returnHeader = {};
			LogoutResult _lgRes = {};
			recv(_sock, (char*)&returnHeader, sizeof(DataHeader), 0);
			//再接收 返回数据的包体
			recv(_sock, (char*)&_lgRes, sizeof(LogoutResult), 0);
			cout << "LogoutResult: " << _lgRes.result << endl;
		}
		else
		{
			cout << "不支持的命令,请重新输入。" << endl;
		}
		//放到循环中 重复接受服务器的返回信息
		//6. 接受服务器信息 recv
		//char recvBuf[256] = {};
		//int nlen = recv(_sock, recvBuf, sizeof(recvBuf), 0); //返回值是接收数据的长度
		//if (nlen > 0)
		//{
		//	DataPackage* pDP = (DataPackage*)recvBuf;
		//	cout << "接收到数据: 年龄=" << pDP->age << ",名字=" << pDP->name << endl;
		//}
		//else
		//{
		//	cout << "ERROR: 数据传输失败..." << endl;
		//}
	}

猜你喜欢

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