【问题标题】:C# Tcp Server client Disconnecting problemsC# Tcp Server 客户端断开连接问题
【发布时间】:2016-01-26 19:32:47
【问题描述】:

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

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();



            }


        }


    }


}
}

我已经尝试关闭 reader 和 tcpClient,但都没有工作。

【问题讨论】:

    标签: c# sockets tcpclient tcplistener


    【解决方案1】:

    最初让我印象深刻的是,您没有在此代码中添加任何错误处理。 当您尝试从断开连接的套接字读取时,您会遇到异常,这会导致您的应用程序崩溃。

    您向 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;
    

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

        【解决方案3】:

        您没有任何代码来检测任何客户端已关闭其连接。你继续做 reader.Readline。但这行不通。您应该改用网络流并检查是否接收到 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 方法立即完成并返回零字节。'

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-07-04
          • 1970-01-01
          • 2014-01-24
          相关资源
          最近更新 更多