C#Tcp Server客户端断开连接问题

时间:2015-08-05 15:58:31

标签: c# sockets tcpclient tcplistener

每当客户端断开连接时,服务器崩溃。这是服务器的代码

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;
using System.Threading;

namespace C_Sharp_Testting
{
class Server
{

    private static TcpListener tcpListener;
    private static List<TcpClient> tcpClientsList = new List<TcpClient>();


    static void Main(string[] args)
    {


        tcpListener = new TcpListener(IPAddress.Any, 1234);
        tcpListener.Start();

        Console.WriteLine("Server started");

        while (true)
        {
            TcpClient tcpClient = tcpListener.AcceptTcpClient();
            tcpClientsList.Add(tcpClient);

            Thread thread = new Thread(ClientListener);
            thread.Start(tcpClient);


        }
    }

    public static void ClientListener(object obj)
    {
        TcpClient tcpClient = (TcpClient)obj;
        StreamReader reader = new StreamReader(tcpClient.GetStream());

        Console.WriteLine("Client connected");

        while (true)
        {
            string message = reader.ReadLine();
            BroadCast(message, tcpClient);
            Console.WriteLine(">>> "+message);

        }

    }

    public static void BroadCast(string msg, TcpClient excludeClient)
    {
        foreach (TcpClient client in tcpClientsList)
        {
            if (client != excludeClient)
            {
                StreamWriter sWriter = new StreamWriter(client.GetStream());
                sWriter.WriteLine(">>> "+msg);
                sWriter.Flush();



            }


        }


    }


}
}

我已经尝试关闭阅读器和tcpClient,但它们都没有工作。

3 个答案:

答案 0 :(得分:0)

您的代码有很多问题。猜测一下,我想说崩溃的主要原因是你将TcpClients添加到列表但从不删除它们。这意味着无论客户端断开连接,您的代码仍会尝试访问每个连接的TcpClient。

最重要的是,代码本质上是线程不安全的。您正在使用foreach循环将项添加到一个线程中的列表中,同时在另一个线程中迭代列表 - 这几乎肯定会导致抛出异常。

最后,没有try-catch块。如果您的代码崩溃,一个简单的改进就是用try-catch块包装问题区域,并在异常发生时处理/记录/检查异常。

答案 1 :(得分:0)

您没有任何代码可以检测到任何客户端已关闭其连接。你继续做读者。记录。但那不会起作用。您应该使用网络流并检查是否接收到0个字节,表示客户端已关闭其连接结束。它是这个或使用异常处理。但是接收0字节并不是一个真正的错误条件。恕我直言,你不应该使用异常来捕获正常的逻辑流程但是真正的错误。这并不意味着您不必将所有内容都包装在try catch块中。你仍然必须这样做。

来自https://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.read(v=vs.110).aspx

  

此方法将数据读入buffer参数并返回成功读取的字节数。如果没有可用于读取的数据,则Read方法返回0. Read操作读取尽可能多的数据,直到size参数指定的字节数。如果远程主机关闭连接,并且已收到所有可用数据,则Read方法立即完成并返回零字节。&#39;

当客户端断开并正确检测到时,您可以执行使其正常工作所需的一切,例如将其从列表中删除,让线程终止等等。

另请注意,tcpClientsList需要某种信号量保护,否则多个线程正在访问同一个列表,这可能导致仅在不久时发生的奇怪行为。

答案 2 :(得分:0)

最初打我的是你没有为这段代码添加任何错误处理。 当您尝试从断开连接的套接字读取时,您将遇到异常,这会使您的应用程序崩溃。

您可以向ClientListener方法添加try和catch语句,以允许每个Socket管理和处理自己的错误。 这意味着您将能够检测断开连接并正常处理它们。

考虑实施活动。 创建一个名为OnDisconnect的事件,然后将自己的处理程序添加到事件中,以从客户端列表中删除已断开连接的客户端。

    /// <summary>
    /// Event is triggered when the peer is disconnecting
    /// </summary>
    public event DisconnectHandler OnDisconnect;
    public delegate void DisconnectHandler(Peer p);

这是一个扩展类

static class SocketExtensions
    {
        /// <summary>
        /// Extension method to tell if the Socket REALLY is closed
        /// </summary>
        /// <param name="socket"></param>
        /// <returns></returns>
        public static bool IsConnected(this Socket socket)
        {
            try
            {
                return !(socket.Poll(1, SelectMode.SelectRead) && socket.Available == 0);
            }
            catch (SocketException) { return false; }
        }
    }

下面的伪代码:

if (PeerStream.CanRead)
                {
                    //networkStream.Read(byteLen, 0, 8)
                    byte[] byteLen = new byte[8];
                    if (_client.Client.IsConnected() == false)
                    {
                        //Fire Disconnect event
                        if (OnDisconnect != null)
                        {
                            disconnected = true;
                            OnDisconnect(this);
                            return null;
                        }
                    }
                    while (len == 0)
                    {
                        PeerStream.Read(byteLen, 0, 8);

                        len = BitConverter.ToInt32(byteLen, 0);
                    }
                    data = new byte[len];

                    PeerStream.Read(data, receivedDataLength, len);

                    return data;