最近在写WebSocket的服务器,打算同时做基于websocket的游戏及游戏服务器。研究了一下WebSocket协议,然后基于epoll写了一个websocket的服务器,可支持超大并发量^_^,这儿向大家提供数据包解析和创建的函数,供大家学习参考。
功能参考RFC 6455 ,目前最新的websocket v13版本的协议写成。支持mask和非mask两种方式,支持解析和创建数据长度在6K以下的WebSocket数据包。
这儿只是个解析示例,现实的网络开发中应使用流解析方式生成,流方式的实现即通过向一个状态机添加缓冲区,当状态机组合出一个包头时回调。
//websocket.h
/**
* WebSocket数据包解析和生成函数,RFC 6455 WebSocket V13 标准
* @author Hoverlees http://www.hoverlees.com
*/
#ifndef __HOVERLEES_WEBSOCKET_H
#define __HOVERLEES_WEBSOCKET_H
#define OPCODE_CONTINUATION0
#define OPCODE_TEXT1
#define OPCODE_BINARY2
#define OPCODE_CLOSE8
#define OPCODE_PING9
#define OPCODE_PONG10
typedef struct WebSocketPacket{
unsigned char fin;
unsigned char mask;
unsigned char opcode;
unsigned char mask_key[4];
int data_len;
unsigned char* data;
}WebSocketPacket;
int websocket_packet_parse(unsigned char* mem,int len,WebSocketPacket* packet,int data_len);
int websocket_packet_build(unsigned char* data,int len,WebSocketPacket* packet);
#endif
//websocket.c
/**
* 解析WebSocket数据包 ,参考RFC 6455 page27
* http://tools.ietf.org/html/rfc6455#page-27
* @param mem 数据包内存
* @param len 数据包长度
* @param packet 解析后的数据将存到这个对象中,请在传入packet之前初始化packet的data指针指向一块可用内存.
* @param data_len packet的内存块可接收的最大字节数
* @return 如果解析成功返回1,解析失败返回0
*/
int websocket_packet_parse(unsigned char* mem,int len,WebSocketPacket* packet,int data_len){
int i,j;
int pos;
pos=2;
i=mem[1]&127;
if(i==127){
//大于65536的数据包不适合用这种解析方式。应该使用流解析方式。
return 0;
}
else if(i==126){
i=mem[2]*256+mem[3];
pos=4;
}
if(i>data_len) return i-data_len;
packet->fin=(mem[0]&128) ? 1:0;
packet->opcode=mem[0]&0xf;
packet->mask=mem[1]&128;
packet->data_len=i;
if(packet->mask){
for(i=0;i<4;i++){
packet->mask_key[i]=mem[pos+i];
}
pos+=4;
for(i=0;idata_len;i++){
j=i%4;
packet->data[i]=mem[pos+i]^packet->mask_key[j];
}
}
else{
for(i=0;idata_len;i++){
packet->data[i]=mem[pos+i];
}
}
packet->data[i]=0;
return 1;
}
/**
* 创建数据包
* @param data 要发送的数据实体
* @param len 数据长度
* @param packet 数据包,生成的数据在数据包的data中,传入前请先初始化data指针以及需要的参数
* @return 最终生成的块大小
*/
int websocket_packet_build(unsigned char* data,int len,WebSocketPacket* packet){
int pos=0;
int i,j;
if(len>65528) return 0;
packet->data[0]=packet->fin? 128:0;
packet->data[0]|=packet->opcode;
if(len>=126){
packet->data[1]=126;
packet->data[2]=(len>>8)&0xFF;
packet->data[3]=len&0xFF;
pos=4;
}
else{
packet->data[1]=len&0xFF;
pos=2;
}
packet->mask=0; //为了支持稍老一点的浏览器
if(packet->mask){
packet->data[1]|=128;
for(i=0;i<4;i++){
packet->data[pos+i]=packet->mask_key[i];
}
pos+=4;
for(i=0;i
j=i%4;
packet->data[pos+i]=data[i]^packet->mask_key[j];
}
}
else{
for(i=0;i
packet->data[pos+i]=data[i];
}
}
pos+=len;
return pos;
}