NCindy

.net平台上的高性能网络程序开发框架

.NET Sockets I/O模型

word里面的文本贴进来,格式又乱了,大家将就看吧。等全文写完后,我会贴出PDF文档的。

1.1   
.NET Sockets I/O
模型

.NET Sockets I/O模型有三种:1.阻塞式I/O2.选择(SelectI/O3.异步I/O

 

1.1.1    阻塞式I/O模型

正如阻塞式I/O模型的名字,这种I/O模型在进行I/O操作时会阻塞调用者线程。阻塞式I/O是最简单最直接的I/O模型,使用阻塞式I/O模型时,用户按照顺序建立连接、发送数据、接收数据……每一步操作都需要进入等待状态。

一个典型的使用阻塞I/O模型的代码段如下所示(为了简单起见,我省略了异常处理)

using System;

using System.Text;

using System.Net.Sockets;

 

namespace NCindy.Articles.Samples

{

    public class BlockingIODemo

    {

        static void Main(string[] args)

        {

            Console.WriteLine("Begin.");

            try

            {

                Demo();

            }

            catch (Exception e)

            {

                Console.WriteLine(e);

            }

            Console.WriteLine("End.");

        }

 

 

        static void Demo()

        {

            //创建一个TCP socket

            Socket sock = new Socket(

                AddressFamily.InterNetwork, //地址族为IPv4

                SocketType.Stream, //Socket类型为流类型,UDP为数据报类型(Dgram)

                ProtocolType.Tcp);

 

            //建立连接

            sock.Connect("talk.google.com", 5222);

 

            byte [] sendBuffer = Encoding.Default.GetBytes("Hello world.");

 

            //发送数据

            int sentBytes = sock.Send(sendBuffer);

 

            Console.WriteLine("Sent bytes: " + sentBytes);

           

            //分配一个k的接收缓冲区

            byte [] recvBuffer = new byte[1024 * 4];

 

            //接收数据

            int recvBytes = sock.Receive(recvBuffer);

 

            //将收到的字节数组转化成对象

            string recvString = Encoding.Default.GetString(recvBuffer, 0, recvBytes);

 

            Console.WriteLine("Received data: \r\n-----\r\n" + recvString);

 

            //停止Socket的接收和发送

            sock.Shutdown(SocketShutdown.Both);

           

            //关闭Socket,释放系统资源

            sock.Close();

        }

    }

}

这段代码按顺序做了这么几样工作:1.创建Socket对象;2.连接到远端服务;3.发送数据;4.接收数据。并且在整个执行过程中,阻塞了主线程的执行,直到Demo方法执行完成Main方法才继续执行。

与其它两种I/O模型相比,阻塞式I/O模型最大的优点在于使用简单,符合直觉的思维方式。缺点也很明显,每个线程只能同时为一个连接服务,如果有大量连接,就要启动大量线程,而大量线程的上下文切换对系统效率会是不小的影响。当然,阻塞式I/O模型配合线程池机制,在处理连接数量较少的情况下也可以达到非常理想的效率。

 

 

1.1.2    选择(SelectI/O模型

在阻塞式I/O模型中,每个线程同时只能处理一个连接。如果需要同时处理多个连接则需要多个线程。为了在一个线程中同时处理多个Socket连接,我们可以使用Select模型。这个模型主要思路为:使用SocketSelect静态方法来从多个Sockets中将可以进行某种操作的Sockets查询出来然后逐个进行处理。

一个典型的使用选择I/O模型的代码段如下所示(为了简单起见,我省略了异常处理)

using System;

using System.Net.Sockets;

using System.Collections;

using System.Net;

using System.IO;

using System.Threading;

 

namespace NCindy.Articles.Samples

{

    class SelectIOSample

    {

        static bool stopEchoServer = false;

        static void Main(string[] args)

        {

            Console.WriteLine("Begin");

            Thread thread = new Thread(new ThreadStart(EchoServerThreadProc));

            thread.Start();

 

            Console.WriteLine("Press 'Enter' to quit.");

            Console.ReadLine();

           

            stopEchoServer = true;

 

            //等待DoEchoServer线程退出

            thread.Join(2000);

            Console.WriteLine("End");

        }

 

        private static void EchoServerThreadProc()

        {

            EchoServer echoServer = new EchoServer();

            try

            {

                echoServer.Start();

                while (!stopEchoServer)

                {

                    //选择出可以进行读操作的Sockets进行处理

                    echoServer.ProcessRead();

 

                    //选择出可以进行写操作的Sockets进行处理

                    echoServer.ProcessWrite();

                }

            }

            catch (Exception e)

            {

                Console.WriteLine(e);

            }

            finally

            {

                echoServer.Stop();

            }

        }

    }

 

    internal class ClientData

    {

        private MemoryStream memStream = new MemoryStream();

       

 

        public void Write(byte[] data, int count)

        {

            memStream.Write(data, 0, count);

        }

 

        public byte[] Read()

        {

            byte[] buf = new byte[memStream.Position];

 

            int count = (int)memStream.Position;

            memStream.Position = 0;

            memStream.Read(buf, 0, count);

            memStream.Position = 0;

 

            return buf;

        }

    }

 

    internal class EchoServer

    {

        private readonly Socket listener;

        private readonly ArrayList clients;

        private readonly Hashtable clientsDataMap;

 

        public EchoServer()

        {

            clients = new ArrayList();

            clientsDataMap = new Hashtable();

            listener = new Socket(AddressFamily.InterNetwork,

                SocketType.Stream,

                ProtocolType.Tcp);

        }

 

        public void Start()

        {

            //监听本机所有IP的端口

            listener.Bind(new IPEndPoint(IPAddress.Any, 6666));

 

            //开始进行监听

            //backlog是指最大允许的未决连接数量。

            //未决连接是指服务器已经接受到远端的连接请求,但是应用还没有调用accept接受的连接

            //如果未决连接超过这个backlog指定的数量,接下来连接会被直接拒绝

            listener.Listen(200); //

           

        }

 

        public void Stop()

        {

            //关闭监听socket

            listener.Close();

 

            //关闭所有的客户端sockets

            foreach (Socket s in this.clients)

            {

                s.Shutdown(SocketShutdown.Both);

                s.Close();

            }

        }

 

 

        private void ProcessError(ArrayList errorList)

        {

           

            if (errorList.Count == 0)

            {

                return;

            }

           

            foreach (Socket s in errorList)

            {

                Console.WriteLine(string.Format("{0} error.", s.RemoteEndPoint));

                this.clients.Remove(s);

            }

        }

 

        public void ProcessWrite()

        {

            ArrayList errorList = new ArrayList(this.clients);

            ArrayList writeList = new ArrayList(this.clients);

            if (writeList.Count == 0)

            {

                return;

            }

 

            int begin = Environment.TickCount;

            Socket.Select(null, writeList, errorList, 1000 * 1000);

            int end = Environment.TickCount;

 

            System.Diagnostics.Trace.WriteLine(string.Format("select write use {0}ms", end - begin));

            this.ProcessError(errorList);

 

            foreach (Socket s in writeList)

            {

                try

                {

                    if (this.clientsDataMap.ContainsKey(s))

                    {

                        ClientData cd = this.clientsDataMap[s] as ClientData;

 

                        byte[] sendBuf = cd.Read();

 

                        s.Send(sendBuf);

                    }

                }

                catch (Exception e)

                {

                    Console.WriteLine(e);

                }

            }

        }

 

        public void ProcessRead()

        {

            try

            {

                ArrayList errorList = new ArrayList(this.clients);

                ArrayList readList = new ArrayList(this.clients);

                readList.Add(this.listener); //Listener Socket的读操作相当于有尚未accept的连接进入

 

                Socket.Select(readList, null, errorList, 1000 * 1000);

 

                this.ProcessError(errorList);

 

                //分配接收缓冲

                byte[] recvBuffer = new byte[1024];

 

                foreach (Socket s in readList)

                {

                    //如果listener可以读,说明可以无阻塞的执行accept

                    if (s == this.listener)

                    {

                        Socket incomingSocket = s.Accept();

                       

                        Console.WriteLine(string.Format("{0} connected.",

                            incomingSocket.RemoteEndPoint));

 

                        this.clients.Add(incomingSocket);

                    }

                    else

                    {

                        int recvCount = s.Receive(recvBuffer);

 

                        //如果接收到的字节数为,则为远端连接shutdown

                        if (recvCount == 0)

                        {

                            Console.WriteLine(string.Format("{0} disconnected.",

                                s.RemoteEndPoint));

 

                            this.clients.Remove(s);

                        }

 

                        //取出连接对应的客户数据对象

                        ClientData cd;

                        if (this.clientsDataMap.ContainsKey(s))

                        {

                            cd = this.clientsDataMap[s] as ClientData;

                        }

                        else

                        {

                            cd = new ClientData();

                            this.clientsDataMap.Add(s, cd);

                        }

 

                        //将读取到的数据放入客户数据对象中,等待可以发送的时候发送

                        cd.Write(recvBuffer, recvCount);

                    }

                }

            }

            catch (Exception e)

            {

                Console.WriteLine(e);

            }

        }

    }

}

 

1.1.3    异步I/O模型

异步I/O模型是.NET中最有效的I/O模型,因为在Windows NT 4和以上的系统中.NET异步I/O在内部使用了IO完成端口。

 To be continued...

posted on 2006-10-31 18:40  iceboundrock  阅读(2927)  评论(7编辑  收藏  举报

导航