【发布时间】:2017-11-07 12:38:45
【问题描述】:
我们的应用程序具有主动连接到客户的内部工厂网络并在发生检查事件时发送消息的功能。客户将其机器和应用程序的 IP 地址和端口号输入到我们的软件中。
我在阻塞模式下使用 TClientSocket,并为OnConnect 和OnError 事件提供了回调函数。假设上述功能已激活,当应用程序启动时,我在单独的线程中调用以下代码:
// Attempt active connection
try
m_socketClient.Active := True;
except
end;
// Later...
// If `OnConnect` and socket is connected...send some data!
// If `OnError`...call `m_socketClient.Active := True;` again
当 IP + 端口有效时,该功能运行良好。但如果不是这样,在数千次错误(以及数小时甚至数天)之后,最终会发生 Windows 套接字错误 10055 (WSAENOBUFS) 并且应用程序崩溃。
this one from ServerFramework 和 this one from Microsoft 等各种文章都谈到了 exhausting the Windows non-paged pool 并提到 (1) 主动管理未完成的异步发送操作的数量和 (2) 释放用于 I/O 操作的数据缓冲区。
我的问题是如何实现这一点,分为三个方面:
A)我做错了什么导致内存泄漏?例如,OnError 处理程序中是否缺少一些清理代码?
B) 你如何监控 I/O 缓冲区以查看它们是否被耗尽?我已经使用Process Explorer 来确认我的应用程序是导致泄漏的原因,但理想情况下我需要一些编程方式来衡量这一点。
C) 除了重启应用之外,有没有办法让 Windows 清除或释放 I/O 操作数据缓冲区?
Delphi、C/C++、C# 中的代码示例。
【问题讨论】:
-
你可能知道
TClientSocket自 Delphi 6 以来已被弃用,现在是 16 年半前。如果这些组件没有正确清理手柄,我不会感到惊讶。其他写得很糟糕的 Delphi 组件(如 shell 组件)也已被弃用并在以后被删除。 -
谢谢 Günther,其实我并不知道。需要刷新我使用的组件!经过一天的研究和测试,我想我已经发现了问题(这反映了我在使用这些 TCP/IP 组件方面的经验不足)......当套接字超时并触发
OnError事件时,你应该致电m_socketClient.Socket.Close()。我正忙着完成我的调查,并会据此得出一个答案。 -
TClientSocket可能已被弃用,但它是一个相当坚如磐石的组件。我从来没有遇到过任何内存/资源问题,而且我已经使用了很长时间。但我从未尝试在OnError事件中设置Active := True。在那种情况下,我总是Close()失败的套接字,然后让线程决定何时再次打开套接字。如果打开连接时出现错误,最好等待几秒钟再尝试再次打开连接。如果在读/写 I/O 过程中发生错误,则立即重新连接。 -
谢谢雷米。同意,
TClientSocket非常好……我只是在出错后忽略了在套接字上调用Close()。每次停止使用套接字时(例如服务器断开连接时),是否必须调用Close()?我确信 Embarcadero 文档在某处清楚地说明了这一切,但我没有发现它。我们没有发现这一点,而是在开发中,因为它是一项高级功能,通常在连接系统上进行测试(或在非连接系统上仅测试几个小时)。羞于发现客户......但至少我已准备好发布修复:o)