每当客户端断开连接时,服务器崩溃。这是服务器的代码
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,但它们都没有工作。
答案 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;