java beginreceive_.NET 4.5 ASync TCP服务器内存泄漏 - BeginReceive / BeginSend

我们需要一个支持与许多客户端进行TCP通信的Windows服务 . 所以我把它 Build 在MSDN Async Example上 . 微软的例子就是客户端向服务器发送一条消息,然后服务器重新发送消息然后关闭 . 大!

因此,盲目地将此部署到我们的产品和客户网站,我们得到报告说它已经崩溃了 . 看看Prod,我们注意到在1天之后,在抛出OutOfMemoryException之前,内存使用量增长到不到1GB . 这里有很多测试!

这发生在1个连接的客户端 . 它发送一个基于XML的消息,每秒大约1200字节 . 是的,每一秒 .

然后,该服务进行一些处理并将返回XML消息发送回客户端 .

我已将TCP客户端/服务器通信转换为一组简单的控制台应用程序来复制问题 - 主要是为了消除其他托管/非托管资源 . 现在我已经看了好几天了,把我所有的头发和牙齿拉了出来 .

在我的例子中,我将重点关注以下类:

B2BSocketManager (Server Listener, Sender, Receiver)

注意我已经更改了代码以返回whoopsy readonly字节数组 - 而不是已发送的消息 . 我还从BeginReceive / BeginSend调用中删除了新的AsyncCallback(委托) .

namespace Acme.OPC.Service.Net.Sockets

{

using Acme.OPC.Service.Logging;

using System;

using System.Linq;

using System.Net;

using System.Net.Sockets;

using System.Text;

using System.Threading;

using System.Threading.Tasks;

public class B2BSocketManager : ISocketSender

{

private ManualResetEvent allDone = new ManualResetEvent(false);

private IPEndPoint _localEndPoint;

private readonly byte[] whoopsy = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

public B2BSocketManager(IPAddress address, int port)

{

_localEndPoint = new IPEndPoint(address, port);

}

public void StartListening()

{

StartListeningAsync();

}

private async Task StartListeningAsync()

{

await System.Threading.Tasks.Task.Factory.StartNew(() => ListenForConnections());

}

public void ListenForConnections()

{

Socket listener = new Socket(_localEndPoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

Log.Instance.Info("B2BSocketManager Listening on " + _localEndPoint.Address.ToString() + ":" + _localEndPoint.Port.ToString());

try

{

listener.Bind(_localEndPoint);

listener.Listen(100);

while (true)

{

allDone.Reset();

Log.Instance.Info("B2BSocketManager Waiting for a connection...");

listener.BeginAccept(new AsyncCallback(ConnectCallback), listener);

allDone.WaitOne();

}

}

catch (Exception e)

{

Log.Instance.Info(e.ToString());

}

}

public void ConnectCallback(IAsyncResult ar)

{

allDone.Set();

Socket listener = (Socket)ar.AsyncState;

Socket handler = listener.EndAccept(ar);

handler.DontFragment = false;

handler.ReceiveBufferSize = ClientSocket.BufferSize;

Log.Instance.Info("B2BSocketManager Client has connected on " + handler.RemoteEndPoint.ToString());

ClientSocket state = new ClientSocket();

state.workSocket = handler;

handler.BeginReceive(state.buffer, 0, ClientSocket.BufferSize, 0, new AsyncCallback(ReadCallback), state); // SocketFlags.None

}

public void ReadCallback(IAsyncResult ar)

{

String message = String.Empty;

ClientSocket state = (ClientSocket)ar.AsyncState;

Socket handler = state.workSocket;

int bytesRead = handler.EndReceive(ar);

if (bytesRead > 0)

{

Console.WriteLine("Received " + bytesRead + " at " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));

message = Encoding.ASCII.GetString(state.buffer, 0, bytesRead);

if (!string.IsNullOrEmpty(message))

{

Send(handler, message);

}

handler.BeginReceive(state.buffer, 0, ClientSocket.BufferSize, 0, ReadCallback, state);

}

}

public void Send(Socket socket, string data)

{

// just hard coding the whoopse readonly byte array

socket.BeginSend(whoopsy, 0, whoopsy.Length, 0, SendCallback, socket);

}

private void SendCallback(IAsyncResult ar)

{

Socket state = (Socket)ar.AsyncState;

try

{

int bytesSent = state.EndSend(ar);

}

catch (Exception e)

{

Log.Instance.ErrorException("", e);

}

}

}

}

ClientSender (Client Sender)

客户端每250毫秒向服务器发送一个xml字符串 . 我想看看它会如何表现 . xml略小于我们在实时系统上发送的内容,只是使用格式化字符串创建 .

namespace TestHarness

{

using System;

using System.Linq;

using System.Net;

using System.Net.Sockets;

using System.Text;

using System.Threading;

class ClientSender

{

private static ManualResetEvent connectDone = new ManualResetEvent(false);

private static ManualResetEvent receiveDone = new ManualResetEvent(false);

private static ManualResetEvent sendDone = new ManualResetEvent(false);

private static void StartSpamming(Socket client)

{

while(true)

{

string message = @"{0}{1}" + Environment.NewLine;

Send(client, string.Format(message, "Be someone" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), String.Concat(Enumerable.Repeat("Oooooooops", 50))));

Thread.Sleep(250);

}

}

public static void Connect(EndPoint remoteEP)

{

Socket listener = new Socket(remoteEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

listener.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), listener);

connectDone.WaitOne();

}

private static void ConnectCallback(IAsyncResult ar)

{

try

{

// Retrieve the socket from the state object.

Socket client = (Socket)ar.AsyncState;

// Complete the connection.

client.EndConnect(ar);

Console.WriteLine("Socket connected to {0}", client.RemoteEndPoint.ToString());

// Signal that the connection has been made.

connectDone.Set();

System.Threading.Tasks.Task.Factory.StartNew(() => StartSpamming(client));

}

catch (Exception e)

{

Console.WriteLine(e.ToString());

}

}

private static void Send(Socket client, String data)

{

byte[] byteData = Encoding.ASCII.GetBytes(data);

client.BeginSend(byteData, 0, byteData.Length, SocketFlags.None, new AsyncCallback(SendCallback), client);

}

private static void SendCallback(IAsyncResult ar)

{

try

{

Socket client = (Socket)ar.AsyncState;

int bytesSent = client.EndSend(ar);

Console.WriteLine("Sent {0} bytes to server " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), bytesSent);

sendDone.Set();

}

catch (Exception e)

{

Console.WriteLine(e.ToString());

}

}

private static void Receive(Socket client)

{

try

{

StateObject state = new StateObject();

state.workSocket = client;

client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);

}

catch (Exception e)

{

Console.WriteLine(e.ToString());

}

}

private static void ReceiveCallback(IAsyncResult ar)

{

try

{

StateObject state = (StateObject)ar.AsyncState;

Socket client = state.workSocket;

int bytesRead = client.EndReceive(ar);

if (bytesRead > 0)

{

state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));

client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);

}

else

{

if (state.sb.Length > 1)

{

string response = state.sb.ToString();

}

receiveDone.Set();

}

}

catch (Exception e)

{

Console.WriteLine(e.ToString());

}

}

}

}

State Class

我想要的只是一个读取缓冲区来消除消息并尝试加载到XML中 . 但是这个已从这个减少版本中删除,以查看仅插座的问题 .

using System;

using System.Linq;

using System.Net.Sockets;

namespace Acme.OPC.Service.Net.Sockets

{

public class ClientSocket

{

public Socket workSocket = null;

public const int BufferSize = 4096;

public readonly byte[] buffer = new byte[BufferSize];

}

}

我在这里分享了我的代码:

我一直使用我的Telerik JustTrace Profiler来分析事物 . 我刚启动服务器应用程序然后启动客户端应用程序 . 这是在我的Windows 7 64位VS2013开发环境中 .

Run 1

我看到内存使用量大约为250KB,工作集大约为20MB . 时间似乎很顺利,然后突然间内存使用将在大约12分钟后加速 . 虽然事情各不相同

iaZDE.png

在我强制GC的~16:45:55(快照)之后,每次按下它时内存开始上升而不是让它继续运行并自动升高,这可能是Telerik的一个问题 .

Run 2

然后,如果我在Send with中创建字节数组(这更像是服务的作用 - 向客户端发送适当的响应字符串):

public void Send(Socket socket, string data)

{

byte[] byteData = Encoding.ASCII.GetBytes(data);

socket.BeginSend(byteData, 0, byteData.Length, 0, SendCallback, socket);

}

我们可以看到更多的内存:

LXRRs.png

这让我想到了记忆中保留的内容 . 我看到了System.Threading.OverlappedData的日志,我注意到了ExecutionContexts . OverlappedData这次引用了一个字节数组 .

bRjqc.png

使用Roots Paths to GC

rTcal.png

我正在进行整夜的分析,所以希望能够在早上添加更多信息 . 希望有人能在此之前指出我正确的方向 - 如果我做错了什么,我太盲目/愚蠢地看到它 .

这是隔夜运行的结果:

ujEAe.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值