真是撞衫了,本来写好个DEMO,打算今天发上来的,可是早上发现翁玉礼http://www.cnblogs.com/wengyuli/同学也发了一个,不过翁同学是用来实现视频聊天的,我是打算用来实现XMPP的;既然大家都对SOCKET这么有兴趣,就放上来一起研究。
先看下实现效果
服务端WPF:
多个用户连接服务端,服务端接收所有用户发过来的信息,也可以向指定的用户发送信息。
客户端Silverlight:
客户端向服务端发送信息,并接收服务端发过来的信息。
这个DEMO的代码参考了这个http://msdn.microsoft.com/zh-cn/magazine/dd315415.aspx,还是官方的代码信得过!Silverlight的客户端没的说,只能用异步Socket实现,WPF的服务端也采用了.net 3.5以后才出现的异步Socket,据说这样可以大大增强服务器端的处理能力。
项目结构如图:
分为三个项目:服务端,客户端和用来宿主SL的web项目,服务端打开两个端口,943和4530,943用来向Silverlight提供跨域文件,4530用来和Silverlight程序通信,我主要说说这个DEMO里面我觉得比较好的地方:
1、客户端和服务端全部采用异步Socket,而没有采用多线程实现,增强程序稳定性,增强程序处理能力,例如信息接收部分:
2、这篇文章中http://www.cnblogs.com/yjmyzz/archive/2009/12/02/1615204.html说的粘包的现象,好像在这个DEMO中是没有的,不知道我说的对不对?还望高人指点。
这里将发送数据进行封包,序列化并转化为byte数组后再发送,接收端执行同样相反的拆包动作,将要发送的数据大小放在封包好的byte数组的前四位,接收端配合接收缓冲,用来判断包是否已经被完整收到。
代码
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
using
System.Xml.serialization;
using
System.IO;
namespace
SilverlightSocketDemo.Server
{
[XmlInclude(
typeof
(MymessagE))]
public
abstract
class
Networkmessage
{
public
static
readonly
int
LENGTH_BYTES
=
4
;
//
first 4 bytes of the buffer will contain the length of the serialized Networkmessage
//
this@R_794_3816@ treats input argument as immutable
public
static
Networkmessage Deserialize(
byte
[] buffer)
{
int
length
=
BitConverter.ToInt32(buffer,
0
);
int
starTindex
=
sizeof
(
int
);
String
strNetworkmessage
=
Encoding.UTF8.GetString(buffer, starTindex, length);
StringReader StringReader
=
new
StringReader(strNetworkmessagE);
Xmlserializer xmlserializer
=
new
Xmlserializer(
typeof
(NetworkmessagE));
return
(NetworkmessagE)xmlserializer.Deserialize(StringReader);
}
//
the first 4 bytes of the serialized byte array will contain the length of the buffer that
//
contains the prevIoUsly serialized Networkmessage;
public
static
byte
[] serialize(Networkmessage msg)
{
StringWriter StringWriter
=
new
StringWriter();
Xmlserializer xmlserializer
=
new
Xmlserializer(
typeof
(NetworkmessagE));
xmlserializer.serialize(StringWriter, msg);
byte
[] messageBytes
=
Encoding.UTF8.GetBytes(StringWriter.ToString());
byte
[] lengthBytes
=
BitConverter.GetBytes(messageBytes.Length);
byte
[] finalBytes
=
new
byte
[lengthBytes.Length
+
messageBytes.Length];
//
copy the length of the serialized object
array.Copy(lengthBytes,
0
, finalBytes, lengthBytes.Length);
array.Copy(messageBytes, lengthBytes.Length, BitConverter.ToInt32(lengthBytes,
0
));
return
finalBytes;
}
}
public
class
Mymessage : Networkmessage
{
public
String
User {
get
;
set
; }
public
String
Content {
get
;
set
; }
public
override
String
ToString()
{
return
User
+
"
:
"
+
Content;
}
}
public
class
SocketBuffer
{
public
const
int
BUFFERSIZE
=
1024
;
protected
byte
[] _buffer;
protected
int
_offset
=
0
;
public
SocketBuffer()
{
_buffer
=
new
byte
[BUFFERSIZE];
}
public
SocketBuffer(
int
sizE)
{
_buffer
=
new
byte
[size];
}
public
byte
[] Buffer
{
get
{
return
_buffer; }
set
{ _buffer
=
value; }
}
//
offset will allways inDicate the length of the buffer that is filled
public
int
Offset
{
get
{
return
_offset; }
set
{ _offset
=
value; }
}
public
int
remaining
{
get
{
return
_buffer.Length
-
_offset; }
}
}
public
class
receiveBuffer : SocketBuffer
{
//
removes a serlialized message from the buffer, copies the partial message to the begining
//
and adjusts the offset
public
void
AdjustBuffer()
{
int
messageSize
=
BitConverter.ToInt32(_buffer,
0
);
int
lengthToCopy
=
_offset
-
Networkmessage.LENGTH_BYTES
-
messageSize;
array.Copy(_buffer, _offset, _buffer, lengthToCopy);
_offset
=
lengthToCopy;
}
//
this method checks if a complete message is received
public
bool
IsmessageReceived()
{
if
(_offset
<
4
)
return
false
;
int
sizeToRecieve
=
BitConverter.ToInt32(_buffer,
0
);
//
check if we have a complete Networkmessage
if
((_offset
-
4
)
<
sizeToRecievE)
return
false
;
//
we have not received the complete message yet
//
we received the complete message and may be more
return
true
;
}
}
}