c++千万级别高并发网络编程(三)

发送结构化网络消息

  • 为什么需要修正
    • 结构化消息
  • 新增内容
  • 服务器整体代码
  • 客户端整体代码
    • 几点说明

为什么需要修正

c++千万级别高并发网络编程(三)_第1张图片

结构化消息

这里是指使用结构体传输数据。
需要考虑字节序对齐,如果客户端和服务器编程语言相同,且在同一环境下,通常字节是相同的,但是可能存在字节长度不一致而导致消息传输与接收不一致的额情况发生。

新增内容

  • 利用结构体一次性传输结构化的消息。

服务器整体代码

#define WIN32_LEAN_AND_MEAN
#define SOCKET int  //VS2015已经定义好的,可以直接用
#include
#include
#include
#include
struct DataPackage  //以最简单的情形为例,可以依据实际工程需求做自定义定制。
{
      int age;
      char name[32];
};
int main()
{
  WORD ver = MAKEWORD(2,2);//调用API2代创建2.x版本
  WSADATA dat;
  WSAStartup(ver,&dat);//启动
  //创建套接字
  SOCKET  seradd =socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
  //bind
  SOCKADDR_IN _sin = {};	
  _sin .sin_family = AF_INET;
//sin_port和sin_addr都必须是网络字节序(NBO),一般可视化的数字都是主机字节序。所以需要转换host to net unsigned.数字可改 
  _sin .sin_port = htons(1234);
  _sin.sin_addr_s_un.S_addr = INADDR_ANY;//访问本机任何网络地址都可以,具体也可以改为本机的某个特定地址
  if (SOCKET_ERROR == bind(seradd , (sockaddr *) &_sin , sizeof(_sin)))
  {
  	std::cout<<"绑定失败"<<endl;
  }
  //监听
  if (SOCKET_ERROR == listen(seradd,5))
  {
  	std::cout<<"绑定失败"<<endl;
  }
  //接受
  sockaddr_in clentAddr = {};//不用赋值了,因为是监听到的,不是给定的
  int nAddrLen = sizeof(sockaddr_in);//给定长度
  SOCKET _cSock = INVAID_SOCKET;
  //发送内容需要预先说定
  char msgbuf[] = "XXXXXXX";
  _cSock = accept(seradd,(sockaddr *) &clentAddr,nAddrLen);
  if(_cSock = INVAID_SOCKET)
  {
  		std::cout<<"获取失败"<<endl;
  }
  char _recvBuf[128] = {};
  while(true)
  {
  	//先接受客户端数据(新增)
  	int nLen = recv(_cSock,_recvBuf,128,0);
  	 if(nLen <= 0)
  	 {
  	 		cout<<"客户端已经退出,任务结束"<<endl;
  	 }
  	 //新增:处理请求
  	 if(0 == strcmp(_recvBuf,"XXX"))
  	 {
  	 	DataPackage  msgBuf = {10,"Tom"};
  	 	send(_cSock,(char *)&msgBuf,sizeof(msgBuf),0);
  	 }
  	 else if(0 == strcmp(_recvBuf,"exit"))
  	 {
  	 		cout<<"客户端已退出"<<endl;
  	  }
  	 else
  	 {
  	 	send(_cSock,msgbuf,strlenf(msgbuf)+1,0);//发送msgbuf中的内容。如果长度确定则不应该每次都重新计算
  	 }
  	
  }
  closesocket(seradd);
  WSAcleanup();//关闭
  return 0;
}

客户端整体代码

#define WIN32_LEAN_AND_MEAN
#include
#include
#include
struct DataPackage  //以最简单的情形为例,可以依据实际工程需求做自定义定制。
{
 	    int age;
 	    char name[32];
};
int main()
{
    WORD ver = MAKEWORD(2,2);//调用API2代创建2.x版本
    WSADATA dat;
    WSAStartup(ver,&dat);//启动
    SOCKET  cliadd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    //bind
    SOCKADDR_IN _sin = {};	
    _sin .sin_family = AF_INET;
//sin_port和sin_addr都必须是网络字节序(NBO),一般可视化的数字都是主机字节序。所以需要转换host to net unsigned.数字可改 
	_sin .sin_port = htons(4567);//和拂去其不同
	_sin.sin_addr_s_un.S_addr = inet_addr("127.0.0.1");//绑定服务器地址
	if (SOCKET_ERROR == connect(cliadd, (sockaddr *) &_sin , sizeof(_sin)))
	{
		std::cout<<"连接失败"<<endl;
	}
	//新增,接收用户输入数据
	while(true)
	{
		char inputBuf[128] = {};
		cin>>inputBuf;
		//接收用户数据,如果是exit就退出
		if(0 == strcmp(inputBuf,"exit"))
		{
		    send(cliadd,inputBuf,sizeof(inputBuf)+1,0);
		    break;
		}
		else
		{
			send(cliadd,inputBuf,sizeof(inputBuf)+1,0);
		}
	    //接受数据
		char recvBuf[128] = {};//最多可以接受的字符
		int nLen = recv(cliadd,recvBuf,0);
		if(nLen > 0 )
		{
			DataPackage *recv = (DataPackage *)recvBuf;//有问题
			cout<<"接受到的数据:年龄="<<recv->age<<"姓名为:"<<recv->name<<endl;//有问题的
		}
   }
   closesocket(cliadd);
   WSAcleanup();//关闭
   return 0;
}

几点说明

DataPackage *recv = (DataPackage *)recvBuf;

这里直接使用强制类型转换是可能存在风险的。后面会考虑进一步采用更加安全的方法去做这件事情

DataPackage *recv = (DataPackage *)recvBuf;//有问题
cout<<"接受到的数据:年龄="<<recv->age<<"姓名为:"<<recv->name<<endl;//有问题的

如果接受的是正确数据,那么这里是没有问题的;但是一旦接受的数据格式与此不尽相同,可能会导致出现较大问题。需要在后续进一步进行修正。利用包头指定数据类型和数据长度。

你可能感兴趣的:(项目,网络编程)