1

我正在寻求有关关闭侦听套接字的帮助。我遇到的问题是,当我Shutdown()之前调用时Close(),这两个调用之一似乎触发了对接收到的最后一个数据的套接字的虚假读取。请问我该如何阻止?

我的应用程序有两对套接字,就像 FTP 一样。一个是连接到远程服务器的客户端,另一个是侦听并接受来自远程主机的第二个连接的服务器。此入站连接连接到异步OnReceived以处理从远程主机传来的未经请求的数据。

这一切正常,两个套接字可以保持连接数天或数周。但如果出现问题,我的反应是关闭一切并重新开始。在调用其中一个inboundSocket.Shutdown()inbounSocket.Close()(不确定是哪个,很难在第二个线程中调试)期间,就好像我正在重新读取入站套接字上的最后一个入站数据包。这会导致更多的问题。

我怎么能关机。关闭、断开连接等而不强制重新读取?

下面的示例代码,被剥离以显示细节。

提前致谢。

丹尼尔

public class TcpIpSenderReceiver
{ 
    /// <summary>
    /// Main thread, main entry point 
    /// </summary>
    public void ConnectAndLogonAndStartReceivingInboundMessages()
    {   
        CreateListenerAndStartListening();
        AcceptInboundSocketAndHookUpDataReceiptCallBack();          
    }

    /// <summary>
    /// Main thread
    /// </summary>
    int CreateListenerAndStartListening()
    { 
        tcpListener = new TcpListener(LocalBoundIpAddress, listeningPort);
        tcpListener.Start();        
    }


    /// <summary>
    /// SECOND thread
    /// </summary>
    void AcceptInboundSocketAndHookUpDataReceiptCallBack()
    {
        int i = 0;
        while (i < 100 && !tcpListener.Pending())
        {
            i++;
            Thread.Sleep(100);
        }
        inboundSocket = tcpListener.AcceptSocket();
        bufferForReceive = new byte[bufferSize];
        WireUpCallbackForAsynchReceive();
    }

    /// <summary>
    /// SECOND thread
    /// </summary>
    void WireUpCallbackForAsynchReceive()
    {
        if (asynchCallbackFunctionForReceive == null)
        {
            asynchCallbackFunctionForReceive = new AsyncCallback(OnDataReceived);
        }
        if (inboundSocket.Connected)
        {
            try
            {
                asyncResultForReceive = inboundSocket.BeginReceive(bufferForReceive, 0, bufferForReceive.Length, SocketFlags.None, asynchCallbackFunctionForReceive, null);
            }
            catch (Exception)
            {
                //...
            }
        }
    }


    /// <summary>
    /// SECOND thread
    /// </summary>
    void OnDataReceived(IAsyncResult asyn)
    {
        // Read data call goes here.....

        if (asyncResultForReceive != null)
        {
            inboundSocket.EndReceive(asyncResultForReceive);  
        }
        WireUpCallbackForAsynchReceive();  // listen again for next inbound message
    } 


    void Shutdown()
    {
        shouldAbortThread = true;
        asyncResultForReceive = null;
        asynchCallbackFunctionForReceive = null;
        if (outboundStreamWriter != null)
        {
            try
            {
                outboundStreamWriter.Close();
                outboundStreamWriter.Dispose();
                outboundStreamWriter = null;
            }
            finally { }
        }
        if (outboundNetworkStream != null)
        {
            try
            {
                outboundNetworkStream.Close();
                outboundNetworkStream.Dispose();
                outboundNetworkStream = null;
            }
            finally { }
        }
        if (tcpClient != null)
        {
            try
            {
                tcpClient.Close();
                tcpClient = null;
            }
            catch (SocketException)
            {
                // ...
            }
        }
        if (inboundSocket != null)
        {
            try
            {
                // I think this is where it's puking
                inboundSocket.Shutdown(SocketShutdown.Both);
                inboundSocket.Close();
                inboundSocket = null;
            }
            catch (SocketException)
            {
                //...
            }
        }
        if (tcpListener != null)
        {
            try
            {
                tcpListener.Stop();
                tcpListener = null;
            }
            catch (SocketException)
            {
                //...
            }
        }
    }


    #region Local variables

    volatile bool shouldAbortThread;
    TcpListener tcpListener;
    TcpClient tcpClient;
    StreamWriter outboundStreamWriter;
    NetworkStream outboundNetworkStream;
    Socket inboundSocket = null;
    IAsyncResult asyncResultForReceive;
    public AsyncCallback asynchCallbackFunctionForReceive;
    byte[] bufferForReceive;
    static string HostnameShared;
    #endregion
}
4

2 回答 2

1

In OnDataReceived it looks (from the comment, and the fact that you ignore the return value of EndReceive) that you're processing the data in the buffer before having called EndReceive on inboundSocket. If this is the case, you have no indication of the number of bytes that were actually read from the socket (which would likely be 0 if the socket is being shut down), and so you're processing the data still in the buffer from the previous read, hence the appearance of re-reading old data.

You need to call EndReceive before attempting to process any data. Something like:

void OnDataReceived(IAsyncResult asyn)
{
    var bytesRead = inboundSocket.EndReceive(asyn);
    if (bytesRead == 0) return; // Socket is closed

    // Process the data here

    WireUpCallbackForAsynchReceive();  // listen again for next inbound message
} 

Furthermore, the fact that you weren't checking the return value of EndReceive suggests that you're expecting each receive to return either an entire buffer's worth of data, or perhaps the same number of bytes as were sent by the server in it's call to Send/BeginSend, which is not always the case.

于 2013-03-19T15:53:55.567 回答
0

我解决了这个问题,如下所示。我没有提供回调函数并让框架处理第二个线程,而是显式地启动了我自己的工作线程并让它阻塞直到接收到数据。如果接收到零字节,我知道另一端正在关闭并且可以很好地终止我的一端。我想分享的唯一问题是我需要尝试捕获阻塞的 socket.Receive() 函数并处理 SocketError.Interrupted 异常。这发生在主程序终止时。

代码看起来有点像这样:

        Thread dataReceivedWorkerThread;

    void AcceptInboundSocketAndStartWorkerThread()
    {            
        inboundSocket = tcpListener.AcceptSocket();
        dataReceivedWorkerThread = new Thread(new ThreadStart(ListenForAndWaitToReceiveInboundData));
        dataReceivedWorkerThread.Start();
    }

    void ListenForAndWaitToReceiveInboundData()
    {
            var bytesRead = 0;
            try
            {
                bytesRead = inboundSocket.Receive(bufferForReceive, 0, bufferForReceive.Length, SocketFlags.None); // blocks here                   
            }
            catch (SocketException se)
            {
                if (se.SocketErrorCode == SocketError.Interrupted) 
                {
                    // Handle shutdown by (e.g.) parent thread....                      
                }
                else
                {
                    // handle other
                }
                return;
            }
            if (bytesRead == 0)
            {
                // handle shutdown from other end
                return;
            }

            // Do stuff with received data....
        }
    }
于 2014-01-03T10:14:14.740 回答