上一篇我们已经在服务器和客户端之间建立起一个能双向通讯的途径,如果你马上按以前的经验直接丢送数据,恭喜,数据能过去,可你却根本不认识,这是自然,他的地盘他要做主,websocket 有其自己约定的数据格式,我们必须按照这个格式来才行的。
协议这玩意,很是枯燥,只能用,不能创新,但多了解一些这种规则,对以后我们定义传输协议有很强的参考价值,所以我们还是得认真瞧瞧。
打开: http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10
找到如下图所示的位置,这里面有详细的说明
具体每个字段的说明,大家可以慢慢啃英文,我们就讨论一下,如何将这玩意,在我们的系统中使用吧。
先用我的语言理解一下这个格式,有什么不正确欢迎大家指正。
这个格式中,最重要的就是第二个字节:
1.第一位决定是否有掩码,如果Mask为1,就会有后面那个4字节的Masking-Key,客户发送过来的数据,都有个这值,所以当我尝试发送一个空串过来的时候,服务器会收到类似如下的数据:(以2进制显示,方便大家了解)。
传输的数据,采用掩码的异或运算来产生,比如我发送一个字符1,服务器会收到如下数据:
1000 0001
1000 0001
0011 1100 1001 0111 0011 1001 1010 1111 0000 1101
如果Mask为0,则不会有后面的掩码
2.后7位是用来决定这个帧的长度,重点为原文中的这一句话:
Payload length: 7 bits, 7+16 bits, or 7+64 bits
如果这7位表示的长度小于126,则此即发送数据的实际长度
如果等于126,则后面两个字节表示长度,即7+16Bits的意思
如果等于127,则后面的8字节表示长度,即7+64Bits的意思
数据传输协议,就应该合理的使用每一个数据位,虽然感觉理解累些,但却能减少数据传输的数量,这就我们大设计协议的时候,应该好好学习的。
为此,我封装了如下两个类,
DataFrameHeader,对协议中头两个字节的处理,代码如下:
///
///
2字节数据头
///
public
classDataFrameHeader
{
private
bool_fin;
private
bool_rsv1;
private
bool_rsv2;
private
bool_rsv3;
private
sbyte_opcode;
private
bool_maskcode;
private
sbyte_payloadlength;
///
///
FIN
///
public
boolFIN{
get{
return_fin;}}
///
///
RSV1
///
public
boolRSV1{
get{
return_rsv1;}}
///
///
RSV2
///
public
boolRSV2{
get{
return_rsv2;}}
///
///
RSV3
///
public
boolRSV3{
get{
return_rsv3;}}
///
///
OpCode
///
public
sbyteOpCode{
get{
return_opcode;}}
///
///
是否有掩码
///
public
boolHasMask{
get{
return_maskcode;}}
///
///
PayloadLength
///
public
sbyteLength{
get{
return_payloadlength;}}
///
///
构造函数
///
///
主要用于解析接收数据
publicDataFrameHeader(
byte[]buffer)
{
if(buffer.Length<
2)
throw
newException(
"
无效的数据头.
");
//
第一个字节
_fin=(buffer[
0]&
0x80)==
0x80;
_rsv1=(buffer[
0]&
0x40)==
0x40;
_rsv2=(buffer[
0]&
0x20)==
0x20;
_rsv3=(buffer[
0]&
0x10)==
0x10;
_opcode=(
sbyte)(buffer[
0]&
0x0f);
//
第二个字节
_maskcode=(buffer[
1]&
0x80)==
0x80;
_payloadlength=(
sbyte)(buffer[
1]&
0x7f);
}
///
///
构造函数
///
///
主要用于发送封装数据
publicDataFrameHeader(
boolfin,
boolrsv1,
boolrsv2,
boolrsv3,
sbyteopcode,
boolhasmask,
intlength)
{
_fin=fin;
_rsv1=rsv1;
_rsv2=rsv2;
_rsv3=rsv3;
_opcode=opcode;
//
第二个字节
_maskcode=hasmask;
_payloadlength=(
sbyte)length;
}
///
///
返回帧头字节
///
///
public
byte[]GetBytes()
{
byte[]buffer=
new
byte[
2]{
0,
0};
if(_fin)buffer[
0]^=
0x80;
if(_rsv1)buffer[
0]^=
0x40;
if(_rsv2)buffer[
0]^=
0x20;
if(_rsv3)buffer[
0]^=
0x10;
buffer[
0]^=(
byte)_opcode;
if(_maskcode)buffer[
1]^=
0x80;
buffer[
1]^=(
byte)_payloadlength;
returnbuffer;
}
}
DataFrame,对协议整体的封装,代码如下:
///
///
数据帧
///
public
classDataFrame
{
DataFrameHeader_header;
private
byte[]_extend=
new
byte[
0];
private
byte[]_mask=
new
byte[
0];
private
byte[]_content=
new
byte[
0];
///
///
构造函数
///
///
主要用于解析接收数据
publicDataFrame(
byte[]buffer)
{
//
格式化帧头
_header=
newDataFrameHeader(buffer);
//
填充扩展长度字节
if(_header.Length==
126)
{
_extend=
new
byte[
2];
Buffer.BlockCopy(buffer,
2,_extend,
0,
2);
}
else
if(_header.Length==
127)
{
_extend=
new
byte[
8];
Buffer.BlockCopy(buffer,
2,_extend,
0,
8);
}
//
是否有掩码
if(_header.HasMask)
{
_mask=
new
byte[
4];
Buffer.BlockCopy(buffer,_extend.Length+
2,_mask,
0,
4);
}
//
消息体
if(_extend.Length==
0)
{
_content=
new
byte[_header.Length];
Buffer.BlockCopy(buffer,_extend.Length+_mask.Length+
2,_content,
0,_content.Length);
}
else
if(_extend.Length==
2)
{
_content=
new
byte[Convert.ToUInt16(_extend)];
Buffer.BlockCopy(buffer,_extend.Length+_mask.Length+
2,_content,
0,_content.Length);
}
else
{
_content=
new
byte[Convert.ToUInt64(Common.CopyArrayData(buffer,
2,
8))];
Buffer.BlockCopy(buffer,_extend.Length+_mask.Length+
2,_content,
0,_content.Length);
}
//
如果有掩码,则需要还原原始数据
if(_header.HasMask)_content=Mask(_content,_mask);
}
///
///
构造函数
///
///
主要用于发送封装数据
publicDataFrame(
stringcontent)
{
_content=Encoding.UTF8.GetBytes(content);
intlength=_content.Length;
if(length<
126)
{
_extend=
new
byte[
0];
_header=
newDataFrameHeader(
true,
false,
false,
false,OpCode.Text,
false,length);
}
else
if(length<
65536)
{
_extend=
new
byte[
2];
_header=
newDataFrameHeader(
true,
false,
false,
false,OpCode.Text,
false,
126);
_extend[
0]=(
byte)(length/
256);
_extend[
1]=(
byte)(length%
256);
}
else
{
_extend=
new
byte[
8];
_header=
newDataFrameHeader(
true,
false,
false,
false,OpCode.Text,
false,
127);
intleft=length;
intunit=
256;
for(
inti=
7;i>
1;i--)
{
_extend[i]=(
byte)(left%unit);
left=left/unit;
if(left==
0)
break;
}
}
}
///
///
获取适合传送的字节数据
///
public
byte[]GetBytes()
{
byte[]buffer=
new
byte[
2+_extend.Length+_mask.Length+_content.Length];
Buffer.BlockCopy(_header.GetBytes(),
0,buffer,
0,
2);
Buffer.BlockCopy(_extend,
0,buffer,
2,_extend.Length);
Buffer.BlockCopy(_mask,
0,buffer,
2+_extend.Length,_mask.Length);
Buffer.BlockCopy(_content,
0,buffer,
2+_extend.Length+_mask.Length,_content.Length);
returnbuffer;
}
///
///
获取文本
///
public
stringText
{
get
{
if(_header.OpCode!=OpCode.Text)
return
string.Empty;
returnEncoding.UTF8.GetString(_content);
}
}
///
///
加掩码运算
///
private
byte[]Mask(
byte[]data,
byte[]mask)
{
for(
vari=
0;i {
data[i]=(
byte)(data[i]^mask[i%
4]);
}
returndata;
}
}
调用的关键代码如下:
......
//
RecvDataBuffer是接收到的数据字节数组,这段代码即服务端收到客户端的信息,加个时间和Hello,再返回给客户。
DataFramedr=
newDataFrame(RecvDataBuffer);
stringstrResp=String.Format(
"
[{0}]:Hello,{1}.
",DateTime.Now.ToString(),dr.Text);
dr=
newDataFrame(strResp);
client.Send(dr.GetBytes());
......
至此,我们终于可以在客户端和服务器之间进行有效沟通了。
转自:http://www.cnblogs.com/xiaoyaoguzhu/archive/2011/12/16/2290323.html