Silverlight异步Socket通信

真是撞衫了,本来写好个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,而没有采用多线程实现,增强程序稳定性,增强程序处理能力,例如信息接收部分:

代码
  public void ReceiveAsync()
        {
            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(
thisnew 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;
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 { getset; }
        
public string Content { getset; }
        
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;
        }
    }
}

 

 

posted @ 2010-07-15 15:30 小庄 阅读(1947) 评论(8) 编辑 收藏

 回复 引用 查看   
#2楼 2010-07-15 15:56 Leon Weng      
呵呵 我本来是想在 silverlight上做P2P视频的 但是发现搞不了 只有通过socket这种比较原始的方式 处理了 看看你的代码先
 回复 引用 查看   
#3楼 2010-07-15 15:59 Leon Weng      
粘包问题 估计需要一定得数据量才能体现的比较明显吧
 回复 引用 查看   
#4楼 2010-07-16 02:06 silverlightchina      
尊敬的作者您好:

您的"Silverlight异步Socket通信"文章已经被银光中国网(SilverlightChina.Net)转载收录,我们在文章明显位置标识您的原创版权信息,如果您对转载有异议,请您联系admin@silverlightchina.net,我们会及时回复。

感谢您提供优秀的Silverlight系列文章。

您的文章地址:
http://silverlightchina.net/html/tips/2010/0715/1538.html

银光中国网

 回复 引用 查看   
#5楼 2010-08-17 13:52 华新      
@小庄
大哥,你这Socket如果用FOR循环发送的话,会出现错误。每隔6-8秒发就没问题

 回复 引用 查看   
#6楼 2010-08-17 13:53 华新      
可不可以给个联系方式?想请教下你。
 回复 引用 查看   
#7楼[楼主] 2010-08-23 09:38 小庄      
@华新
我qq 16202419

 回复 引用 查看   
#8楼 2010-10-12 10:17 rgqancy      
路过,标记