真是撞衫了,本来写好个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,而没有采用多线程实现,增强程序稳定性,增强程序处理能力,例如信息接收部分:
{
ReceiveAsync(_receiveSocketArgs);
}
private void ReceiveAsync(SocketAsyncEventArgs socketAsyncEventArgs)
{
if ( ! _acceptedSocket.ReceiveAsync(socketAsyncEventArgs))
{
ReceiveCallback(_acceptedSocket, socketAsyncEventArgs);
}
}
void ReceiveCallback( object sender, SocketAsyncEventArgs e)
{
if (e.SocketError != SocketError.Success)
{
return ;
}
_receiveBuffer.Offset += e.BytesTransferred;
if (_receiveBuffer.IsMessageReceived())
{
if (OnReceive != null )
{
NetworkMessage msg = NetworkMessage.Deserialize(_receiveBuffer.Buffer);
_receiveBuffer.AdjustBuffer();
OnReceive( this , new ReceiveArgs(msg));
}
}
else
{
// adjust the buffer pointer
e.SetBuffer(_receiveBuffer.Offset, _receiveBuffer.Remaining);
}
// queue a an async read request
ReceiveAsync(_receiveSocketArgs);
}
2、这篇文章中http://www.cnblogs.com/yjmyzz/archive/2009/12/02/1615204.html说的粘包的现象,好像在这个DEMO中是没有的,不知道我说的对不对?还望高人指点。
这里将发送数据进行封包,序列化并转化为byte数组后再发送,接收端执行同样相反的拆包动作,将要发送的数据大小放在封包好的byte数组的前四位,接收端配合接收缓冲,用来判断包是否已经被完整收到。
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 function 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, 0 , lengthBytes.Length);
Array.Copy(messageBytes, 0 , finalBytes, 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, 0 , 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 ;
}
}
}
源码下载http://files.cnblogs.com/xiaozhuang/SilverlightSocketDemo.rar
摘自:http://www.cnblogs.com/xiaozhuang/archive/2010/07/15/1778091.html