代码之家  ›  专栏  ›  技术社区  ›  Matt Ellen Bipin Vayalu

另一端关闭时套接字未注册

  •  2
  • Matt Ellen Bipin Vayalu  · 技术社区  · 16 年前

    我已经写了一个测试,我认为应该测试这个系统是否工作,但是我正在测试的对象似乎没有注册套接字是否关闭,除非我输入一个断点然后继续。

    测试的重要部分如下所示:

    StreamingMonitor sm = new StreamingMonitor();
    bool errored = false;
    string msg = "";
    sm.ErrorOccurred += (s, a) =>
    {
        errored = true;
        msg = a.Exception.Message;
    };
    sm.Enabled = true;
    client = listener.AcceptTcpClient();
    client.GetStream().Write(BitConverter.GetBytes(10000), 0, 4);
    client.Close();
    while(!errored)
    {}
    Assert.AreEqual("A request to send or receive data was disallowed because the socket had already been shut down in that direction with a previous shutdown call", msg);
    

    TcpListener 对象 listener 正在侦听环回地址。

    这个 StreamingMonitor 启用时开始侦听要检索的数据长度。数据的长度总是假定适合一个有符号的32位整数。

    当接收到消息长度时,将调用此方法。

        private void GotMessageLength(IAsyncResult asyncResult)
        {
            try
            {
                client.Client.EndReceive(asyncResult);
                if(firstMessage)
                {
                    firstMessage = false;
                    if (Connected != null)
                    {
                        Connected(this, new EventArgs());
                    }
                }
                int msgLen = BitConverter.ToInt32(messageLength, 0);
                byte[] message = new byte[msgLen];
                List<byte> lbMessage = new List<byte>();
                int bytesReturned = client.Client.Receive(message);
                int remaining = (msgLen < bytesReturned) ? bytesReturned - msgLen : msgLen - bytesReturned;
                if(remaining > 0)
                {
                    if (bytesReturned > 0)
                    {
                        for (int i = 0; i < bytesReturned; i++)
                        {
                            lbMessage.Add(message[i]);
                        }
                    }
                    while(remaining > 0)
                    {
                        if(!client.Connected)
                        {
                            throw new SocketException((int)SocketError.Shutdown);
                        }
                        bytesReturned = client.Client.Receive(message);
                        remaining = (remaining < bytesReturned) ? bytesReturned - remaining : remaining - bytesReturned;
                        if (bytesReturned > 0)
                        {
                            for (int i = 0; i < bytesReturned; i++)
                            {
                                lbMessage.Add(message[i]);
                            }
                        }
                    }
                    message = lbMessage.ToArray();
                }
                MessageReceived(this, new MessageReceivedEventArgs(message));
                if (Enabled)
                {
                    client.Client.BeginReceive(messageLength, 0, 4, SocketFlags.None, GotMessageLength, null);
                }
            }
            catch (SocketException ex)
            {
                if(ErrorOccurred != null)
                {
                    ErrorOccurred(this, new ErrorEventArgs(ex));
                }
            }
            catch (ObjectDisposedException)
            {
    
            }
        }
    

    但是,单元测试会陷入无限循环中,等待错误发生,因为 流监视器 从未意识到另一端已经关闭。

    SteamingMonitor 意识到服务器不见了吗?

    这在环回地址上可能吗?

    对不起,所有的代码,我无法思考如何削减方法。

    1 回复  |  直到 16 年前
        1
  •  3
  •   Markus Andersson    16 年前

    环回(或仅仅使用localhost)通常与实际网络的行为方式不同。类似于在每次调用socketapi时发送/接收多少数据的场景。所以一定要测试或真实的网络连接。

    socketapi只会在尝试发送到另一端时发现另一端是否断开连接(我认为这是正确的)。所以某种心跳功能就派上用场了=)

    Edit:您还可以获取SocketException,通过尝试接收来确定对方是否断开了连接(对我的一些旧代码进行了一些基本测试)。

    protected void ReceiveCallback(IAsyncResult ar)
    {
         var so = (StateObject)ar.AsyncState;
         if (!so.Socket.Connected) return;
    
         try
         {
             int read = so.Socket.EndReceive(ar);
             if (read > 0)
             ProcessBuffer(so, so.Buffer, read);
    
             so.Socket.BeginReceive(so.Buffer, 0, so.Buffer.Length, SocketFlags.None, ReceiveCallback, so);
          }
          catch (SocketException e)
          {
              Trace.WriteLine("[Networking]::NetBase.ReceiveCallback: SocketException");
              Output.WriteLine(e.Message);
              RaiseDisconnected();
          }
          catch (ObjectDisposedException e)
          {
              Trace.WriteLine("[Networking]::NetBase.ReceiveCallback: ObjectDisposedException");
              Output.WriteLine(e.Message);
              RaiseDisconnected();
           }
    }
    

    如果另一方由于某种原因崩溃,这将调用我的断开连接函数。 希望有帮助