【问题标题】:Why does my server's performance suffer after operating for some time? [closed]为什么我的服务器运行一段时间后性能会下降? [关闭]
【发布时间】:2015-04-10 01:56:50
【问题描述】:

我编写了服务器代码,它使用来自多个客户端(超过 500 个)的多线程(异步)连续接收来自服务器的数据。 它从所有客户端读取数据并在多线程方面正常工作并接收客户端发送的所有消息。

有什么问题?

问题是当我在 1 到 2 小时后继续从客户端读取数据时,我的服务器变慢了。我的意思是,它不断增加 CPU 使用率(可能是因为释放内存……因为这里我们把所有东西都留在垃圾收集器上)。当我在 CPU 中看到内存在一段时间后从 14% 突然上升到 80-100% 时。但它仍然从客户端接收数据(但有些数据泄漏)但它仍然接收。而且我无法确定问题的原因。

有人可以告诉我这可能是什么基本原因吗?

代码的重要部分是:

public void StartServer() 
{
    try 
    {
        server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        IPEndPoint ipep = new IPEndPoint(IPAddress.Any, port);
        server.Bind(ipep);
        server.Listen(4);
        AppendIncomingData(txtMsg, "Server Started..");

        btnStart.Enabled = false;
        lblStartDate.Text = DateTime.Now.ToString();
        server.BeginAccept(new AsyncCallback(AcceptConn), server);
    } 
    catch (Exception ex) 
    {
        AppendIncomingData(txtError, ex.Message);
    }
}

void AcceptConn(IAsyncResult ia)
{
    Socket client;
    try
    {
        Socket oldserver = (Socket) ia.AsyncState;
        client = oldserver.EndAccept(ia);

        StateObject state = new StateObject();
        state.workSocket = client;
        client.ReceiveTimeout = 1000;
        client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveData), state);
        server.BeginAccept(new AsyncCallback(AcceptConn), server);
    }
    catch (Exception ex) 
    {
        AppendIncomingData(txtError, "AcceptConn Exception : " + ex.Message);
    }
}

/// <summary>
/// StateObject Class to read data from Client
/// </summary>
public class StateObject
{
    // Client  socket.
    public Socket workSocket = null;
    // Size of receive buffer.
    public const int BufferSize = 512;
    // Receive buffer.
    public byte[] buffer = new byte[BufferSize];
    // Received data string.
    public StringBuilder sb = new StringBuilder();
    public Int64 DeviceId = 0;
}

void ReceiveData(IAsyncResult ia)
{
    StateObject state = (StateObject)ia.AsyncState;
    Socket client = state.workSocket;

    string sql = "";
    string stringData = "";
    try
    {
        recv = client.EndReceive(ia);
        if (recv == 0)
        {
            client.Close();
            server.BeginAccept(new AsyncCallback(AcceptConn), server);
            return;
        }

        Array.Resize(ref state.buffer, StateObject.BufferSize);
        client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveData), state);
    } //End Try
    catch (Exception ex)
    {
        AppendIncomingData(txtError, "ReceiveData Error : " + stringData + ": " + ex.Message);

        client.Close();
        server.BeginAccept(new AsyncCallback(AcceptConn), server);
    }
}

public void AppendIncomingData(object dataControl, string str)
{
    if (btnMsg.Text.ToUpper().Contains("START") == true)
        return;

    TextBox lst = (TextBox)dataControl;

    if (lst.InvokeRequired)
    {
        AppendIncomingData_Delegate method = new AppendIncomingData_Delegate(AppendIncomingData);
        lst.Invoke(method, new object[] { lst, str });
    }
    else
    {
        msglines += 1;
        if (msglines > 500)
        {
            msglines = 0;
            //lst.Clear();
            txtError.Clear();
            txtMsg.Clear();
        }
        lst.AppendText(Environment.NewLine + DateTime.Now.ToString() + " " + str);
    }
}

可能是什么问题?

【问题讨论】:

  • 我建议您看看互联网上提供的各种免费和商业诊断工具,这可以让您缩小罪魁祸首。
  • 这可能不是 问题,但您几乎完全忽略了来自EndReceive 的返回值。这告诉您实际收到了多少字节,这可能与与您请求读取的字节数相同。 TCPs 抽象是一个任意字节流,所以不能保证Send 在一端调用和Receive 在另一端调用会1-1 匹配。
  • 内存泄漏最终会导致内存耗尽而不是减速。由于包含可访问数据的页面在物理内存中被调用,而其他页面被调出到交换文件,因此磁盘活动高将表明出现抖动。抖动是由于缺乏内存来处理应用程序的需求,但不一定是内存泄漏。在这两种情况下,您都没有理由遇到高 CPU 利用率。我认为您应该在其他地方寻找解释。使用分析工具。

标签: c# multithreading asynchronous server asyncsocket


【解决方案1】:

您描述的症状是内存使用量单调增加之一。也就是说,您的程序会随着时间的推移分配越来越多的内存。最终,分配了如此多的内存,以至于必须将其中的一些交换到磁盘。但是,如果所有分配的内存仍在定期使用,则必须将该内存换回 RAM,并且必须换出其他内存。这就是所谓的“捶打”。

如果没有可靠地证明问题的a good, minimal, complete code example,就不可能确定问题所在。但是您的代码中有几个明显的问题可能是原因:

  1. 您在关闭连接时调用BeginAccept()除了在您打开连接时调用它。这意味着随着时间的推移,您会创建越来越多出色的接受 I/O 操作。

上述问题可能相对较小,尽管无法确定客户行为模式,但无法确定。由于这个问题的几何性质,随着时间的推移,它很容易为进程添加大量内存分配。

还有这个……

  1. 您似乎维护了一个TextBox 控件,在其中添加了与服务器操作相关的状态消息(例如异常和客户端连接通知)。您已注释掉将清除相关TextBox 的行。虽然您确实清除了 txtErrortxtMsg 对象,但值得注意的是,您的接收完成回调方法不会对接收到的数据执行任何操作,这表明您在代码示例中遗漏了该细节因为某些原因。因此,您可能在收到数据时还写信给其他TextBox,但并未清除该数据。

还有可能您没有丢弃 state 对象来关闭套接字。同样,您发布的代码不会对接收到的数据执行任何操作,这对于任何服务器来说都是非常荒谬的,所以大概您在代码示例中遗漏了该细节,从而留下了您管理不善的可能性该数据结构(例如,即使客户端已断开连接,仍保留所有客户端数据结构)。

对于比上述更模糊的答案,您需要发布一个更好、更模糊的代码示例。


最后,我将指出代码中其他一些明显的错误:

  1. 当远程端点开始关闭连接时,您只需关闭套接字。你也应该在你的一端进行优雅的关闭(即调用Socket.Shutdown()),以便远程端点看到正确的关闭而不是重置连接。
  2. 正如我所提到的,您正在调用BeginAccept() 处理套接字错误和连接关闭。不要那样做。
  3. 您在收到数据后调用Array.Resize()。即使在正常的网络接收情况下也没有理由这样做,即您实际上正在调整阵列大小。但是通过传递与数组当前大小相同的新大小值来调用Array.Resize()?毫无意义。

【讨论】:

  • 感谢您给我这么漂亮的长答案和您给我的时间。为了回答您的第 1 点(共 3 点),我已重置连接,因为我的服务器将不断接收数据(如果出现任何故障,它将重新连接到客户端)。而我从服务器上的客户端收到的数据,我只需将其插入数据库(我只是隐藏了其代码)。
【解决方案2】:

AcceptConn 似乎一直在以下行中调用自己:server.BeginAccept(new AsyncCallback(AcceptConn), server);,从而创建了一个无限循环。之所以如此,是因为对 EndAccept、BeginReceive 或 BeginAccept 的调用都不会阻塞。

void AcceptConn(IAsyncResult ia)
{
    Socket client;
    try
    {
        Socket oldserver = (Socket) ia.AsyncState;
        client = oldserver.EndAccept(ia);

        StateObject state = new StateObject();
        state.workSocket = client;
        client.ReceiveTimeout = 1000;
        client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveData), state);
        server.BeginAccept(new AsyncCallback(AcceptConn), server);
    }
    catch (Exception ex) 
    {
        AppendIncomingData(txtError, "AcceptConn Exception : " + ex.Message);
    }
}

【讨论】:

  • 谢谢..让我先检查一下。如果可行,很快就会出现
猜你喜欢
  • 2018-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-06
  • 1970-01-01
  • 2023-03-18
  • 2019-12-22
  • 1970-01-01
相关资源
最近更新 更多