最近在写WebSocket的服务器,打算同时做基于websocket的游戏及游戏服务器。研究了一下WebSocket协议,然后基于epoll写了一个websocket的服务器,可支持超大并发量^_^,这儿向大家提供数据包解析和创建的函数,供大家学习参考。
功能参考RFC 6455 ,目前最新的websocket v13版本的协议写成。支持mask和非mask两种方式,支持解析和创建数据长度在6K以下的WebSocket数据包。
这儿只是个解析示例,现实的网络开发中应使用流解析方式生成,流方式的实现即通过向一个状态机添加缓冲区,当状态机组合出一个包头时回调。
本文地址: http://www.hoverlees.com/blog/?p=1395
//websocket.h /** * WebSocket数据包解析和生成函数,RFC 6455 WebSocket V13 标准 * @author Hoverlees http://www.hoverlees.com */ #ifndef __HOVERLEES_WEBSOCKET_H #define __HOVERLEES_WEBSOCKET_H #define OPCODE_CONTINUATION 0 #define OPCODE_TEXT 1 #define OPCODE_BINARY 2 #define OPCODE_CLOSE 8 #define OPCODE_PING 9 #define OPCODE_PONG 10 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;i<packet->data_len;i++){ j=i%4; packet->data[i]=mem[pos+i]^packet->mask_key[j]; } } else{ for(i=0;i<packet->data_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<len;i++){ j=i%4; packet->data[pos+i]=data[i]^packet->mask_key[j]; } } else{ for(i=0;i<len;i++){ packet->data[pos+i]=data[i]; } } pos+=len; return pos; }
websocket体验:
基于websocket的火拼俄罗斯
非常不错,谢谢!