silverlight客户端和服务端就像一对被微软家长阻隔的恋人,服务端提供了安全策略文件这个硬通货,就如同男人买了房,一切都开绿灯了。
这对恋人终于可以约会了,他们如何飞鸿传书呢?是写在枫叶上,还是封上了火漆的羊皮卷里。其实每个民族有自己的风俗,每个程序员也可以写出不同的协议。
我们把他们通讯的基本单位抽象成Message,你可以这么描述它:
{
/// <summary>
/// 处理信息的socket对象
/// </summary>
public System.Net.Sockets.Socket clientSocket { get; set; }
/// <summary>
/// 消息内容
/// </summary>
public byte[] Content{get;set;}
/// <summary>
/// 消息长度
/// </summary>
public int Size{get;set;}
/// <summary>
/// 消息状态
/// </summary>
public byte Flag{get;set;}
/// <summary>
/// 消息类别
/// </summary>
public byte Class {get;set;}
也就是说服务端和客户端通讯的时候是一个个message为单位发送过去的,但是传输过程中,服务端不可能刚好完整就接到了一个Message,也可能接收到半个Message,所以需要加一个size来判断是否Message读取完毕。
在封装Message的时候我们要写一个MessageStream类来方便封装Message,一般网络游戏服务端和客户端都会有这样的类,来从byte数组中把数据读出,或者把数据写入到byte数组中,这样的类里一般都有ReadInt,ReadByte,ReadString,ReadFloat,WriteInt,WriteByte , WriteString,WriteFloat 这样的方法。
这里我给出一个很简单的封装类MessageStream
{
private byte [] buffer;
private int position;
private int length;
private int capacity;
public MessageStream()
{
buffer = new byte [ 0 ];
position = 0 ;
length = 0 ;
capacity = 0 ;
}
private byte ReadByte()
{
if ( this .position >= this .length)
{
return 0 ;
}
return this .buffer[ this .position ++ ];
}
private int ReadInt()
{
int num = this .position += 4 ;
if (num > this .length)
{
this .position = this .length;
return - 1 ;
}
return ((( this .buffer[num - 4 ] | ( this .buffer[num - 3 ] << 8 )) | ( this .buffer[num - 2 ] << 0x10 )) | ( this .buffer[num - 1 ] << 0x18 ));
}
private byte [] ReadBytes( int count)
{
int num = this .length - this .position;
if (num > count)
{
num = count;
}
if (num <= 0 )
{
return null ;
}
byte [] buffer = new byte [num];
if (num <= 8 )
{
int num2 = num;
while ( -- num2 >= 0 )
{
buffer[num2] = this .buffer[ this .position + num2];
}
}
else
{
Buffer.BlockCopy( this .buffer, this .position, buffer, 0 , num);
}
this .position += num;
return buffer;
}
public bool Read( out Message message)
{
message = null ;
position = 0 ;
if (length > 6 )
{
message = new Message();
message.Class = ReadByte();
message.Flag = ReadByte();
message.Size = ReadInt();
if (message.Size <= 0 || message.Size <= length - position)
{
if (message.Size > 0 )
{
message.Content = ReadBytes(message.Size);
}
Remove(message.Size + 6 );
return true ;
}
else
{
message = null ;
return false ;
}
}
else
{
return false ;
}
}
private void EnsureCapacity( int value)
{
if (value <= this .capacity)
return ;
int num1 = value;
if (num1 < 0x100 )
num1 = 0x100 ;
if (num1 < ( this .capacity * 2 ))
num1 = this .capacity * 2 ;
byte [] buffer1 = new byte [num1];
if ( this .length > 0 )
Buffer.BlockCopy( this .buffer, 0 , buffer1, 0 , this .length);
this .buffer = buffer1;
this .capacity = num1;
}
public void Write(byte[] intbuffer, int offset, int count)
{
if (intbuffer.Length - offset < count)
{
count = intbuffer.Length - offset;
}
EnsureCapacity(length + count);
Array.Clear(this.buffer, length, capacity - length);
Buffer.BlockCopy(intbuffer, offset, this.buffer, length, count);
length += count;
}
{
if (length >= count)
{
Buffer.BlockCopy(buffer, count, buffer, 0 , length - count);
length -= count;
Array.Clear(buffer, length, capacity - length);
}
else
{
length = 0 ;
Array.Clear(buffer, 0 , capacity);
}
}
}
有了MessageStream这样的辅助类了,我们就可以在Message类加上一个添加消息和一个读取消息的方法
/// 把消息对象转化为byte[]
/// </summary>
/// <returns></returns>
public byte [] ToBytes()
{
byte [] _byte;
using (MemoryStream mem = new MemoryStream())
{
BinaryWriter writer = new BinaryWriter(mem);
writer.Write(Class);
writer.Write(Flag);
writer.Write(Size);
if (Size > 0 )
{
writer.Write(Content);
}
_byte = mem.ToArray();
writer.Close();
}
return _byte;
}
/// <summary>
/// 从byte[]中读取一个message对象
/// </summary>
/// <param name="Buffer"></param>
/// <returns></returns>
public static Message FromBytes( byte [] Buffer)
{
Message message = new Message();
using (MemoryStream mem = new MemoryStream(Buffer))
{
BinaryReader reader = new BinaryReader(mem);
message.Class = reader.ReadByte();
message.Flag = reader.ReadByte();
message.Size = reader.ReadInt32();
if (message.Size > 0 )
{
message.Content = reader.ReadBytes(message.Size);
}
reader.Close();
}
return message;
}
有了MessageStream,Message这两个忠实的仆人的帮助,SL客户端和服务端这对恋人就不用像发摩尔斯电码那样来交流了,他们只要口述旨意,让两个仆人去滴滴答答发电报就行了。见过电影里发电报的都知道,有人发电报,就有人收电报。所以客户端有这样两个仆人,服务端也得有这样两个仆人。那我们程序员的话说SL客户端有这样两个类,同样服务端也要有这样两个类。
当然以上我只是简单描述了一个Message对象,在真实项目里Message对象没这么简单。我举个例子:
格式:
循环 | 循环 | ||||||||||||||||||||||||||
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 不定长 | ||||||||||||
消息个数 | 消息总长度 | 用户UID | 用户CID | 单个消息指针起始位置 | 消息内容 | ||||||||||||||||||||||
消息内容协议 | |||||||||||||||||||||||||||
0 | 1 | 2 | 3 | 4 | 5 | 不定长 | |||||||||||||||||||||
消息Group | 消息具体type | 时间戳 | 消息逻辑内容 |
| |||||||||||||||||||||||
HTTP命令区:
Message:1 (byte) 消息个数
MessageLength: 120 (int) byte数组总长度
UID:2 (int)用户标识ID
CID 1 (int)用户子ID
MessagePointer: 0 (short) 单个消息指针起始位置
HTTP 内容区:
GroupID:1 (byte) 消息组ID
GroupType:1 (byte) 消息组具体操作ID
Timestamp: 0 (int) 时间戳
ContentLength: 10 (short) 消息内容的长度(根据内容来定类型)
Content:”你好吗?” (string) 消息内容
这种协议一次可以发多个消息体,只带一个消息头,也带上用户的信息,因为mmorpg里参与最多是用户,发的消息也和用户编号直接相关。
有兴趣的同学可以写下这个种协议的写入和读取。到此为止,是不是服务端和客户端就可以踏入婚姻的殿堂了,可惜还远没有呢,这是各自准备了两个伴郎和伴娘,客户端急了:“老公,你房子什么时候才装修好啊?”
大家看到这里也在想,服务端什么时候才可以搭建好啊,就像装房子一样,急不得,还有好多问题。请看下一篇:
Silverlight MMORG WebGame游戏设计(四)-----Client:Server!房子啥时候装好?我急嫁人啊!