[英]C# TCP socket performance degrading as application runs
我寫了一個C#服務器應用程序。 服務器使用異步TCP套接字。
數據包是80-180字節的數據。
對於性能測試,我有一個客戶端連接並連續發送數據包。 通過調試前100個數據包(0-100)在大約5秒內接收。 當服務器收到數據包#300-400時,接收數據包大約需要30秒。 隨着更多接收發生,性能繼續下降。
我環顧四周,一直無法找到解決方案。 我已經嘗試設置Socket.NoDelay標志,以防Nagle算法禁止服務器。
我已經禁用了服務器中的所有功能; 所以它只是收到以確保我沒有失去其他代碼中的性能。
我還檢查了我的CPU利用率,它是~13%。 我有超過2 GB的可用內存。 在運行應用程序時,ram不會不斷增長,利用率也很低。
關於調試什么,看看下一個我感到很遺憾......
編輯:添加代碼示例
public void StartListening()
{
try
{
IPAddress ipAddress = IPAddress.Parse("192.168.2.60");
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, m_Port);
m_MainSocket = new Socket(localEndPoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
m_MainSocket.NoDelay = true;
m_MainSocket.Bind(localEndPoint);
m_MainSocket.Listen(10);
m_MainSocket.BeginAccept(new AsyncCallback(clientConnected), null);
System.Diagnostics.Debug.WriteLine("Listening on:Local IP Address: " + localEndPoint.Address.ToString() + " Port :" + localEndPoint.Port.ToString() + "\n");
}
catch (SocketException se)
{
System.Diagnostics.Debug.WriteLine("Listening Exception \n");
System.Diagnostics.Debug.WriteLine(se.Message);
}
}
void clientConnected(IAsyncResult ar)
{
try
{
SocketState state = new SocketState(m_MainSocket.EndAccept(ar));
Client client = new Client(state);
if (client.SocketState.clientSocket.Connected)
{
System.Diagnostics.Debug.WriteLine("Client #?????? Connected \n");
AddLogText("Client #?????? Connected \r\n\r\n");
waitForData(client);
SetSendButton(true);
}
m_MainSocket.BeginAccept(new AsyncCallback(clientConnected), null);
}
catch (ObjectDisposedException)
{
System.Diagnostics.Debug.WriteLine("Client Connected: Socket has been closed\n");
}
catch (SocketException se)
{
System.Diagnostics.Debug.WriteLine("Client Connected Exception \n");
System.Diagnostics.Debug.WriteLine(se.Message);
}
}
void waitForData(Client client)
{
try
{
SocketState state = new SocketState(client.SocketState.clientSocket);
client.SocketState.clientSocket = null;
client.SocketState = state;
client.SocketState.clientSocket.BeginReceive(client.SocketState.DataBuffer, 0, client.SocketState.DataBuffer.Length, SocketFlags.None, new AsyncCallback(readDataCallback), client);
}
catch (SocketException se)
{
System.Diagnostics.Debug.WriteLine("Wait For Data Exception \n");
System.Diagnostics.Debug.WriteLine(se.Message);
}
}
public void readDataCallback(IAsyncResult ar)
{
Client client = (Client)ar.AsyncState;
try
{
// Read data from the client socket.
int iRx = client.SocketState.clientSocket.EndReceive(ar);
client.SocketState.SB.Append(Encoding.ASCII.GetString(client.SocketState.DataBuffer, 0, iRx));
string sPacketString = client.SocketState.SB.ToString();
Server formServer = this;
Packet_Helper packet_helper = new Packet_Helper(sPacketString, formServer);
Packet packet = new Packet(sPacketString);
client.SerialNumber = packet.SerialNumber;
client.FirmwareVersion = packet.FirmwareVersion;
client.ProductID = packet.ProductID;
client.HardwareVersion = packet.HardwareVersion;
if (!m_Clients.ContainsKey(packet.SerialNumber))
{
m_Clients.Add(packet.SerialNumber, client);
UpdateClientList();
string[] packets = client.refreshAll();
for (int i = 0; i < packets.Length; i++)
{
byte[] byteData = Encoding.ASCII.GetBytes(packets[i]);
client.SocketState.clientSocket.BeginSend(byteData, 0, byteData.Length, SocketFlags.None, new AsyncCallback(SendCallback), client);
AddPacketsSentText(packets[i] + "--" + (iSent++).ToString() + "\r\n\r\n");
}
}
System.Diagnostics.Debug.WriteLine("Read " + sPacketString.Length.ToString() + " bytes from " + client.SerialNumber + "\n" + sPacketString + "\n");
AddLogText("Read " + sPacketString.Length.ToString() + " bytes from " + client.SerialNumber + " \r\n");
AddLogText(sPacketString.ToString() + "\r\n\r\n");
waitForData(client);
}
catch (ObjectDisposedException)
{
System.Diagnostics.Debugger.Log(0, "1", "\nOnDataReceived: Socket has been closed\n");
}
catch (SocketException se)
{
if (se.ErrorCode == 10054) // Error code for Connection reset by peer
{
string sclientSerial = "??????";
if (client.SerialNumber != null || client.SerialNumber != "")
sclientSerial = client.SerialNumber;
AddLogText("Client " + sclientSerial + " Disconnected" + "\r\n\r\n");
System.Diagnostics.Debug.WriteLine("Client " + sclientSerial + " Disconnected" + "\n");
m_Clients.Remove(sclientSerial);
UpdateClientList();
}
else
{
System.Diagnostics.Debug.WriteLine("Read Data Exception \n");
System.Diagnostics.Debug.WriteLine(se.Message);
}
}
}
class SocketState
{
private Socket m_ClientSocket; //Socket connection to the client
private byte[] m_DataBuffer = new byte[256]; //Buffer to store the data sent by the client
private StringBuilder m_SB = new StringBuilder(); //for building recieved data into a string
/// <summary>
/// Gets or Sets the client Socket
/// </summary>
public Socket clientSocket
{
get { return m_ClientSocket; }
set { m_ClientSocket = value; }
}
/// <summary>
/// Gets the DataBuffer
/// </summary>
public byte[] DataBuffer
{
get { return m_DataBuffer; }
set { DataBuffer = value; }
}
/// <summary>
/// Gets or Sets the SB
/// </summary>
public StringBuilder SB
{
get { return m_SB; }
set { m_SB = value; }
}
public SocketState(Socket socket)
{
m_ClientSocket = socket;
m_ClientSocket.ReceiveBufferSize = 256;
m_ClientSocket.NoDelay = true;
//m_DataBuffer = Enumerable.Repeat((byte)0, 256).ToArray();
}
}
編輯:添加AddLogText()函數。 此函數用於將文本添加到UI中的文本框。
// Delegate - 啟用異步調用以設置tb_ListeningLog的text屬性
delegate void AddLogTextCallback(string text);
private void AddLogText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.tb_ListeningLog.InvokeRequired)
{
AddLogTextCallback d = new AddLogTextCallback(AddLogText);
this.Invoke(d, new object[] { text });
}
else
{
this.tb_ListeningLog.Text += text;
tb_ListeningLog.SelectionStart = tb_ListeningLog.Text.Length;
tb_ListeningLog.ScrollToCaret();
}
}
我用這個答案在黑暗中拍了一下,但你發布的代碼肯定有幫助。
隨着時間的推移你可能會看到性能下降的原因是因為readDataCallback
方法中的代碼。 您設置的方式,數據處理在您再次接收之前完成。 這意味着隨着處理長度的增加,接收數據之間的持續時間也會增加。
我不知道你的很多方法中有什么代碼,但是你應該看看可能需要一段時間才能完成的任何循環。 如果您在查找代碼時遇到問題,請嘗試查找哪些方法花費的時間最長,然后繼續縮小代碼范圍。
例如(我猜測瓶頸在這個代碼區域):
if (!m_Clients.ContainsKey(packet.SerialNumber))
{
m_Clients.Add(packet.SerialNumber, client);
AddLogText("Running UpdateClientList\r\n");
UpdateClientList();
AddLogText("Doing client.refreshAll\r\n");
string[] packets = client.refreshAll();
AddLogText("Doing for loop\r\n");
for (int i = 0; i < packets.Length; i++)
{
byte[] byteData = Encoding.ASCII.GetBytes(packets[i]);
client.SocketState.clientSocket.BeginSend(byteData, 0, byteData.Length, SocketFlags.None, new AsyncCallback(SendCallback), client);
AddPacketsSentText(packets[i] + "--" + (iSent++).ToString() + "\r\n\r\n");
}
}
只需觀察每種方法與眼睛之間的時間量,或使其更容易,並使用Stopwatch
或DateTime
時間來顯示確切的時間。
此外,如果您發現代碼的行為無法提高效率,您可以想到在單獨的線程中處理數據。 我假設不希望這種行為,因為手頭的問題。
對於AddLogText
方法,請嘗試使用tb_ListeningLog.Text.AppendText
而不是+ =。
我不知道為什么你有這么長的代碼來讀取更多的數據。 另外,嘗試將消息放入可由不同線程處理的隊列中。
這是我使用的實現:
// Read data from the client
private void ReadCallback(IAsyncResult ar)
{
StateObject state = (StateObject)ar.AsyncState;
Socket socket = state.workSocket;
try
{
if (socket.Connected)
{
// Read the socket
int bytesRead = socket.EndReceive(ar);
// Deserialize objects
foreach (MessageBase msg in MessageBase.Receive(socket, bytesRead, state))
{
// Add objects to the message queue
lock (this.messageQueue)
messageQueue.Enqueue(msg);
}
// Notify any event handlers
if (DataRecieved != null)
DataRecieved(socket, bytesRead);
// Asynchronously read more client data
socket.BeginReceive(state.Buffer, state.readOffset, state.BufferSize - state.readOffset, 0,
ReadCallback, state);
}
else
{
HandleClientDisconnect(socket);
}
}
catch (SocketException)
{
HandleClientDisconnect(socket);
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.