首页 > 深入讨论.NET Socket的Accept方法

深入讨论.NET Socket的Accept方法

    深入讨论.NET  SocketAccept方法

考虑一个问题,假如同时有50个连接请求进入一个服务器(这种情况对于普通负载的Web服务器都是很常见的)会怎么样?阻塞式I/O只能循环调用Accept,一个一个对50个连接进行Accept操作,而选择模型也是一样。异步模型呢?假如我们预先发起了100BeginAccept操作,异步模型能够同时处理50个连接么?MSDN没有回答这个问题,我们只有向.NET framework的代码来寻求解答了。

打开Reflector工具,找到Socket类型的BeginAccept方法,这个方法有三种overload,我们首先来看比较简单的一种(由于是用Reflector工具反编译的,代码的局部变量名无意义):

        public IAsyncResult BeginAccept(AsyncCallback callback, object state)

        {

            if (this.CanUseAcceptEx)

            {

                return this.BeginAccept(0, callback, state);

            }

            if (Logging.On)

            {

                Logging.Enter(Logging.Sockets, this, "BeginAccept", "");

            }

            if (this.CleanedUp)

            {

                throw new ObjectDisposedException(base.GetType().FullName);

            }

            AcceptAsyncResult result1 = new AcceptAsyncResult(this, state, callback);

            result1.StartPostingAsyncOp(false);

            this.DoBeginAccept(result1);

            result1.FinishPostingAsyncOp(ref this.Caches.AcceptClosureCache);

            if (Logging.On)

            {

                Logging.Exit(Logging.Sockets, this, "BeginAccept", result1);

            }

            return result1;

        }

 

函数的前三行首先判断,是否可以直接使用AcceptEx,如果可以则调用BeginAccept的第三种overload方式。这里要说明两点,1.为什么要用AcceptEx2.为什么不总是用AcceptEx而是要有条件的使用。第一个问题是因为在Winsock2种,AcceptEx的速度快过accept方法(MSDN是这么说的:A program can make a connection to a socket more quickly using AcceptEx instead of the accept function.)。第二个问题是因为AcceptExWinsock2的扩展函数,Win98和以下的版本都不支持这个函数。

放下直接调用第三种overload方式的函数不提,我们先继续往下看。接下来的三行代码是做log的,再接下来的三行代码是检查Socket对象是否已经被Disposed了。

接下来的两行代码首先构造一个AsyncResult的实例,然后设定AsyncResultStartPostingAsyncOp(开始投递异步操作)属性为false。终于,我们到了最关键的地方执行实际的DoBeginAccept操作。用Reflector打开这个方法,我们看到这个方法最关键的思路是把Socket设置到非阻塞模式,然后将应用程序投递的Accept请求放入Accept队列中,为了避免阻塞,.NET framework使用WSAEventSelect和线程池共同配合,一旦有新连接进入,WSAEventSelect会触发Socket.AcceptCallback方法的执行,逐个处理AcceptQueue中的Accept请求。因为此时Socket处于非阻塞模式,无论是否有未决的连接,Accept都会会立即返回,在没有未决连接的情况下,Accept会返回一个WouldBlock的错误代码。如果出现了这样的错误,就继续等待下一次的WSAEventSelect事件触发。讨论了这么长,大家应该对public IAsyncResult BeginAccept(AsyncCallback callback, object state)的原理清楚了吧,这种overload方式依然未能解决逐个调用Accept来处理未决连接的问题,虽然比阻塞式和选择式I/O有所改善,但是在不能调用AcceptEx的情况下,性能依然会有所损失。那么让我们继续来看看另外两种BeginAccept的情况吧。

 public IAsyncResult BeginAccept(int receiveSize, AsyncCallback callback, object state)

 {

       return this.BeginAccept(null, receiveSize, callback, state);

 }

这个overload形式直接调用了第三种,我们也直接来分析第三种BeginAccept

public IAsyncResult BeginAccept(Socket acceptSocket,

 int receiveSize,

 AsyncCallback callback,

 object state)

{

    if (Logging.On)

    {

        Logging.Enter(Logging.Sockets, this, "BeginAccept", "");

    }

    if (this.CleanedUp)

    {

        throw new ObjectDisposedException(base.GetType().FullName);

    }

    if (receiveSize < 0)

    {

        throw new ArgumentOutOfRangeException("size");

    }

AcceptOverlappedAsyncResult result1 =

new AcceptOverlappedAsyncResult(this, state, callback);

    result1.StartPostingAsyncOp(false);

    this.DoBeginAccept(acceptSocket, receiveSize, result1);

    result1.FinishPostingAsyncOp(ref this.Caches.AcceptClosureCache);

    if (Logging.On)

    {

        Logging.Exit(Logging.Sockets, this, "BeginAccept", result1);

    }

    return result1;

}

 

前面的几行代码依旧是log、检查Socket状态和参数有效性,我们直接来分析最核心的DoBeginAccept(acceptSocket, receiveSize, result1)方法。使用Reflector查看这个DoBeginAccept,它比较简单我把代码贴出来,

 1 private void DoBeginAccept(Socket acceptSocket, int receiveSize,

                           AcceptOverlappedAsyncResult asyncResult)

 2 {

 3       int num2;

 4       if (!ComNetOS.IsWinNt)

 5       {

 6             throw new PlatformNotSupportedException(SR.GetString("WinNTRequired"));

 7       }

 8       if (this.m_RightEndPoint == null)

 9       {

10             throw new InvalidOperationException(SR.GetString("net_sockets_mustbind"));

11       }

12       if (!this.isListening)

13       {

14             throw new InvalidOperationException(SR.GetString("net_sockets_mustlisten"));

15       }

16       if (acceptSocket == null)

17       {

18             acceptSocket = new Socket(this.addressFamily, this.socketType, this.protocolType);

19       }

20       else if (acceptSocket.m_RightEndPoint != null)

21       {

22             throw new InvalidOperationException(SR.GetString("net_sockets_namedmustnotbebound"

                        new object[] { "acceptSocket" }));

23       }

24       asyncResult.AcceptSocket = acceptSocket;

25       int num1 = this.m_RightEndPoint.Serialize().Size + 0x10;

26       byte[] buffer1 = new byte[receiveSize + (num1 * 2)];

27       asyncResult.SetUnmanagedStructures(buffer1, num1);

28       SocketError error1 = SocketError.Success;

29       if (!UnsafeNclNativeMethods.OSSOCK.AcceptEx(this.m_Handle, 

                      acceptSocket.m_Handle,

                      Marshal.UnsafeAddrOfPinnedArrayElement(asyncResult.Buffer, 
0),

                      receiveSize,

                      num1, num1, 
out num2, asyncResult.OverlappedHandle))

30       {

31             error1 = (SocketError) Marshal.GetLastWin32Error();

32       }

33       error1 = asyncResult.CheckAsyncCallOverlappedResult(error1);

34       if (error1 != SocketError.Success)

35       {

36             SocketException exception1 = new SocketException(error1);

37             this.UpdateStatusAfterSocketError(exception1);

38             if (Logging.On)

39             {

40                   Logging.Exception(Logging.Sockets, this"BeginAccept", exception1);

41             }

42             throw exception1;

43       }

44 }

45 

46  

47 





前面是一堆参数有效性检查、对象状态检查、操作系统环境有效性检查的代码。接着初始化了接收数据缓冲区,调用AcceptEx方法,处理错误,直截了当。也就是说,对于这个overload方式,BeginAccept不是自己逐个处理未决连接,而是交给了操作系统内核来完成,会有更好的效率。并且,这个方法的第一个参数,使我们有可能重用Socket对象,由于创建Socket对象是一个相对比较耗时的操作,所以在需要处理大量连接的服务器程序中,能够重用Socket对象更加降低了系统消耗。

 

转载于:https://www.cnblogs.com/ncindy/archive/2006/11/01/546828.html

更多相关:

  • #include #include #include #include #include #include #include

  • 题目:表示数值的字符串 请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100"、"5e2"、"-123"、"3.1416"、"0123"及"-1E-16"都表示数值,但"12e"、"1a3.14"、"1.2.3"、"+-5"及"12e+5.4"都不是。 解题: 数值错误的形式有多种多样,但是正确的...

  • 加法伺候  //超过20位数值相加---------------------------------------- function bigNumAdd(a, b) {if (!(typeof a === "string" && typeof b === "string")) return console.log("传入参数必...

  • 业务场景: 从中文字句中匹配出指定的中文子字符串 .这样的情况我在工作中遇到非常多, 特梳理总结如下. 难点: 处理GBK和utf8之类的字符编码, 同时正则匹配Pattern中包含汉字,要汉字正常发挥作用,必须非常谨慎.推荐最好统一为utf8编码,如果不是这种最优情况,也有酌情处理. 往往一个具有普适性的正则表达式会简化程...

  • 简单record 一下 #include // 'struct sockaddr_in' #include #include // 'struct ifreq' and 'struct if_nameindex' #include #inc...

  • 菜鸟一枚,正在学习C++ Gui Qt4,整理很零碎,欢迎批评指正   1.窗口标题: QWidget *window = new QWidget; window->setWindowTitle("Enter Your Age"); **************************************** 关于标题...

  • 将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例: 输入:1->2->4, 1->3->4 输出:1->1->2->3->4->4 总体思路是: 比较两个链表头节点,较小的插入新链表指针之后,同时较小链表指针向后移动一位 实现如下: ListNode* mergeTwo...

  • 1.直接调用微软socket对象处理 static void Main(string[] args){try{IPAddress ip = new IPAddress(new byte[] { 127, 0, 0, 1 });//在3721端口新建一个TcpListener对象TcpListener listener = new...

  •   现在很多地方都会用到zookeeper, 用到它的地方就是为了实现分布式。用到的场景就是服务注册,比如一个集群服务器,需要知道哪些服务器在线,哪些服务器不在线。   ZK有一个功能,就是创建临时节点,当机器启动应用的时候就会连接到一个ZK节点,然后创建一个临时节点,那么通过获取监听该路径,并且获取该路径下的节点数量就知道有哪些服务...

  • 前台到后台java时data日期类型的转化 在实体类中用@DataTimeFormat,这样设置即使传过来是空的字符串也是可以转的,要和前面传过来的格式一致,如 @XmlElement(name="BeginDate") @DateTimeFormat(pattern="yyyy-MM-dd") private Date begin...