我的连接处理程序如下(这是个人实验而不是生产代码)
如果我没有在while循环中的任何地方添加Thread.Sleep,它会开始吸收CPU。相反,如果我做睡眠以缓解无休止的垃圾邮件,我会错过断开连接.. CPU上升与运行的客户端/线程数量成正比,因此不是导致高使用率的监听器本身,而是下面发布的实际客户端线程。任何人对如何解决这个问题都有任何想法?
(我正在避免基于等待的解决方案,因为我对async / await不够熟悉,并且线程方法对于这个相当小的项目工作正常)
我只是在SO周围寻找解决方案,并没有注意到任何特定的问题或提供解决方案而不是指导人们异步/等待文章,所以很抱歉,如果我错过了适用的答案。< / p>
private void HandleConnection(CancellationToken ct) {
int recv = 0;
byte[] buf = new byte[4096];
Trace.WriteLine($"{_name} Connected");
if (_ns.CanWrite && _client.Connected) {
_ns.Write(Encoding.BigEndianUnicode.GetBytes("■WEL"), 0, Encoding.BigEndianUnicode.GetBytes("■WEL").Length);
try {
while (_client.Connected && !ct.IsCancellationRequested) {
while (!_ns.DataAvailable) { //first attempted solution
Thread.Sleep(100); // miss discon if i sleep here
}
if (ct.IsCancellationRequested) {
Trace.WriteLine($"{(string)this} thread aborting");
break;
}
buf = new byte[4096];
if (_client.Connected && _ns.DataAvailable) {
recv = _ns.Read(buf, 0, buf.Length);
} else {
recv = 0;
}
if (recv > 0) {
string r = Encoding.BigEndianUnicode.GetString(buf);
r = r.TrimEnd('\0');
if (String.IsNullOrEmpty(r) || String.IsNullOrWhiteSpace(r))
r = null; //need the !not version
else
if (ParseMsg(r))
break;
}
//Thread.Sleep(100); // also miss discon here too
}
} catch (IOException ioe) { }
Trace.WriteLine($"{_name} Disconnected");
if (OnDisconnected != null)
OnDisconnected(this);
}
}
答案 0 :(得分:3)
通过套接字进行通信的正确方法是:
正确的线程方法需要每个连接两个线程。我不相信它比异步方法更简单。
P.S。如果您的代码使用Connected
,则会出现错误。正确的解决方案永远不需要使用Connected
。
答案 1 :(得分:1)
我遇到了与你相同的问题,但我发现解决这个问题的最佳方法是:
不使用睡眠和线程阻止套接字。
升级:如果您使用线程并进入服务器,则每个连接接收和回复每条消息的性能都会很低。
如果您需要高性能应用程序,则不得使用sleeps或为您接受的每个连接创建线程。最好的方法是使用NetworkStream提供的Asyncronous方法,使用BeginRead
和EndRead
,例如:
public void run()
{
server = new TcpListener(IPAddress.Any, port);
server.Start();
log.Info("Starting SocketServer on Port [" + port + "]");
while (keepRunning)
{
try
{
TcpClient socket = server.AcceptTcpClient();
if (keepRunning)
RequestManager.createRequestForEvalue(socket, idLayout);
}
catch (Exception ex)
{
log.Error(ex.Message);
log.Error(ex.StackTrace);
}
}
log.Info("Server Stoped.");
}
public static bool createRequestForEvalue(TcpClient socket, int idLayout)
{
Request req = null;
req = new Request(socket,idLayout);
registerRequest(req.ID,req); //Registra el Request, para su posterior uso.
// DO NOT CREATE THREADS FOR ATTEND A NEW CONNECTION!!!
//Task.Factory.StartNew(req.RunForIVR);
//ThreadPool.QueueUserWorkItem(req.RunForIVR);
req.startReceiveAsync(); //Recive data in asyncronus way.
return true;
}
public void startReceiveAsync()
{
try
{
log.Info("[" + id + "] Starting to read the Request.");
requestBuffer = new byte[BUFFER_SIZE];
NetworkStream nst = socket.GetStream();
nst.BeginRead(requestBuffer, 0,BUFFER_SIZE, this.requestReceived, nst);
}catch(Exception ex)
{
log.Error("[" + id + "] There was a problem to read the Request: " + ex.Message);
RequestManager.removeRequest(id);
closeSocket();
}
}
public void requestReceived(IAsyncResult ar)
{
try
{
NetworkStream nst = socket.GetStream();
int bread = nst.EndRead(ar); //Block the socket until all the buffer has been available.
message = Encoding.UTF8.GetString(requestBuffer, 0, BUFFER_SIZE);
log.Info("[" + id + "] Request recived: [" + message +"]");
RunForIVR();
}
catch (Exception ex)
{
log.Error("[" + id + "] There was a problem to read the Request: " + ex.Message);
RequestManager.removeRequest(id);
closeSocket();
}
}
public void SendResponse(String Response)
{
StringBuilder sb = new StringBuilder();
sb.Append(Response);
sb.Append('\0', BUFFER_SIZE - Response.Length);
string message = sb.ToString();
log.Info("[" + id + "] ivrTrans CMD: [" + idCMD + "] RESPONSE: [" + Response + "]");
NetworkStream nst = socket.GetStream();
byte[] buffer = new byte[BUFFER_SIZE];
for (int i = 0; i < BUFFER_SIZE; i++)
buffer[i] = (byte)message.ElementAt(i);
nst.BeginWrite(buffer, 0, BUFFER_SIZE, this.closeSocket, nst);
}
public void closeSocket(IAsyncResult ar = null)
{
try
{
if (ar != null) //Since 4.24
{
NetworkStream nst = socket.GetStream();
nst.EndWrite(ar);
}
socket.Close();
socket = null;
}catch(Exception ex)
{
log.Warn("[" + id + "] There was a problem to close the socket. Error: " + ex.Message + Environment.NewLine + ex.StackTrace);
}
log.Info("[" + id + "] Socket closed.");
}
升级我使用
EndRead
确保请求已全部到达。
通过其他方式,您可以使用BeginWrite
和EndWrite
来了解套接字何时完成写入以关闭连接
通过这种方式,您将以尽可能快的方式参加连接。在我的情况下,我将CPU使用率从30%降低到0%,每小时15K请求。