websocket底层处理粘包_Websocket消息帧粘包,拆包及处理方法

本文介绍了WebSocket协议在处理粘包、拆包问题时的方法,详细讲解了如何构建和解析WebSocket消息帧。通过示例代码展示了如何实现连接服务器、心跳检测、数据发送和接收,并提供了相关函数如`webSocket_send`和`webSocket_recv`的实现。内容包括基础的网络控制、数据编码和解码,以及基于套接字的通信技巧。
摘要由CSDN通过智能技术生成

#include "websocket_common.h"

#include

#include

#include // 使用 malloc, calloc等动态分配内存方法

#include // 获取系统时间

#include

#include // 非阻塞

#include

#include // inet_addr()

#include // close()

#include // 文件IO操作

#include //

#include

#include

#include

#include // gethostbyname, gethostbyname2, gethostbyname_r, gethostbyname_r2

#include

#include

#include

#include

#include // SIOCSIFADDR

//==============================================================================================

//======================================== 设置和工具部分 =======================================

//==============================================================================================

// 连接服务器

#define WEBSOCKET_LOGIN_CONNECT_TIMEOUT 1000 // 登录连接超时设置 1000ms

#define WEBSOCKET_LOGIN_RESPOND_TIMEOUT (1000 + WEBSOCKET_LOGIN_CONNECT_TIMEOUT) // 登录等待回应超时设置 1000ms

// 发收

// 生成握手key的长度

#define WEBSOCKET_SHAKE_KEY_LEN 16

//==================== delay ms ====================

void webSocket_delayms(unsigned int ms)

{

struct timeval tim;

tim.tv_sec = ms/1000;

tim.tv_usec = (ms%1000)*1000;

select(0, NULL, NULL, NULL, &tim);

}

//-------------------- IP控制 --------------------

int netCheck_setIP(char *devName, char *ip)

{

struct ifreq temp;

struct sockaddr_in *addr;

int fd, ret;

//

strcpy(temp.ifr_name, devName);

if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)

return -1;

//

addr = (struct sockaddr_in *)&(temp.ifr_addr);

addr->sin_family = AF_INET;

addr->sin_addr.s_addr = inet_addr(ip);

ret = ioctl(fd, SIOCSIFADDR, &temp);

//

close(fd);

if(ret < 0)

return -1;

return 0;

}

void netCheck_getIP(char *devName, char *ip)

{

struct ifreq temp;

struct sockaddr_in *addr;

int fd, ret;

//

strcpy(temp.ifr_name, devName);

if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)

return;

ret = ioctl(fd, SIOCGIFADDR, &temp);

close(fd);

if(ret < 0)

return;

//

addr = (struct sockaddr_in *)&(temp.ifr_addr);

strcpy(ip, inet_ntoa(addr->sin_addr));

//

// return ip;

}

//==================== 域名转IP ====================

typedef struct{

pthread_t thread_id;

char ip[256];

bool result;

bool actionEnd;

}GetHostName_Struct;

//

void *websocket_getHost_fun(void *arge)

{

int ret;

//int i;

char buf[1024];

struct hostent host_body, *host = NULL;

struct in_addr **addr_list;

GetHostName_Struct *gs = (GetHostName_Struct *)arge;

/* 此类方法不可重入! 即使关闭线程

if((host = gethostbyname(gs->ip)) == NULL)

//if((host = gethostbyname2(gs->ip, AF_INET)) == NULL)

{

gs->actionEnd = true;

return NULL;

}*/

if(gethostbyname_r(gs->ip, &host_body, buf, sizeof(buf), &host, &ret))

{

gs->actionEnd = true;

return NULL;

}

if(host == NULL)

{

gs->actionEnd = true;

return NULL;

}

addr_list = (struct in_addr **)host->h_addr_list;

//printf("ip name : %s\r\nip list : ", host->h_name);

//for(i = 0; addr_list[i] != NULL; i++) printf("%s, ", inet_ntoa(*addr_list[i])); printf("\r\n");

if(addr_list[0] == NULL)

{

gs->actionEnd = true;

return NULL;

}

memset(gs->ip, 0, sizeof(gs->ip));

strcpy(gs->ip, (char *)(inet_ntoa(*addr_list[0])));

gs->result = true;

gs->actionEnd = true;

return NULL;

}

//

int websocket_getIpByHostName(char *hostName, char *backIp)

{

int i, timeOut = 1;

GetHostName_Struct gs;

if(hostName == NULL)

return -1;

else if(strlen(hostName) < 1)

return -1;

//----- 开线程从域名获取IP -----

memset(&gs, 0, sizeof(GetHostName_Struct));

strcpy(gs.ip, hostName);

gs.result = false;

gs.actionEnd = false;

if (pthread_create(&gs.thread_id, NULL, (void *)websocket_getHost_fun, &gs) < 0)

return -1;

i = 0;

while(!gs.actionEnd)

{

if(++i > 10)

{

i = 0;

if(++timeOut > 1000)

break;

}

webSocket_delayms(1000);// 1ms延时

}

// pthread_cancel(gs.thread_id);

pthread_join(gs.thread_id, NULL);

if(!gs.result)

return -timeOut;

//----- 开线程从域名获取IP -----

memset(backIp, 0, strlen(backIp));

strcpy(backIp, gs.ip);

return timeOut;

}

//==================== 加密方法BASE64 ====================

//base64编/解码用的基础字符集

const char websocket_base64char[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

/*******************************************************************************

* 名称: websocket_base64_encode

* 功能: ascii编码为base64格式

* 形参: bindata : ascii字符串输入

* base64 : base64字符串输出

* binlength : bindata的长度

* 返回: base64字符串长度

* 说明: 无

******************************************************************************/

int websocket_base64_encode( const unsigned char *bindata, char *base64, int binlength)

{

int i, j;

unsigned char current;

for ( i = 0, j = 0 ; i < binlength ; i += 3 )

{

current = (bindata[i] >> 2) ;

current &= (unsigned char)0x3F;

base64[j++] = websocket_base64char[(int)current];

current = ( (unsigned char)(bindata[i] << 4 ) ) & ( (unsigned char)0x30 ) ;

if ( i + 1 >= binlength )

{

base64[j++] = websocket_base64char[(int)current];

base64[j++] = '=';

base64[j++] = '=';

break;

}

current |= ( (unsigned char)(bindata[i+1] >> 4) ) & ( (unsigned char) 0x0F );

base64[j++] = websocket_base64char[(int)current];

current = ( (unsigned char)(bindata[i+1] << 2) ) & ( (unsigned char)0x3C ) ;

if ( i + 2 >= binlength )

{

base64[j++] = websocket_base64char[(int)current];

base64[j++] = '=';

break;

}

current |= ( (unsigned char)(bindata[i+2] >> 6) ) & ( (unsigned char) 0x03 );

base64[j++] = websocket_base64char[(int)current];

current = ( (unsigned char)bindata[i+2] ) & ( (unsigned char)0x3F ) ;

base64[j++] = websocket_base64char[(int)current];

}

base64[j] = '\0';

return j;

}

/*******************************************************************************

* 名称: websocket_base64_decode

* 功能: base64格式解码为ascii

* 形参: base64 : base64字符串输入

* bindata : ascii字符串输出

* 返回: 解码出来的ascii字符串长度

* 说明: 无

******************************************************************************/

int websocket_base64_decode( const char *base64, unsigned char *bindata)

{

int i, j;

unsigned char k;

unsigned char temp[4];

for ( i = 0, j = 0; base64[i] != '\0' ; i += 4 )

{

memset( temp, 0xFF, sizeof(temp) );

for ( k = 0 ; k < 64 ; k ++ )

{

if ( websocket_base64char[k] == base64[i] )

temp[0]= k;

}

for ( k = 0 ; k < 64 ; k ++ )

{

if ( websocket_base64char[k] == base64[i+1] )

temp[1]= k;

}

for ( k = 0 ; k < 64 ; k ++ )

{

if ( websocket_base64char[k] == base64[i+2] )

temp[2]= k;

}

for ( k = 0 ; k < 64 ; k ++ )

{

if ( websocket_base64char[k] == base64[i+3] )

temp[3]= k;

}

bindata[j++] = ((unsigned char)(((unsigned char)(temp[0] << 2))&0xFC)) | \

((unsigned char)((unsigned char)(temp[1]>>4)&0x03));

if ( base64[i+2] == '=' )

break;

bindata[j++] = ((unsigned char)(((unsigned char)(temp[1] << 4))&0xF0)) | \

((unsigned char)((unsigned char)(temp[2]>>2)&0x0F));

if ( base64[i+3] == '=' )

break;

bindata[j++] = ((unsigned char)(((unsigned char)(temp[2] << 6))&0xF0)) | \

((unsigned char)(temp[3]&0x3F));

}

return j;

}

//==================== 加密方法 sha1哈希 ====================

typedef struct SHA1Context{

unsigned Message_Digest[5];

unsigned Length_L

### 回答1: Unity是一款广泛使用的游戏引擎,它允许开发者将游戏导出为WebGL格式,从而可以通过浏览器进行游玩。而WebGL是一种基于JavaScript的API,可以在浏览器中渲染3D及2D图形。 WebSocket是一种在浏览器和服务器之间建立实时双向通信的协议。它使得网页可以实时地更新数据,而无需使用传统的HTTP轮询。 在使用Unity开发WebGL游戏时,可能会遇到WebSocket粘包的问题。这种情况通常发生在网络传输数据时,数据包没有被完整地传输到接收端,导致数据被拆分或者合并,从而影响游戏的表现。 为了解决WebSocket粘包的问题,可以通过以下几种方式: 1. 增加缓冲区大小:可以通过增加WebSocket的缓冲区大小来减少WebSocket粘包的发生率。 2. 将数据包分割成小块:可以将较大的数据包拆分成多个小块进行传输,这样可以减少数据包的大小,从而降低粘包的概率。 3. 使用特定的协议:开发者可以使用一些特定的协议或者框架,如protobuf或者MsgPack,这些协议可以将数据格式化,从而减少数据包的大小,同时也可以降低WebSocket粘包的概率。 综上所述,WebSocket粘包是Unity WebGL游戏开发的一个常见问题,但可以通过增加缓冲区大小、将数据包分割成小块或者使用特定的协议来解决。 ### 回答2: Unity WebGL是一种基于Web的游戏引擎,它使用WebGL技术使游戏能够在Web浏览器中运行。WebSocket是一种HTML5提供的网络通信协议,与HTTP不同的是,WebSocket建立的是一个持久化的连接,可以实现双向通信。 但是,在使用Unity WebGL和WebSocket进行通信时,可能会出现粘包现象。粘包指的是在传输过程中,多个数据包被合并成一个较大的数据包,而接收端却只能对这一个较大的数据包进行处理,导致数据解析错误或出现数据丢失。 粘包的出现是由于WebSocket是基于TCP协议的,而TCP协议具有流式传输的特点,即没有边界限制。因此,在发送端连续发送多个数据包时,它们可能会依次被组成一个大数据包发送至接收端,而接收端则需要进行额外的解析和处理,才能正确获取每个数据包的信息。 解决粘包问题需要在程序设计上进行优化,可以在发送端添加边界符或长度前缀来区分每个数据包,以便接收端能够正确读取每个数据包。此外,也可以通过加密、压缩等技术来减小数据包的大小,从而减少粘包情况的发生。最好的解决方法还是在网络通信的设计过程中对TCP协议有深入的理解并且进行有效优化。 总之,对于Unity WebGL和WebSocket的开发者,需要注意粘包问题的可能性,设计时应该尽可能避免此类问题的发生。 ### 回答3: Unity WebGl游戏在使用websocket时常常会出现粘包现象。websocket是一种双向通信的网络协议,与HTTP协议相比,具有更快的速度和更低的延迟,因此在实时性要求较高的游戏开发中得到了广泛应用。 WebSocket粘包是指两个或更多个数据包被粘成一个数据包,导致数据混乱或数据丢失的现象。在Unity WebGl中,Unity引擎使用websocket协议与服务器通信时,如果两个消息同时到达,则可能会发生粘包。这会导致接收方解析数据时出现意外的数据,并可能会导致游戏异常或崩溃。 为了解决这个问题,可以在每个消息之前添加一个消息头以标识消息的长度。这样一来,在接收websocket消息时,就可以首先读取消息头,得到消息的长度,然后按照消息长度读取数据,以避免粘包的问题。 另外,在编写游戏网络通信代码时,也需要注意一些细节问题,如尽量避免长时间等待服务器响应、适量分包发送数据等,这样可以有效降低websocket粘包发生的概率,提高游戏网络通信的效率和稳定性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值