C# TCP服务器连接导致处理器过载
我有一个TCP/IP服务器,当消息通过它发送时,它应该允许连接保持打开状态。然而,似乎有些客户机为每条消息打开了一个新的连接,这导致CPU使用量达到最大值。我试图通过添加一个超时来解决这个问题,但似乎偶尔也会出现问题。我怀疑我的解决方案不是最好的选择,但我不确定会是什么 下面是我删除日志记录、错误处理和处理的基本代码C# TCP服务器连接导致处理器过载,c#,.net,tcp,tcplistener,C#,.net,Tcp,Tcplistener,我有一个TCP/IP服务器,当消息通过它发送时,它应该允许连接保持打开状态。然而,似乎有些客户机为每条消息打开了一个新的连接,这导致CPU使用量达到最大值。我试图通过添加一个超时来解决这个问题,但似乎偶尔也会出现问题。我怀疑我的解决方案不是最好的选择,但我不确定会是什么 下面是我删除日志记录、错误处理和处理的基本代码 private void StartListening() { try { _tcpListener = new TcpLi
private void StartListening()
{
try
{
_tcpListener = new TcpListener( IPAddress.Any, _settings.Port );
_tcpListener.Start();
while (DeviceState == State.Running)
{
var incomingConnection = _tcpListener.AcceptTcpClient();
var processThread = new Thread( ReceiveMessage );
processThread.Start( incomingConnection );
}
}
catch (Exception e)
{
// Unfortunately, a SocketException is expected when stopping AcceptTcpClient
if (DeviceState == State.Running) { throw; }
}
finally { _tcpListener?.Stop(); }
}
我认为实际的问题是正在创建多个进程线程,但没有关闭。下面是ReceiveMessage的代码
private void ReceiveMessage( object IncomingConnection )
{
var buffer = new byte[_settings.BufferSize];
int bytesReceived = 0;
var messageData = String.Empty;
bool isConnected = true;
using (TcpClient connection = (TcpClient)IncomingConnection)
using (NetworkStream netStream = connection.GetStream())
{
netStream.ReadTimeout = 1000;
try
{
while (DeviceState == State.Running && isConnected)
{
// An IOException will be thrown and captured if no message comes in each second. This is the
// only way to send a signal to close the connection when shutting down. The exception is caught,
// and the connection is checked to confirm that it is still open. If it is, and the Router has
// not been shut down, the server will continue listening.
try { bytesReceived = netStream.Read( buffer, 0, buffer.Length ); }
catch (IOException e)
{
if (e.InnerException is SocketException se && se.SocketErrorCode == SocketError.TimedOut)
{
bytesReceived = 0;
if(GlobalSettings.IsLeaveConnectionOpen)
isConnected = GetConnectionState(connection);
else
isConnected = false;
}
else
throw;
}
if (bytesReceived > 0)
{
messageData += Encoding.UTF8.GetString( buffer, 0, bytesReceived );
string ack = ProcessMessage( messageData );
var writeBuffer = Encoding.UTF8.GetBytes( ack );
if (netStream.CanWrite) { netStream.Write( writeBuffer, 0, writeBuffer.Length ); }
messageData = String.Empty;
}
}
}
catch (Exception e) { ... }
finally { FileLogger.Log( "Closing the message stream.", Verbose.Debug, DeviceName ); }
}
}
对于大多数客户机,代码运行正常,但也有少数客户机似乎为每条消息创建了新连接。我怀疑问题在于我如何处理IOException。对于失败的系统,代码似乎直到第一条消息传入后30秒才到达finally语句,每条消息都会创建一个新的ReceiveMessage线程。因此,日志将显示传入的消息,其中30秒将开始显示有关消息流关闭的多条消息
下面是我如何检查连接,以防这很重要
public static bool GetConnectionState( TcpClient tcpClient )
{
var state = IPGlobalProperties.GetIPGlobalProperties()
.GetActiveTcpConnections()
.FirstOrDefault( x => x.LocalEndPoint.Equals( tcpClient.Client.LocalEndPoint )
&& x.RemoteEndPoint.Equals( tcpClient.Client.RemoteEndPoint ) );
return state != null ? state.State == TcpState.Established : false;
}
你在相当多的层面上(以一种更糟糕的方式)重新发明轮子:
编辑:和3。你真的需要缓存你辛苦创建的线程。如果您有那么多的长期连接,每个线程只有一个套接字通信,那么系统线程池就不够了,但是您可以构建自己的可扩展线程池。您也可以使用
select
在一个线程上共享多个套接字,但这会大大改变您的逻辑。我会放弃多线程,让它点击而不是点击谢谢您的回复。我有1秒的超时时间,这样当服务停止时监听器可以安全地关闭(DeviceState==State.Running)。如果我使用纯阻塞网络流,我的理解是,在收到消息之前,我没有办法阻止它。理论上,我应该只从客户端获得一个连接,所以我应该只有一个线程。然而,情况显然并非总是如此,这就是为什么我允许多个,并试图关闭那些不再使用的。但不确定这是正确的/最好的做法。您可以关闭代码中的套接字,也可以中止线程。我建议您关闭侦听器套接字,以唤醒您的侦听线程,并出现“连接已关闭”异常。此外,如果您首先假设世界上只有一个连接,在这个世界上,您可以通过隧道从手机的另一端获取新的IP(从而连接),那么您需要阅读更多有关现代套接字使用的信息,或者使用已经创建的框架(比如asp.net mvc core和web服务),或者更好,两者都使用!