Silverlight下的Socket通讯

1 篇文章 0 订阅
1 篇文章 0 订阅


Silverlight下的Socket通讯

分类: Silverlight|WPF 1513人阅读 评论(1) 收藏 举报

本文版权归 博客园 程兴亮 所有,如有转载,请严格按如下方式表明原文作者及出处,以示尊重!!

作者:程兴亮
出处:http://www.cnblogs.com/chengxingliang/archive/2012/05/24/2515100.html

 

  在Silverlight中进行通讯,只能使用4502-4534之间的端口进行数据传输,另外Silverlight客户端会自动向943端口的服务器端发送一个“<policy-file-request/>”的语句请求,然后服务器端943端口回发以下文件以许可Socket通讯。

<?xml version="1.0" encoding="utf-8" ?>
<access-policy>
  <cross-domain-access>
    <policy>
      <allow-from>
        <domain uri="*"/>
      </allow-from>
      <grant-to>
        <socket-resource port="4502-4534" protocol="tcp"/>
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>

  A.现在我们首先来看服务器端的代码,主要分为策略响应步骤和服务响应步骤。

  策略步骤一:启动监听943端口是否有需要安全策略文件请求

  策略步骤二:如果客户端请求是<policy-file-request/>,则将安全策略文件作为bytes发送给客户端

  服务步骤一:启动服务器端,监听4525端口,是否有Socket对话请求

  服务步骤二:如果有客户端请求的连接,则发送消息告知客户端

  代码如下:

    class Program
    {
        static void Main(string[] args)
        {
            //策略步骤一:启动监听943端口是否有需要安全策略文件请求
            Thread access = new Thread(new ThreadStart(accessThread));
            access.Start();

            //服务步骤一:启动服务器端,监听4525端口,是否有Socket对话请求
            Thread server = new Thread(new ThreadStart(ServerThread));
            server.Start();
        }
        //策略请求监听
        static void accessThread()
        {
            //获取943端口监听的Socket服务端
            Socket socket = GetSocketServer(943);
            while (true)
            {
                Socket new_access = socket.Accept();
                string clientPolicyString = "<policy-file-request/>";
                byte[] requestbytes = new byte[clientPolicyString.Length];
                new_access.Receive(requestbytes);
                string requeststring = System.Text.Encoding.UTF8.GetString(requestbytes, 0, requestbytes.Length);

                if (requeststring == clientPolicyString)
                {
                    //策略步骤二:如果客户端请求是<policy-file-request/>,则将安全策略文件作为bytes发送给客户端
                    byte[] accessbytes = GetPolicyToClient();
                    new_access.Send(accessbytes, accessbytes.Length, SocketFlags.None);
                    new_access.Close();
                }
                Thread.Sleep(100);
            }
        }

        static void ServerThread()
        {
            //获取4525端口监听的Socket服务端
            Socket socket = GetSocketServer(4525);
            while (true)
            {
                Socket _socket = socket.Accept();
                //服务步骤二:如果有客户端请求的连接,则发送消息告知客户端
                byte[] b2 = new byte[1024];
                _socket.Receive(b2);
                Console.WriteLine(Encoding.UTF8.GetString(b2).Replace("\0", ""));
                string recString = "我已经收到消息了";
                _socket.Send(Encoding.UTF8.GetBytes(recString));
                _socket.Close();
                Thread.Sleep(100);
            }
        }
        //根据端口建立Socket服务器端
        static Socket GetSocketServer(int serverPort)
        {
            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            socket.Bind(new IPEndPoint(IPAddress.Any, serverPort));
            socket.Listen(40);
            return socket;
        }
        //获取安全策略文件的byte[]
        static byte[] GetPolicyToClient()
        {
            string path = Environment.CurrentDirectory.Replace("\\bin\\Debug","");
            FileStream fs = new FileStream(path+ @"\clientaccesspolicy.xml", FileMode.Open);
            int length = (int)fs.Length;
            byte[] bytes = new byte[length];
            fs.Read(bytes, 0, length);
            fs.Close();
            return bytes;
        }

    }

  B.其次我们来看客户端操作,分为以下几个步骤:

  客户端步骤一:发起服务器连接请求。

  客户端步骤二:连接服务器成功,将需要发送的数据放入缓冲区中,然后异步向服务器发送消息请求

  客户端步骤三:消息发送成功,此时设置一个新的缓冲区实例,并且发起异步接收服务器返回的消息

  客户端步骤四:获取到服务器返回的消息,关闭Socket 

  客户端cs代码如下:

    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
        }
        System.Net.Sockets.Socket socket;

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            byte[] userbytes = Encoding.UTF8.GetBytes(this.tbInput.Text);
            socket = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            SocketAsyncEventArgs socketArgs = new SocketAsyncEventArgs();
            socketArgs.RemoteEndPoint = new DnsEndPoint("127.0.0.1", 4525);
            //将需要发送的内容转为byte[],保存到UserToken属性中
            socketArgs.UserToken = userbytes;
            socketArgs.Completed += new EventHandler<SocketAsyncEventArgs>(socketArgs_Completed);
            //客户端步骤一:发起服务器连接请求。
            socket.ConnectAsync(socketArgs);
        }
        //每发生一个Socket操作都讲激活此方法,操作包括(Connect/Send/Receive/None)
        void socketArgs_Completed(object sender, SocketAsyncEventArgs e)
        {
            if (e.LastOperation == SocketAsyncOperation.Connect)
            {
                //客户端步骤二:连接服务器成功,将需要发送的数据放入缓冲区中,然后异步向服务器发送消息请求
                byte[] userbytes = (byte[])e.UserToken;
                e.SetBuffer(userbytes, 0, userbytes.Length);
                socket.SendAsync(e);
            }
            else if (e.LastOperation == SocketAsyncOperation.Send)
            {
                //客户端步骤三:消息发送成功,此时设置一个新的缓冲区实例,并且发起异步接收服务器返回的消息
                byte[] userbytes = new byte[1024];
                e.SetBuffer(userbytes, 0, userbytes.Length);
                socket.ReceiveAsync(e);
            }
            else if (e.LastOperation == SocketAsyncOperation.Receive)
            {
                //客户端步骤四:获取到服务器返回的消息,关闭Socket
                string RecevieStr = Encoding.UTF8.GetString(e.Buffer, 0, e.Buffer.Length).Replace("\0", "");
                //因为是异步Socket请求,所以需要使用UI线程更新lbShowMessage的显示效果
                this.lbShowMessage.Dispatcher.BeginInvoke(new DoThingDele(DoThing), RecevieStr);
                socket.Close();
            }
        }
        //更新UI
        public void DoThing(string arg)
        {
            this.lbShowMessage.Content = this.lbShowMessage.Content + "->" + arg;
        }
        //声明的一个DoThing方法委托
        public delegate void DoThingDele(string arg);
    }

  客户端Xaml前台代码如下:

    <Grid x:Name="LayoutRoot" Background="White" ShowGridLines="True">
        <TextBox Height="23" HorizontalAlignment="Left" Margin="20,20,0,0" 
                 Name="tbInput" VerticalAlignment="Top" Width="243" />
        <Button Content="发 送" Height="23" HorizontalAlignment="Left" 
                Margin="279,20,0,0" Name="button1" VerticalAlignment="Top" 
                Width="75" Click="button1_Click" />
        <sdk:Label Height="28" HorizontalAlignment="Left" Margin="20,57,0,0"
                   Name="lbShowMessage" VerticalAlignment="Top" Width="358" />
    </Grid>

  最后效果如下,如需源码请点击 SLSocket.zip 下载,本文演示的是最简单通讯效果:

 




说说Silverlight里的Socket

作为.Net Framework中的“古董级”类,Socket类无论在1.0,还是最新的3.5 sp1的网络编程里,都占据着极其重要的作用。优化网络架构,一直是开发人员追求的问题。在2.0时代,我们使用异步Socket(Begin、End等方法)来增强性能,不过,这类方法往往要初始化一个IAsyncResult的对象,这给.Net的GC带来了额外的负担。因此,从2.0 sp1起,.Net Framework的Socket类里又多了一种新的异步方式(Async),Silverlight从2.0起,支持通过Async Socket的方式和服务器通信。

Async Socket

首先让我们来看看使用Async方法和以往的使用Begin、End的方法有什么不同吧:

如果是Begin、End的方法,我们采用以下方法来Accept一个客户端:

Server.BeginAccept(new AsyncCallback(DoAcceptTcpClientCallBack), Server);

以及CallBack函数:

protected void DoAcceptTcpClientCallBack(IAsyncResult result)
{
    //获得当前Socket
    Socket listener = result.AsyncState as Socket;
    //远程Socket对象
    Socket client = null;

    try
    {
        //停止同步监听端口
        client = listener.EndAccept(result);
    }
    catch
    {
        return;
    }

    if (client == null)
        return;

    StateObject state = new StateObject(BufferSize);
    //开始异步接收远程Socket发来的数据
    client.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None,
        new AsyncCallback(DoBeginReceiveCallback), state);

}

如果采用Async方式,则相对简单不少,先初始化一个SocketAsyncEventArgs对象:

SocketAsyncEventArgs AcceptEventArgs = new SocketAsyncEventArgs();
AcceptEventArgs.Completed += OnAcceptCompleted;
Server.AcceptAsync(AcceptEventArgs);

然后处理Completed事件:

protected void OnAcceptCompleted(object sender, SocketAsyncEventArgs e)
{
    if (e.SocketError == SocketError.Success)
    {
        Socket client = e.AcceptSocket;
        SocketAsyncEventArgs args = new SocketAsyncEventArgs();
        args.SetBuffer(new Byte[10240], 0, 10240);
        args.Completed += OnClientReceive;
        client.ReceiveAsync(args);
        Server.AcceptAsync(AcceptEventArgs);
    }
}

SocketAsyncEventArgs

Async Socket的核心部分就是使用SocketAsyncEventArgs类,该类专为需要高性能的网络服务器应用程序而设计。应用程序可以完全使用增强的异步模式,也可以仅仅在目标热点区域(例如,在接收大量数据时)使用此模式。

这些增强功能的主要特点是可以避免在异步套接字 I/O 量非常大时发生重复的对象分配和同步。当前由Socket 类实现的开始/结束设计模式要求为每个异步套接字操作分配一个 IAsyncResult 对象。

SocketAsyncEventArgs类有一个Completed事件,一切和Async Socket有关的操作结素后,都将调用该事件的处理函数。例如在上面的例子中,我们使用OnAcceptCompleted方法来处理Accept后的结果。通常我们使用此类执行异步套接字操作的模式包含以下步骤:

  1. 分配一个新的 SocketAsyncEventArgs 上下文对象,或者从应用程序池中获取一个空闲的此类对象。
  2. 将该上下文对象的属性设置为要执行的操作(例如,完成回调方法、数据缓冲区、缓冲区偏移量以及要传输的最大数据量)。
  3. 调用适当的套接字方法 (xxxAsync) 以启动异步操作。
  4. 如果异步套接字方法 (xxxAsync) 返回 true,则在回调中查询上下文属性来获取完成状态。
  5. 如果异步套接字方法 (xxxAsync) 返回 false,则说明操作是同步完成的。可以查询上下文属性来获取操作结果。
  6. 将该上下文重用于另一个操作,将它放回到应用程序池中,或者将它丢弃。

可以使用同一个SocketAsyncEventArgs来处理Accept(服务器)、Connect(客户端)、Send、Receive的结果,SocketAsyncEventArgs有一个LastOperation的属性来标注该Completed是哪种操作的结果:

if (e.LastOperation == SocketAsyncOperation.Connect)
{
    //…………
}
else if (e.LastOperation == SocketAsyncOperation.Receive)
{
    //…………
}
else if (e.LastOperation == SocketAsyncOperation.Send)
{
    //…………
}

当然,也可以为不同的操作使用不同的SocketAsyncEventArgs对象。

可以在事件处理函数中通过观察SocketError属性来判断调用结果,当

e.SocketError == SocketError.Success

表明调用成功。

通过调用SocketAsyncEventArgs的SetBuffer方法,来设置需要发送的数据,或指定在接收数据时,的缓存数组。例如在上面例子里,我们为args设置了一个10240字节的数组,来存放接收到的数据:

args.SetBuffer(new Byte[10240], 0, 10240);

在Silverlight里使用Async Socket

在Silverlight里使用Async Socket,除了某些方法不支持外,大体上和完整版的.Net类似。不过,也有一些小小的限制:

  1. 网络应用程序可以连接到的端口范围必须在 4502-4534 范围内。这些是使用Socket从Silverlight应用程序进行连接所唯一允许使用的端口。如果连接的目标端口不在此端口范围内,则尝试连接时将会失败。
  2. Silverlight 运行时中的安全策略系统要求必须先从网络资源下载一个策略文件,之后才允许网络连接访问该资源。源站点和跨域网络访问都因此而受到影响。

对于上述第二点,我们使用以下形式的策略文件:

<?xml version="1.0" encoding ="utf-8"?>
<access-policy>
    <cross-domain-access>
        <policy>
            <allow-from>
                <domain uri="file:///" />
            </allow-from>
            <grant-to>
                <socket-resource port="4502-4506" protocol="tcp" />
            </grant-to>
        </policy>
    </cross-domain-access>
</access-policy>

可以在服务器上架设一个Server服务,监听943接口。Silverlight通过Socket访问服务器资源时,首先连接到服务器地址的943端口,然后下载该策略文件。

封装好的AsyncClient类

以下是我写的一个可在Silverlight中使用的Async Socket类,用于虚拟实验室里的Socket操作。和普通的Socket不同,该类在发送数据包时,在数据包头上添加4字节,作为数据包的长度;接收数据包时,同样的先读取前4字节,作为本次接收的长度。:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Net;
using System.ComponentModel;

namespace Newinfosoft.Net.Sockets
{
    public class AsyncClient : INotifyPropertyChanged
    {
        #region Events
        /// <summary>
        /// 当接收到数据时
        /// </summary>
        public event AsyncConnectionEventHandler DataRecieved;
        /// <summary>
        /// 当数据发送完毕时
        /// </summary>
        public event AsyncConnectionEventHandler DataSend;
        /// <summary>
        /// 当连接服务器成功时
        /// </summary>
        public event AsyncConnectionEventHandler Connected;
        /// <summary>
        /// 当属性改变时(例如是否连接属性)
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion

        /// <summary>
        /// 用于存放接收时临时数据的内部类
        /// 假设服务器发送来的数据格式为:
        /// |数据长度n|数据本生
        ///    4字节    n字节
        /// </summary>
        protected class StateObject
        {
            public byte[] Buffer;
            /// <summary>
            /// 还有多少字节的数据没有接收
            /// </summary>
            public int RemainSize = 0;

            public MemoryStream Stream = null;

            public StateObject(int bufferSize)
            {
                Buffer = new byte[bufferSize];
            }

            ~StateObject()
            {
                if (Stream != null)
                {
                    Stream.Close();
                    Stream.Dispose();
                }
            }
        }

        /// <summary>
        /// 客户端Socket对象
        /// </summary>
        public Socket Client { get; private set; }

        #region 异步SocketAsyncEventArgs
        public SocketAsyncEventArgs SendEventArgs { get; private set; }
        public SocketAsyncEventArgs ReceiveEventArgs { get; private set; }
        public SocketAsyncEventArgs ConnectEventArgs { get; private set; }
        #endregion

        /// <summary>
        /// 读取或设置接收时的StateObject
        /// </summary>
        protected StateObject State { get; set; }

        /// <summary>
        /// 发送锁,只有当前一个包中的数据全部发送完时,才允许发送下一个包
        /// </summary>
        protected object m_lockobject = new object();
        protected ManualResetEvent SendResetEvent = new ManualResetEvent(false);

        #region IsConnecting
        protected bool m_IsConnecting = false;
        /// <summary>
        /// 读取或设置是否连接到远程服务器,可用于绑定操作
        /// </summary>
        public bool IsConnecting
        {
            get
            {
                return m_IsConnecting;
            }
            set
            {
                if (m_IsConnecting != value)
                {
                    m_IsConnecting = value;
                    OnPropertyChanged("IsConnecting");
                }
            }
        }
        #endregion

        /// <summary>
        /// 通过指定的IPAddress和Port创建AsyncClient,需要调用Connect方法连接
        /// </summary>
        /// <param name="bufferSize">接收缓存大小</param>
        public AsyncClient(int bufferSize)
        {
            State = new StateObject(bufferSize);

            SendEventArgs = new SocketAsyncEventArgs();
            ReceiveEventArgs = new SocketAsyncEventArgs();
            ReceiveEventArgs.Completed += OnReceiveCompleted;
            SendEventArgs.Completed += OnSendCompleted;

            IsConnecting = false;
        }

        /// <summary>
        /// 将已有的Socket包装为AsyncClient对象,
        /// 如果Socket没有连接,则需要调用Connect方法
        /// </summary>
        /// <param name="socket">Socket对象</param>
        /// <param name="bufferSize">接收缓存的大小</param>
        public AsyncClient(Socket socket, int bufferSize)
        {
            State = new StateObject(bufferSize);            

            SendEventArgs = new SocketAsyncEventArgs();
            ReceiveEventArgs = new SocketAsyncEventArgs();
            ReceiveEventArgs.Completed += OnReceiveCompleted;
            SendEventArgs.Completed += OnSendCompleted;

            this.Client = socket;
            if (socket != null && socket.Connected)
            {
                ReceiveEventArgs.SetBuffer(State.Buffer, 0, State.Buffer.Length);
                Client.ReceiveAsync(ReceiveEventArgs);
                IsConnecting = true;
            }
            else
            {
                IsConnecting = false;
            }
        }

        /// <summary>
        /// 连接
        /// </summary>
        /// <param name="address">IP地址</param>
        /// <param name="port">端口号</param>
        public void Connect(String address, int port)
        {
            if (Client == null)
            {
                Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            }
            if (!Client.Connected)
            {
                ConnectEventArgs = new SocketAsyncEventArgs();
                ConnectEventArgs.Completed += OnConnectComplete;
                EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Parse(address), port);
                ConnectEventArgs.RemoteEndPoint = remoteEndPoint;
                Client.ConnectAsync(ConnectEventArgs);
            }
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="data">需要发送的数据</param>
        public void Send(Byte[] data)
        {
            Send(data, 0, data.Length);
        }

        /// <summary>
        /// 发送数据,按照以下格式发送数据:
        /// |数据长度n|需要发送的数据
        ///    4字节        n字节
        /// </summary>
        /// <param name="data">需要发送的数据</param>
        /// <param name="offset">需要发送数据的偏移量</param>
        /// <param name="size">需要发送数据的长度</param>
        public void Send(Byte[] data, int offset, int size)
        {
            if (!IsConnecting)
            {
                throw new Exception("没有连接,无法发送数据!");
            }

            lock (m_lockobject)
            {
                if (data == null || data.Length == 0)
                    return;
                //计算数据的长度,并转换成字节数组,作为本次发送的头部
                byte[] length = BitConverter.GetBytes(size);
                Byte[] buffer = new Byte[size + length.Length];

                Array.Copy(length, 0, buffer, 0, length.Length);
                Array.Copy(data, offset, buffer, length.Length, size);
                //设置发送Buffer
                SendEventArgs.SetBuffer(buffer, 0, buffer.Length);

                SendResetEvent.Reset();
                Client.SendAsync(SendEventArgs);
                //等待发送成功的信息,只有收到该信息,才退出lock锁,
                //这样,确保只有当前面得数据发送完后,才发送下一段数据
                SendResetEvent.WaitOne();
            }
        }

        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        protected void OnConnectComplete(object sender, SocketAsyncEventArgs e)
        {
            if (e.SocketError == SocketError.Success)
            {
                IsConnecting = true;
                ReceiveEventArgs.SetBuffer(State.Buffer, 0, State.Buffer.Length);
                Client.ReceiveAsync(ReceiveEventArgs);
                if (Connected != null)
                {
                    Connected(this, new AsyncConnectionEventArgs(null, this));
                }
            }
            else
            {
                IsConnecting = false;
            }
        }

        void OnSendCompleted(object sender, SocketAsyncEventArgs e)
        {
            //如果传输的数据量为0,则表示链接已经断开
            if (e.BytesTransferred == 0)
            {
                Client.Close();
            }
            else
            {

                if (DataSend != null)
                {
                    AsyncConnectionEventArgs se = new AsyncConnectionEventArgs(e.Buffer, this);
                    DataSend(this, se);
                }
                //通知数据发送完毕
                SendResetEvent.Set();
            }
        }

        /// <summary>
        /// 接收数据处理函数
        /// 1、将收到的数据包中的前4字节转换成Int32类型,作为本次数据包的长度。
        /// 2、将这个值设置成StateObject的RemainSize。
        /// 3、将数据包中剩下的数据写入StateObject的MemoryStream,并减少相应的RemainSize值。
        /// 4、直到RemainSize=0时,表示这一段数据已经接收完毕,从而重复1,开始下一段数据包的接收
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnReceiveCompleted(object sender, SocketAsyncEventArgs e)
        {
            //如果传输的数据量为0,则表示链接已经断开
            if (e.BytesTransferred == 0)
            {
                Client.Close();
            }
            else
            {
                int position = 0;

                while (position < e.BytesTransferred)
                {
                    if (State.RemainSize > 0)
                    {
                        int bytesToRead = State.RemainSize > e.BytesTransferred - position ?
                            e.BytesTransferred - position : State.RemainSize;
                        State.RemainSize -= bytesToRead;
                        State.Stream.Write(State.Buffer, position, bytesToRead);
                        position += bytesToRead;

                        if (State.RemainSize == 0)
                        {
                            if (DataRecieved != null)
                            {
                                AsyncConnectionEventArgs ce =
                                    new AsyncConnectionEventArgs(State.Stream.ToArray(), this);
                                DataRecieved(this, ce);
                            }
                            State.Stream.Dispose();
                        }

                    }
                    else
                    {
                        State.RemainSize = BitConverter.ToInt32(State.Buffer, position);
                        State.Stream = new MemoryStream(State.RemainSize);
                        position += 4;
                    }
                }
                //重新设置数据缓存区
                e.SetBuffer(State.Buffer, 0, State.Buffer.Length);
                Client.ReceiveAsync(e);
            }
        }
    }
}

使用方式如下:

AsyncClient client= new AsyncClient(10240);
client.Connected += OnClientConnected;
client.DataRecieved += OnClientDataRecieved;
client.Connect("127.0.0.1", 4503);

小节

本文介绍了在Silverlight中,如何使用Async Socket来连接服务器,以及如何编写跨域访问的配置文件。最后,给出了一个适用于Silverlight的类AsyncClient,封装了Async Socket方法。注意,该类在每次发送数据时,需要在数据的前面发送该段数据的长度,因此,只能适用于采用类似方式编写的Server对象:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.IO;

namespace Newinfosoft.Net.Sockets
{
    /// <summary>
    /// ConnectionServer在.Net 2.0 sp1上的实现
    /// </summary>
    public class AsyncServer
    {
        #region events
        /// <summary>
        /// 接收到数据
        /// </summary>
        public event AsyncConnectionEventHandler DataRecieved;
        public event AsyncConnectionEventHandler DataSend;
        /// <summary>
        /// 远程Socket连接
        /// </summary>
        public event AsyncConnectionEventHandler RemoteSocketAccept;
        /// <summary>
        /// 开始监听端口
        /// </summary>
        public event EventHandler Started;
        /// <summary>
        /// 停止监听端口
        /// </summary>
        public event EventHandler Stoped;

        #endregion

        protected class StateObject
        {
            public Socket ClientSocket = null;
            public byte[] Buffer;
            public int RemainSize = 0;
            public MemoryStream Stream = null;

            public StateObject(int bufferSize)
            {
                Buffer = new byte[bufferSize];
            }

            ~StateObject()
            {
                Stream.Close();
                Stream.Dispose();
            }
        }

        /// <summary>
        /// 读取TCP监听服务
        /// </summary>
        public Socket Server
        {
            get;
            private set;
        }

        /// <summary>
        /// 读取当前服务的端口号
        /// </summary>
        public int Port
        {
            get;
            private set;
        }

        /// <summary>
        /// 读取或设置是否正在监听端口
        /// </summary>
        public bool IsListening
        {
            get;
            set;
        }

        /// <summary>
        /// 读取或设置缓存大小
        /// </summary>
        public int BufferSize
        {
            get;
            set;
        }

        public SocketAsyncEventArgs AcceptEventArgs { get; set; }

        #region 构造函数

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="port">监听端口</param>
        public AsyncServer(int port)
            : this(port, 20480)
        {
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="port">监听端口</param>
        /// <param name="bufferSize">缓存大小</param>
        public AsyncServer(int port, int bufferSize)
        {
            Port = port;
            BufferSize = bufferSize;
        }

        #endregion

        public void Start()
        {
            try
            {
                if (Server == null)
                {
                    IPEndPoint localEP = new IPEndPoint(IPAddress.Any, Port);
                    Server = new Socket(localEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                    Server.Bind(localEP);
                }
                Server.Listen(int.MaxValue);
                IsListening = true;
                if (Started != null)
                {
                    Started(this, new EventArgs());
                }
                AcceptEventArgs = new SocketAsyncEventArgs();
                AcceptEventArgs.Completed += OnAcceptCompleted;
                Server.AcceptAsync(AcceptEventArgs);

            }
            catch (SocketException ex)
            {
                throw ex;
            }
        }

        public void Stop()
        {
            IsListening = false;

            if (Server != null)
            {
                Server.Close();
                Server = null;
                if (Stoped != null)
                {
                    Stoped(this, new EventArgs());
                }
            }
        }

        #region Accept

        protected void OnAcceptCompleted(object sender, SocketAsyncEventArgs e)
        {
            if (e.AcceptSocket.RemoteEndPoint != null)
            {
                AsyncClient client = new AsyncClient(e.AcceptSocket, BufferSize);

                client.DataRecieved += (s2, e2) =>
                {
                    if (DataRecieved != null)
                    {
                        DataRecieved(this, e2);
                    }
                };

                client.DataSend += (s2, e2) =>
                {
                    if (DataSend != null)
                    {
                        DataSend(this,e2);
                    }
                };

                //抛出远程Socket连接事件
                if (RemoteSocketAccept != null)
                {
                    RemoteSocketAccept(this, new AsyncConnectionEventArgs(null, client));
                }
                e.AcceptSocket = null;
                Server.AcceptAsync(AcceptEventArgs);
            }
        }
        #endregion
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值