02
Dec
December 2, 2012 分类: ASM/C/C++     作者: hoverlees     留言: 12

经过本人对WebSocket协议的研究和WebSocket服务器的编写,对WebSocket有了深入的了解,在此写个总结供以后研究参考。

本文地址:http://www.hoverlees.com/blog/?p=1413

1.概要

WebSocket是最新提出用于实现服务器与浏览器双向通信的一种解决方案,用于取代一些传统的数据推送方案(如iframe长连接,ajax轮询等)。该方案由于一直在草案阶段,最新的版本为version 13.该版本出现在RFC6455中。而在safari(包括桌面和移动版本)上则是使用的websocket的 draft-ietf-hybi版。照这个趋势看来,WebSocket早晚会向RFC6455的方向定型。

在本文中,称RFC6455版的chrome版,safari使用的版本为safari版。


查看详细内容 »

标签:
13
Nov
November 13, 2012 分类: ASM/C/C++     作者: hoverlees     留言: 1

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

标签:
22
Sep
September 22, 2012 分类: ASM/C/C++     作者: hoverlees     留言: 94

浏览器嵌入窗口程序一直以来就是WEB应用程序的首选方案,这种方案只需要实现一个主窗口,并提供一些接口供脚本调用,内部的界面和逻辑处理全部用html,css,javascript去实现。我最早看到的相关应用是四五年前的新浪聊天室。我本人在后来的一些项目中也多次用到这种基于IE内核的实现方式。

随着HTML5的强大,嵌入浏览器方式比嵌入Shockwave Flash的方式更应该作为首选方案。本文介绍嵌入IE,Chrome,Firefox三种方式。

本文链接: http://www.hoverlees.com/blog/?p=1339

1.嵌入IE浏览器

嵌入IE内核应该是早期最常用的方法,使用windows平台上的ActiveX方式,将IWebBrowser2对象嵌入到窗口中,IWebBrowser2接口涉及到很多的接口,可以让我们进行事件处理、操作DOM、与JS通信,使用一个连接点接口与浏览器内部连接,获取和处理事件。

使用IE内核有一个缺点就是用户的操作系统各种各样,内核也是多个版本而且不兼容(只有IE9+才支持HTML5)。导致程序非常不稳定,再有就是不能跨平台。以至于现在嵌入IE应该是最差的方式了。

2.嵌入Firefox

Mozilla的XULRunner是一个跨平台的浏览器应用框架,被Mozilla用于Firefox和ThunderBird等软件的核心,同样是开源和支持HTML5,项目使用XPCOM方式实现,除了XPCOM对象(windows系统的在xpcom.dll中)的获取外,其它属性,对象和函数的访问均与MDN上Javascript的文档相同,可谓文档齐全。同样也有已经实现的第三方C++项目将基于XULRunner的浏览器封装成控件,非常方便使用。

MDN地址:https://developer.mozilla.org/en-US/docs/XULRunner

3.嵌入Chrome

Embedding Chrome:经过本人的研究,个人认为嵌入Chrome是最好的解决方案,Chrome本身开源,高效的v8引擎。同时也有很多附属的开源项目,libcef就是其中一个,cef是chrome embed framework的缩写,意在实现chrome嵌入应用程序,本人对这个项目下载下来后进行过测试,效果非常好,支持HTML5,同时跨平台。

项目地址:http://code.google.com/p/chromiumembedded/

下面是libcef调用的示例程序,给大家做参考。


查看详细内容 »

标签: ,
20
Sep
September 20, 2012 分类: ASM/C/C++     作者: hoverlees     留言: 发表

这是一个由C和lua实现的HTTP代理服务器,运行在linux下,服务器使用EPOLL事件模型处理网络流,性能还不错,本人已经使用它代理firefox上网一段时间了,能稳定运行。

如要应用在大并发量的场合,需要在启动程序前设置ulimit为一个合适的值,否则服务器在并发量达到限定值时直接关闭连接,出现连接重置的情况。

代理服务器实现了lua宿主,lua主要用于配置服务器的监听IP和端口,同时处理请求回调,可以通过对请求地址、请求主体内容、请求方式进行判断以决定是否接受该代理。

lua脚本文件名为config.lua,下面是一个示例配置:

--[[
HTTP代理服务器配置文件
@author Hoverlees  http://www.hoverlees.com/blog/?p=1337
]]

--变量名必须为proxy_config
proxy_config={
	--监听IP
	listen="0.0.0.0",
	--监听端口
	port=8766,
	on_request=function(request)
		--[[
		当收到请求后的回调函数 request为一个表格,包含内容:
		1. requestURI  请求地址
		2. requestMethod 请求方式
		3. requestBody 请求体
		4. 请求的HTTP头,如Host,Content-Type,Connection等.
		返回true表示允许代理,返回false表示不允许代理.
		]]
		print("代理:"..request.requestURI)
		return true;
	end
}

从这里下载 HTTP代理服务器 源码

19
Sep
September 19, 2012 分类: ASM/C/C++     作者: hoverlees     留言: 8

Sunday Search算法(D.M. Sunday: A Very Fast Substring Search Algorithm. Communications of the ACM, 33, 8, 132-142 (1990))

是大多数情况下比KMP和BM算法更快的串搜索算法,而且原理非常简单易理解.

例如要在”searcqpozreusevnsearch”中搜索”search”

首先初始化一个256长度的索引表,记录每个字节对应搜索串中的倒数位置,即’h'=1,’c'=2,’r'=3,’a'=4,’e'=5,’s'=6,其它字节对应-1

searcqpozreusevnsearch
search

第一次循环发现’q'与’h'不等,这时候查看搜索字符串长度后一位’p'在索引表中的值,发现是-1,则直接右移搜索字符串长度+1

searcqpozreusevnsearch
-------search

第二次循环,’s'与’o'不同,再查看搜索字符串长度后一位’e'在索引表中值为5,直接右移5位

searcqpozreusevnsearch
------------search

第三次循环,’v'与’a'不同,查询索引表中’a'的值为4,右移4位

searcqpozreusevnsearch
----------------search

第四次循环找到匹配.正常情况下比BM和Horspool都要快很多.

以前拿汇编语言写过,由于最近又需要用到,所以又重新拿C语言写了个,这次把它发到博客上方便以后使用.

#include <string.h>
/**
 * Sunday Search算法C实现
 * @author Hoverlees http://www.hoverlees.com
 */
unsigned char* sunday_search(unsigned char* str,int str_len,unsigned char* sub,int sub_len);


查看详细内容 »

09
Jun
June 9, 2012 分类: ASM/C/C++     作者: hoverlees     留言: 发表

在linux的内核模块中要申请内存,一般都是使用kmalloc的方式申请一块指定大小的内存,但这个函数有一定的限制,一般只能申请到几M到十几M的内存空间,申请内存太多将会失败。这样的限制是好的,保证具有完全权限的内核程序不会无意地过度浪费内存资源(例如通过变量a进行kmalloc(a),但因为程序员的问题,导致a是一个非常大的数字,如果不限制就会因为内存不足就导致内核崩溃了。),如果要进行DMA操作,使用几M的内存已经能达到很好的效率。

但是肯定有在内核中使用超大块内存的情况,例如vmware,它可以预分配虚拟机的所有内存。这时候,就应该自己去按页分配并自己进行页管理。

这里有一个我以前写的内核中大型内存数组管理函数,提供给大家参考参考。没有写太多的注释,仅供有兴趣的参考哈

kvector.h

#ifndef _HOVERLEES_KVECTOR_H
#define _HOVERLEES_KVECTOR_H
#include <linux/slab.h>

#define ELEMENT_LENGTH	8

#define INDEX_LINK_EACH_NUM (PAGE_SIZE/sizeof(void*)-1)
#define ELEMENTS_PER_PAGE PAGE_SIZE/ELEMENT_LENGTH

typedef struct _index_link_node{
	void* data_pages[INDEX_LINK_EACH_NUM];
	struct _index_link_node * next;
}index_link_node;

typedef struct _kvector{
	int size;
	int current_index_pos;
	int total_index_elements;
	int index_node_num;
	index_link_node* index;
}kvector;

typedef int (*KVECTOR_EACH_PAGE_CB)(void* p,void* param);

/*初始化数组*/
int kvector_init(kvector* vector,int init_size);
/*销毁数组*/
void kvector_destroy(kvector* vector);
/*数组大小更改*/
int kvector_resize(kvector* vector,int new_size);
/*设置数组中的第index个元素*/
int kvector_set(kvector* vector,int index,void* mem);
/*取得数组中的第index个元素*/
int kvector_get(kvector* vector,int index,void* mem);

void kvector_foreach_page(kvector* vector,KVECTOR_EACH_PAGE_CB callback,void* param);

#endif


查看详细内容 »

03
May
May 3, 2012 分类: ASM/C/C++     作者: hoverlees     留言: 发表

Windows上实现异形窗口有多种形式,比如SetWindowRgn方式,UpdateLayeredWindow方式,DirectX方式等,这儿要介绍的是最简单但最有效的UpdateLayeredWindow方式。

UpdateLayeredWindow可以将一个位图用alpha混合的方式更新到屏幕上,让Layered window具有半透明和异形的效果。

UpdateLayeredWindow函数定义如下:

BOOL UpdateLayeredWindow(
  HWND hwnd,             // layered窗口句柄,窗口必须有WS_EX_LAYERED样式
  HDC hdcDst,            // 屏幕DC,可以通过GetDC(NULL)取得
  POINT *pptDst,         // layered窗口要移动到的位置
  SIZE *psize,           // 设置layered窗口的大小
  HDC hdcSrc,            // 源DC,类似BitBlt的源DC
  POINT *pptSrc,         // 要画到layered窗口上的源DC上的起点
  COLORREF crKey,        // 要作为透明的颜色,这个参数针对位图很有效
  BLENDFUNCTION *pblend, // 混合模式,指定alphaFormat为AC_SRC_ALPHA,就使用alpha混合方式。
  DWORD dwFlags          // 标志使用哪种混合模式,可以是ULW_ALPHA 表示使用alpha混合,ULW_OPAQUE表示不使用alpha混合,ULW_COLORKEY表示指定的颜色为透明色。
);

其实只要这个函数调用正确,透明窗口就实现了,下面是实现的一个透明窗口示例,用的是筱筱姑娘最喜欢的阿狸作为透明窗口主角。

文章中给出类的定义说明,示例程序和源码通过这里下载


查看详细内容 »

标签: ,
06
Apr
April 6, 2012 分类: ASM/C/C++, Java     作者: hoverlees     留言: 3

本来想玩玩android的网络应用,特地买了一个htc G12手机来做实验,首先实验了p2p,发现不同的网络运营商根本打不通(为此,我还特地分析了QQ 的解决方式,他们也是走TCP的),后来想想p2p实现在这种场合根本没有太多意义,自已要实现数据包的顺序,校验,还不如用tcp.后来就做成了服务器转发方式。

可惜,我的htc被小偷偷了,准备还是用iphone 4s了,所以这个代码也不打算写了,有兴趣的人可以拿去参考参考。

服务器使用的还是java的nio方式,心得就是调用方式是和epoll最像,epoll的方式是在发送数据前注册EPOLLOUT事件,然后在有缓冲区可以发送数据时就能收到这个事件,在这个事件收到时进行数据包发送,当数据包发送完成后再取消注册EPOLLOUT事件即可,这样就不用判断是否有缓冲可以发送数据了。java nio的select 就相于epoll_wait。

相比于 windows上的io completion port方式,不同的就是数据发送。iocp发送数据是不管怎样,先把你的数据通过WSASend加到发送队列,然后内核会帮你按顺序发送这个队列,当发送完成后会通知应用程序。他们的差别在于通知是一先一后的关系,但都是最高效的。所以我猜想windows平台上的java nio实现就是只要你注册了监听写事件时就会循环收到写事件而不管缓冲区是否为空。

这是一个eclipse工程,com.hoverlees下的为android客户端程序,com.astream是拿udp做p2p的实验程序.com.astream.Server为服务器程序,Client.java为命令行客户端。

已实现的功能:

1.查看用户列表

2.选择用户进行聊天

3.可发送文本,图片,视频,语音

4.可发送离线文件。

界面比较丑,因为是做着玩的。

整个工程下载

08
Mar
March 8, 2012 分类: ASM/C/C++     作者: hoverlees     留言: 发表

由于经常需要这样一些场合,让应用程序能够响应简单的HTTP请求,以实现一些功能,所以就做了一个这样的小型HTTP服务器库。库使用C++实现,编译成MT,Release模式的静态链接库,欢迎有兴趣的朋友使用。该库仅适用Windows操作系统。

下面是库的定义:

#ifndef __HOVERLEES_TINY_HTTPD_H
#define __HOVERLEES_TINY_HTTPD_H

#include <Windows.h>
#include <WinSock.h>
#include <map>

struct HoverCompare{
	bool operator()(const char* x, const char* y){
		return strcmp(x,y)<0;
	}
};
/**
 * @name 嵌入应用程序的简单HTTP服务器
 * @author Hoverlees http://www.hoverlees.com
 * 调用者继承这个类以处理HTTP请求
 */
class TinyHttpd{
private:
	static bool inited;
	static WSADATA wsaData;
	static DWORD WINAPI ServerThreadProc(void* lpParameter);
	static DWORD WINAPI ClientThreadProc(void* lpParameter);
protected:
	const static int STATUS_IDLE=0;
	const static int STATUS_RUNNING=1;
	typedef std::map<char*,char*,HoverCompare> hmap;
	int status;
	SOCKET serverSocket;
	sockaddr_in address;
public:
	TinyHttpd();
	/**
	 * 启动服务器
	 * @param bindPort 梆定的HTTP端口
	 * @param bindIP 梆定IP地址
	 * @return 成功状态
	 */
	bool startServer(unsigned short bindPor=80,const char* bindIP="0.0.0.0");
	/**
	 * 停止服务器
	 * @return 成功状态
	 */
	bool stopServer();
	/**
	 * 向客户端发送应答,每一个请求只能调用一次这个函数。
	 * @param clientSocket 客户端socket
	 * @param mem 应答的内容
	 * @param memSize 应答内容大小
	 * @param response_headers HTTP响应头,如果不设置,可以为NULL
	 * @return 操作结果
	 */
	bool sendResponse(SOCKET clientSocket,const char* mem,int memSize,hmap* response_headers);
	/**
	 * 回调方法,当服务器收到HTTP请求时,调用这个函数,注意TinyHttpd只支持GET请求
	 * @param clientSocket客户端socket,一般不直接操作,而是传入到sendResponse中。
	 * @param uri 请求uri,如客户端请求 /hoverlees?a=1&b=2 时,uri是 /hoverlees
	 * @param params 请求参数,这是HTTP get参数key=>value的map对象.如 params["a"]=1
	 * @return 如果用户处理了该请求,返回true,这时服务器响应200,如果没处理请求返回false ,服务器响应404
	 */
	virtual bool onHttpRequest(SOCKET clientSocket,const char* uri,hmap* params){return false;}
};

#endif

使用示例如下:

查看详细内容 »

24
Feb
February 24, 2012 分类: ASM/C/C++     作者: hoverlees     留言: 3

在普通情况下,IWebBrowser2控件加入到窗口中时,浏览器的快捷键,包括DEL,TAB等特殊键都不能使用。这个不是控件的Bug,而是一种特性。要让控件处理这些按键,需要通过webBrowser插件的IOleInPlaceActiveObject来处理。

具体操作是在主窗口的事件循环里处理。步骤如下:

//窗口主循环线程
while(bRet=GetMessage(&msg,NULL,0,0)!=0){
	if(bRet==-1) break;
	TranslateMessage(&msg);

	if(IsDialogMessage(WebBrowser2控件窗口句柄,&msg)){//如果是控件窗口的消息
		QueryInterface取得IWebBrowser2的IOleInPlaceActiveObject
		调用IOleInPlaceActiveObject的TranslateAccelerator函数,参数是&msg
	}
	else DispatchMessage(&msg); 
}

C语言使用者在QueryInterface和调用COM对象函数时,可以用我另一编关于COM的C调用的函数库。

标签: ,