问题
我是一个致力于C#网络通信的搬砖人,但是今天被一个朋友提问,关于,他的软件在使用我的RRQMSocket框架时,运行24小时,内存从19变成30的问题。起初我没在意,因为即使是30Mb,也比较小,完全可以不用管。但是这使我想起了之前压力测试时发生的一个情况。
之前在压测时,100个客户端,每个客户端每秒钟发送近4000条1kb的数据。在测试期间(一分钟),内存从29变成205Mb,而且还在不断上升。
但是诡异的是,我把该代码放在自己电脑上运行时,却没有任何问题。
最后定性为微软SDK的bug。
但是今天又有了受害者。所以我得站出来,拿出有力证据。不然还以为我在胡说。
相关代码
首先,代码分3个类型测试。分别为仿RRQM的IOCP架构、常规IOCP架构、多线程阻塞架构。对应代码。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace SocketLib
{
public class RRQMIOCPMode
{
public void Start(int port)
{
//用列表存下已连接的客户端,模拟RRQM的SocketClients集合。
List<SimulationSocketClient> simulations = new List<SimulationSocketClient>();
Task.Run(() =>
{
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(new IPEndPoint(IPAddress.Any, port));
socket.Listen(10);
while (true)
{
Socket newSocket = socket.Accept();//同步接收,这个无所谓
SimulationSocketClient simulationSocketClient = new SimulationSocketClient() { Socket = newSocket };
simulations.Add(simulationSocketClient);
simulationSocketClient.BeginReceive();
}
});
}
}
class SimulationSocketClient
{
public Socket Socket { get; set; }
public void BeginReceive()
{
eventArgs = new SocketAsyncEventArgs();
eventArgs.Completed += EventArgs_Completed;
byte[] buffer = new byte[1024 * 64];
eventArgs.SetBuffer(buffer, 0, buffer.Length);
if (!Socket.ReceiveAsync(eventArgs))
{
ProcessReceived(eventArgs);
}
}
SocketAsyncEventArgs eventArgs;
private void EventArgs_Completed(object sender, SocketAsyncEventArgs e)
{
if (e.LastOperation == SocketAsyncOperation.Receive)
{
ProcessReceived(e);
}
}
private void ProcessReceived(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success && e.BytesTransferred > 0)
{
//在这里处理数据,此处不做任何处理,直接进行下次接收。
if (!Socket.ReceiveAsync(e))
{
ProcessReceived(e);
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace SocketLib
{
public class NormalIOCPMode
{
public void Start(int port)
{
Task.Run(() =>
{
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(new IPEndPoint(IPAddress.Any, port));
socket.Listen(10);
while (true)
{
Socket newSocket = socket.Accept();//同步接收,这个无所谓
SetSocket(newSocket);
}
});
}
public void SetSocket(Socket socket)
{
SocketAsyncEventArgs eventArgs = new SocketAsyncEventArgs();
eventArgs.Completed += EventArgs_Completed;
eventArgs.UserToken = socket;
byte[] buffer = new byte[1024 * 64];
eventArgs.SetBuffer(buffer, 0, buffer.Length);
if (!socket.ReceiveAsync(eventArgs))
{
ProcessReceived(eventArgs);
}
}
private void EventArgs_Completed(object sender, SocketAsyncEventArgs e)
{
if (e.LastOperation == SocketAsyncOperation.Receive)
{
ProcessReceived(e);
}
}
private void ProcessReceived(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success && e.BytesTransferred > 0)
{
//在这里处理数据,此处不做任何处理,直接进行下次接收。
if (!((Socket)e.UserToken).ReceiveAsync(e))
{
ProcessReceived(e);
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace SocketLib
{
public class MultithreadingMode
{
public void Start(int port)
{
Task.Run(() =>
{
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(new IPEndPoint(IPAddress.Any, port));
socket.Listen(10);
while (true)
{
Socket newSocket = socket.Accept();//同步接收,这个无所谓
SetSocket(newSocket);
}
});
}
public void SetSocket(Socket socket)
{
Thread thread = new Thread(()=>
{
byte[] buffer = new byte[1024*64];
while (true)
{
int r = socket.Receive(buffer);
//在这里处理数据,此处不做任何处理,直接进行下次接收。
}
});
thread.Name = socket.RemoteEndPoint.ToString();
thread.IsBackground = true;
thread.Start();
}
}
}
测试方法
方法有两个,既然指出了Winform的罪过,那就先拿Winform开刀。
建立winform项目,选择net461架构,不然说我欺负你。
界面
代码
using SocketLib;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
switch (this.comboBox1.SelectedIndex)
{
case 0:
{
RRQMIOCPMode mode = new RRQMIOCPMode();
mode.Start(7790);
break;
}
case 1:
{
NormalIOCPMode mode = new NormalIOCPMode();
mode.Start(7790);
break;
}
case 2:
{
MultithreadingMode mode = new MultithreadingMode();
mode.Start(7790);
break;
}
default:
break;
}
MessageBox.Show("启动成功");
}
}
}
第二种,就用控制台
using SocketLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("0.仿RRQM的IOCP架构");
Console.WriteLine("1.常规IOCP架构");
Console.WriteLine("2.多线程阻塞架构");
switch (Console.ReadLine())
{
case "0":
{
RRQMIOCPMode mode = new RRQMIOCPMode();
mode.Start(7790);
break;
}
case "1":
{
NormalIOCPMode mode = new NormalIOCPMode();
mode.Start(7790);
break;
}
case "2":
{
MultithreadingMode mode = new MultithreadingMode();
mode.Start(7790);
break;
}
default:
break;
}
Console.WriteLine("启动成功");
Console.ReadKey();
}
}
}
测试客户端,使用的是RRQMBox的压力测试,这个不用关心,因为无论如何,也与客户端没关系。不过想自己测试的话,可以去https://gitee.com/RRQM_OS/RRQMBox下载。
Winform测试结果
在仿RRQM的IOCP模式下,和正常IOCP模式下,测试1分钟,内存从29升到219Mb。
在多线程模式下,测试1分钟,内存从29升到50Mb,就不再升,且没有再升的迹象。
控制台测试结果
控制台下,一切正常。
####相关资源
最后再说一下,如果你在自己电脑测试没有问题的话,那就没有问题,因为这就是很诡异。__