深入讨论.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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值