在后台线程中,TcpListener不能完全处理

时间:2012-04-25 00:11:37

标签: c# multithreading sockets tcp dispose

我从MSDN获取了TCPListener示例,并将其放在一个名为ListenForClients的方法中,然后使用该方法作为delgate启动一个Thread,就像这样。

public class MediaStreamSender
{
    private List<TcpClient> clients = new List<TcpClient>();
    private TcpListener tcpListener;
    private Thread listenerThread;
    private int port;

    public MediaStreamSender(int port)
    {
        this.port = port;
        listenerThread = new Thread(ListenForClients);
        listenerThread.IsBackground = true;
        listenerThread.Start();
    }

    private void ListenForClients()
    {
        try
        {
            // Set the TcpListener on port 13000.
            IPAddress localAddr = IPAddress.Parse("127.0.0.1");

            // TcpListener tcpListener = new TcpListener(port);
            tcpListener = new TcpListener(localAddr, port);

            // Start listening for client requests.
            tcpListener.Start();

            // Buffer for reading data
            Byte[] bytes = new Byte[256];
            String data = null;

            // Enter the listening loop.
            while (true)
            {
                Console.Write("Waiting for a connection... ");

                // Perform a blocking call to accept requests.
                // You could also user server.AcceptSocket() here.
                TcpClient client = tcpListener.AcceptTcpClient();
                Console.WriteLine("Connected!");

                data = null;

                // Get a stream object for reading and writing
                NetworkStream stream = client.GetStream();

                int i;

                // Loop to receive all the data sent by the client.
                while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
                {
                    // Translate data bytes to a ASCII string.
                    data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
                    Console.WriteLine("Received: {0}", data);

                    // Process the data sent by the client.
                    data = data.ToUpper();

                    byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);

                    // Send back a response.
                    stream.Write(msg, 0, msg.Length);
                    Console.WriteLine("Sent: {0}", data);
                }

                // Shutdown and end connection
                client.Close();
            }
        }
        catch (SocketException e)
        {
            Console.WriteLine("SocketException: {0}", e);
        }
        finally
        {
            // Stop listening for new clients.
            if (tcpListener!= null)
            {
                tcpListener.Stop();
            }
        }
    }
}

虽然这里的例子很奇怪,但我发现了一个我根本不懂的行为。

为什么我创建一个简单的命令行应用程序

class Program
{
    static void Main(string[] args)
    {
        CreateASender();
        Thread.Sleep(2000);
        CreateASender();
        Console.ReadLine();
    }

    private static void CreateASender()
    {
        MediaStreamSender sender = new MediaStreamSender(8660);
    }
}

注意到我甚至给了2秒来清理套接字资源,因为我不知道是否有必要,它会抛出以下异常。

SocketException: System.Net.Sockets.SocketException (0x80004005): Only one usage of each socket address (protocol/network address/port) is normally permitted
   at System.Net.Sockets.Socket.DoBind(EndPoint endPointSnapshot, SocketAddress socketAddress)
   at System.Net.Sockets.Socket.Bind(EndPoint localEP)
   at System.Net.Sockets.TcpListener.Start(Int32 backlog)
   at System.Net.Sockets.TcpListener.Start()
   at Construct.Media.MediaStream.MediaStreamSender.ListenForClients() in C:\-----------\MediaStreamSender.cs:line 113

我原本以为在我创建第二个MediaStreamSender时,第二个TcpListener,第一个对象的后台线程已经中止,finally块已经运行,关闭TcpListener,并释放我创建相同的第二个。

请注意,即使我将终结器放入MediaStreamSender对象中,仍然会发生这种情况

public class MediaStreamSender
{
    private List<TcpClient> clients = new List<TcpClient>();
    private TcpListener tcpListener;
    private Thread listenerThread;
    private int port;
    .
    .
    .
    ~MediaStreamSender()
    {
       if (tcpListener != null)
       {
           tcpListener.Stop();
       }

       foreach (TcpClient client in clients)
       {
           client.Close();
       }

       listenerThread.Abort();
       listenerThread.Join();
   }
}

而且,即使是陌生人,如果我在Visual Studio中进行单元测试,只会创建一个MediaStreamSender,那么它每隔一段时间就会发生 。然后我一遍又一遍地进行单元测试。

无法承受的巨大文字墙,任何人都可以解释最新情况吗?

0 个答案:

没有答案