这是我的第一个问题,希望你能帮助我。
我有2个程序,一个服务器和一个客户端,我的问题在客户端。
运行2到3天后,它使用超过300MB的RAM(我可以通过查看TaskManager来告诉它)并且永远不会释放它!此外,我不得不说这个客户端每秒都会从GPS设备接收数据。 我用ANTS Memory Profiler分析了我的客户端,我发现我多次创建一个对象并且它们永远不会被破坏。
这是我的代码:
private static TcpClient _client;
private readonly ManualResetEvent _receiveDone = new ManualResetEvent(false);
public void Receive()
{
if (_client.Connected)
{
_receiveDone.Reset();
ObjectState state = new ObjectState();
state.WorkSocket = _client.Client;
state.Data = new byte[_client.ReceiveBufferSize];
_client.Client.BeginReceive(state.Data, 0, Convert.ToInt32(_client.ReceiveBufferSize), 0, ReceiveCallback, state);
if (!_receiveDone.WaitOne(20000))
{
//after 20 seconds WITHOUT RECEIVING DATA, do some code to test if the connection is alive
}
}
}
void ReceiveCallback(IAsyncResult ar)
{
ObjectState state = (ObjectState)ar.AsyncState;
Socket client = state.WorkSocket;
if (client.Connected)
{
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
{
string response = Encoding.ASCII.GetString(state.Data, 0, bytesRead);
doProcess(response);
client.BeginReceive(state.Data, 0, Convert.ToInt32(_client.ReceiveBufferSize), 0, ReceiveCallback, state);
_receiveDone.Set();
}
else
{
//Disconnection
}
}
}
public class ObjectState
{
public Socket WorkSocket;
public byte[] Data;
}
ANTS Memory Profiler告诉我,我有数千个byte[]
的实时实例。 (因为我总是创建ObjectState
)
我想做的第一件事:在我致电ObjectState
之后,处理我创建的所有BeginReceive
,但我只收到第一条消息。
然后我想停止使用ObjectState
......怎么样?
这是我修改过的代码:
private _data byte[];
public void Receive()
{
if (_client.Connected)
{
_receiveDone.Reset();
_data = new byte[_client.ReceiveBufferSize];
_client.Client.BeginReceive(_data, 0, Convert.ToInt32(_client.ReceiveBufferSize), 0, ReceiveCallback, null);
if (!_receiveDone.WaitOne(20000))
{
//after 20 seconds of inactivity do some code to test if the connectio is alive
}
}
}
void ReceiveCallback(IAsyncResult ar)
{
Socket client = _cliente.Client;
if (client.Connected)
{
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
{
string response = Encoding.ASCII.GetString(_data, 0, bytesRead);
doProcess(response);
client.BeginReceive(_data, 0, Convert.ToInt32(_client.ReceiveBufferSize), 0, ReceiveCallback, null);
_receiveDone.Set();
}
else
{
//Disconnection
}
}
}
这有什么问题?我只得到第一条消息,所以这不好。
然后,如果我删除_receiveDone.Set
它会收到所有消息,但是_receiveDone.WaitOne(2000)
总是每20秒执行一次,无论我是否真正接收数据,这也不好。
所以我的问题是,我该怎么做才能减少使用这么多内存?我希望这能很好地解释我的问题。
修改 我上传了这些图片,希望它们也能提供帮助。
答案 0 :(得分:1)
if (client.Connected)
{
int bytesRead = client.EndReceive(ar);
// etc..
调用EndReceive 不是可选的,你有来调用它,否则你会泄漏资源。使用try / catch捕获ObjectDisposedException。
答案 1 :(得分:0)
我认为主要的问题是,当没有任何可用的东西时,你依靠BeginReceive
返回0字节。但这不是它的工作原理。 BeginReceive
将阻止,直到有可用数据,或者直到客户端断开连接。
在第一种情况下,ReceiveCallback
将state
对象传递给client.BeginReceive
。现在你有一个ObjectState
实例在那里等待来自客户端的数据。 _receiveDone
事件已设置,因此您的Receive
方法退出但客户端连接仍处于打开状态且您有异步读取。
下次调用Receive
时,它将创建另一个 ObjectState
实例并发出另一个异步读取请求。
第二种情况非常可怕。您的_data
数组属于类范围,每次都会重新分配。因此,异步读取可能会填充您传递的缓冲区,但是当您到达ReceiveCallback
时数据会丢失。
您的代码仅对第一次接收进行超时检查。在那之后,事情可以无限期地挂起。我会建议更像:
private _data byte[];
private bool _receiving = false;
public void StartReceiving()
{
if (_receiving)
{
// ERROR: Already receiving
throw new InvalidOperationException();
}
_receiving = true;
Receive();
}
public void Receive()
{
if (_client.Connected)
{
_data = new byte[_client.ReceiveBufferSize];
var ir _client.Client.BeginReceive(_data, 0, Convert.ToInt32(_client.ReceiveBufferSize), 0, ReceiveCallback, null);
if (!_ir.WaitOne(20000))
{
//after 20 seconds of inactivity do some code to test if the connectio is alive
}
}
}
void ReceiveCallback(IAsyncResult ar)
{
Socket client = _cliente.Client;
int bytesRead;
try
{
bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
{
string response = Encoding.ASCII.GetString(_data, 0, bytesRead);
doProcess(response);
// Calling Receive here ensures that the timeout check happens
// with every read.
Receive();
}
else
{
_receiving = false;
}
}
catch
{
// Catch SocketException and others here
}
}
请注意,您不需要_receiveDone
事件。您可以使用IAsyncResult.WaitHandle
进行检查。