SocketAsyncEventArgs内存占用太大引发OutOfMemoryExceptions

最近把一个接收socket服务端软件升级为iocp方案,其实就是换成c#的socket的异步方法,主要参考的是微软写的Server类,然后再百度找到基于这个类实现的代码来改造

上线一两天后,软件崩溃了,查日志发现报了很多OutOfMemoryExceptions错误,都是在发送方法上。

因为我发送方法用的SocketAsyncEventArgs是自己new的,不是从SocketAsyncEventArgsPool池里分配的,所以发送后一直没被GC回收掉。

短短5分钟,就占用了1.5G

 

我发送的方法是这样的,微软它的的例子就没这个问题,因为它每个socket接收跟发送都是用的同一个SocketAsyncEventArgs。它是收包后,再发包,然后再收包,这个SocketAsyncEventArgs一直贯穿于里面,但实际场景根本没法跟微软那例子一样,我可能要主动发包给客户端,或者客户端发包给我,我不需要回包给客户,或者客户端发了粘包给我,我需要拆分开发两个包给客户端。所以我接收都是用的同一个SocketAsyncEventArgs,但发送是另外new的。

 public void SendBytes(AsyncUserToken userToken, byte[] bytes)
        {
            if (userToken == null || userToken.Socket == null || !userToken.Socket.Connected)
                return;
            try
            {
                //新建异步发送对象, 发送消息  
                SocketAsyncEventArgs sendArg = new SocketAsyncEventArgs();
                sendArg.UserToken = userToken;
                sendArg.SetBuffer(bytes, 0, bytes.Length);  //将数据放置进去.  
                userToken.Socket.SendAsync(sendArg);
            }
            catch (Exception e)
            {
                UIManager.LogException(e);
            }
        }

我后面也想过也在SocketAsyncEventArgsPool池里面分配,但它这个SetBuffer方法不太懂怎么用,我不知怎么把我的数据传进去,如果SetBuffer(bytes,0,bytes.Length),那它不就引用到我这个字节上,跟bufferManager的缓存数组没引用关系了。也还是会占用内存。

研究后,有两种解决方法,一种最简单,改用同步方法Send,改回后就非常稳定了

 

当然,异步方法还是可以用的,只是需要在它的回调方法里面SetBuffer(null,0,0),释放掉字节的引用,如下面这样,改完后内容也是非常稳定,基本在50M左右

public void SendBytes(AsyncUserToken userToken, byte[] bytes)
{
  if (userToken == null || userToken.Socket == null || !userToken.Socket.Connected)
     return;
  try
  {
    //新建异步发送对象, 发送消息  
    SocketAsyncEventArgs sendArg = new SocketAsyncEventArgs();
    sendArg.Completed += SendArg_Completed;
    //sendArg.UserToken = userToken;
    sendArg.SetBuffer(bytes, 0, bytes.Length);  //将数据放置进去.  
    userToken.Socket.SendAsync(sendArg);
    //userToken.Socket.Send(bytes);
  }
  catch (Exception e)
  {
    UIManager.LogException(e);
  }
}

private void SendArg_Completed(object sender, SocketAsyncEventArgs e)
{
  ProcessSend(e);
}
private void ProcessSend(SocketAsyncEventArgs e)
{
  e.SetBuffer(null, 0, 0);
  if (e.SocketError != SocketError.Success)
  {
    CloseClientSocket(e);
  }
  else
  {
    //UIManager.Log($"ProcessSend断线了{e.BytesTransferred}-{e.SocketFlags}");        
  }
}

 

posted on 2023-08-15 23:17  菜工  阅读(178)  评论(0编辑  收藏  举报

导航