自己实现的WebSocket服务器数据包处理函数

最近在写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的火拼俄罗斯

Join the Conversation

1 Comment

Leave a Reply to analyzer

Your email address will not be published. Required fields are marked *