原文:C#高性能大容量SOCKET并发(四):缓存设计

在编写服务端大并发的应用程序,需要非常注意缓存设计,缓存的设计是一个折衷的结果,需要通过并发测试反复验证。有很多服务程序是在启动时申请足够的内存空间,避免在运行期间再申请空间,这种是固定空间申请。还有一种是在运行期间动态增长的缓存设计,随着运行动态申请内存,这种事动态空间申请。这两种机制各有优劣,固定空间申请优点是效率高,运行稳定,缺点是对应用场景具有限制;动态空间申请优点是能适应更好的应用场景,缺点是效率相对低一些,并发数降一些;这种性能下降不是太明显,毕竟申请释放内存的效率NET是有优化的,具体需要根据应用场景设计。

在C#版IOCP中我们结合了固定缓存设计和动态缓存设计,其中服务端支持连接数使用了固定缓存设计(AsyncSocketUserTokenPool),根据程序启动时设置的最大连接数申请固定个数的对象。其中接收数据缓存(DynamicBufferManager m_receiveBuffer)、发送数据列表(AsyncSendBufferManager m_sendBuffer)是随着接收数据大小动态增长。

固定缓存设计

固定缓存设计我们需要建立一个列表进行,并在初始化的时候加入到列表中,实现非常简单,列出代码供参考。

    public class AsyncSocketUserTokenPool
{
private Stack<AsyncSocketUserToken> m_pool; public AsyncSocketUserTokenPool(int capacity)
{
m_pool = new Stack<AsyncSocketUserToken>(capacity);
} public void Push(AsyncSocketUserToken item)
{
if (item == null)
{
throw new ArgumentException("Items added to a AsyncSocketUserToken cannot be null");
}
lock (m_pool)
{
m_pool.Push(item);
}
} public AsyncSocketUserToken Pop()
{
lock (m_pool)
{
return m_pool.Pop();
}
} public int Count
{
get { return m_pool.Count; }
}
}

初始化加入列表的代码如下:

        public void Init()
{
AsyncSocketUserToken userToken;
for (int i = 0; i < m_numConnections; i++) //按照连接数建立读写对象
{
userToken = new AsyncSocketUserToken(m_receiveBufferSize);
userToken.ReceiveEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
userToken.SendEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
m_asyncSocketUserTokenPool.Push(userToken);
}
}

动态缓存设计

动态缓存是随着数据量大小动态增长,申请的内存在运行过程中重复利用,不释放,这样对内存只进行读写,不进行申请和释放,整体性能较高,因为内存申请释放比读写的效率低很多,因为申请释放内存需要进行加锁,进行系统内核和用户切换,因此使用动态缓存可以降低内核和用户态切换,提高性能。动态缓存的代码如下:

public class DynamicBufferManager
{
public byte[] Buffer { get; set; } //存放内存的数组
public int DataCount { get; set; } //写入数据大小 public DynamicBufferManager(int bufferSize)
{
DataCount = 0;
Buffer = new byte[bufferSize];
} public int GetDataCount() //获得当前写入的字节数
{
return DataCount;
} public int GetReserveCount() //获得剩余的字节数
{
return Buffer.Length - DataCount;
} public void Clear(int count) //清理指定大小的数据
{
if (count >= DataCount) //如果需要清理的数据大于现有数据大小,则全部清理
{
DataCount = 0;
}
else
{
for (int i = 0; i < DataCount - count; i++) //否则后面的数据往前移
{
Buffer[i] = Buffer[count + i];
}
DataCount = DataCount - count;
}
} public void WriteBuffer(byte[] buffer, int offset, int count)
{
if (GetReserveCount() >= count) //缓冲区空间够,不需要申请
{
Array.Copy(buffer, offset, Buffer, DataCount, count);
DataCount = DataCount + count;
}
else //缓冲区空间不够,需要申请更大的内存,并进行移位
{
int totalSize = Buffer.Length + count - GetReserveCount(); //总大小-空余大小
byte[] tmpBuffer = new byte[totalSize];
Array.Copy(Buffer, 0, tmpBuffer, 0, DataCount); //复制以前的数据
Array.Copy(buffer, offset, tmpBuffer, DataCount, count); //复制新写入的数据
DataCount = DataCount + count;
Buffer = tmpBuffer; //替换
}
} public void WriteBuffer(byte[] buffer)
{
WriteBuffer(buffer, 0, buffer.Length);
} public void WriteShort(short value, bool convert)
{
if (convert)
{
value = System.Net.IPAddress.HostToNetworkOrder(value); //NET是小头结构,网络字节是大头结构,需要客户端和服务器约定好
}
byte[] tmpBuffer = BitConverter.GetBytes(value);
WriteBuffer(tmpBuffer);
} public void WriteInt(int value, bool convert)
{
if (convert)
{
value = System.Net.IPAddress.HostToNetworkOrder(value); //NET是小头结构,网络字节是大头结构,需要客户端和服务器约定好
}
byte[] tmpBuffer = BitConverter.GetBytes(value);
WriteBuffer(tmpBuffer);
} public void WriteLong(long value, bool convert)
{
if (convert)
{
value = System.Net.IPAddress.HostToNetworkOrder(value); //NET是小头结构,网络字节是大头结构,需要客户端和服务器约定好
}
byte[] tmpBuffer = BitConverter.GetBytes(value);
WriteBuffer(tmpBuffer);
} public void WriteString(string value) //文本全部转成UTF8,UTF8兼容性好
{
byte[] tmpBuffer = Encoding.UTF8.GetBytes(value);
WriteBuffer(tmpBuffer);
}
}

异步发送列表

异步发送列表是在动态缓存的基础上加了一个列表管理,记录每个包的位置信息,并提供管理函数,代码示例如下:

namespace SocketAsyncSvr
{
struct SendBufferPacket
{
public int Offset;
public int Count;
} //由于是异步发送,有可能接收到两个命令,写入了两次返回,发送需要等待上一次回调才发下一次的响应
public class AsyncSendBufferManager
{
private DynamicBufferManager m_dynamicBufferManager;
public DynamicBufferManager DynamicBufferManager { get { return m_dynamicBufferManager; } }
private List<SendBufferPacket> m_sendBufferList;
private SendBufferPacket m_sendBufferPacket; public AsyncSendBufferManager(int bufferSize)
{
m_dynamicBufferManager = new DynamicBufferManager(bufferSize);
m_sendBufferList = new List<SendBufferPacket>();
m_sendBufferPacket.Offset = 0;
m_sendBufferPacket.Count = 0;
} public void StartPacket()
{
m_sendBufferPacket.Offset = m_dynamicBufferManager.DataCount;
m_sendBufferPacket.Count = 0;
} public void EndPacket()
{
m_sendBufferPacket.Count = m_dynamicBufferManager.DataCount - m_sendBufferPacket.Offset;
m_sendBufferList.Add(m_sendBufferPacket);
} public bool GetFirstPacket(ref int offset, ref int count)
{
if (m_sendBufferList.Count <= 0)
return false;
offset = m_sendBufferList[0].Offset;
count = m_sendBufferList[0].Count;
return true;
} public bool ClearFirstPacket()
{
if (m_sendBufferList.Count <= 0)
return false;
int count = m_sendBufferList[0].Count;
m_dynamicBufferManager.Clear(count);
m_sendBufferList.RemoveAt(0);
return true;
} public void ClearPacket()
{
m_sendBufferList.Clear();
m_dynamicBufferManager.Clear(m_dynamicBufferManager.DataCount);
}
}
}

DEMO下载地址:http://download.csdn.net/detail/sqldebug_fan/7467745

免责声明:此代码只是为了演示C#完成端口编程,仅用于学习和研究,切勿用于商业用途。水平有限,C#也属于初学,错误在所难免,欢迎指正和指导。邮箱地址:fansheng_hx@163.com。

C#高性能大容量SOCKET并发(四):缓存设计的更多相关文章

  1. C#高性能大容量SOCKET并发(转)

    C#高性能大容量SOCKET并发(零):代码结构说明 C#高性能大容量SOCKET并发(一):IOCP完成端口例子介绍 C#高性能大容量SOCKET并发(二):SocketAsyncEventArgs ...

  2. C#高性能大容量SOCKET并发(零):代码结构说明

    原文:C#高性能大容量SOCKET并发(零):代码结构说明 C#版完成端口具有以下特点: 连接在线管理(提供在线连接维护,连接会话管理,数据接收,连接断开等相关事件跟踪): 发送数据智能合并(组件会根 ...

  3. C#高性能大容量SOCKET并发(二):SocketAsyncEventArgs封装

    原文:C#高性能大容量SOCKET并发(二):SocketAsyncEventArgs封装 1.SocketAsyncEventArgs介绍 SocketAsyncEventArgs是微软提供的高性能 ...

  4. C#高性能大容量SOCKET并发(十一):编写上传客户端

    原文:C#高性能大容量SOCKET并发(十一):编写上传客户端 客户端封装整体框架 客户端编程基于阻塞同步模式,只有数据正常发送或接收才返回,如果发生错误则抛出异常,基于TcpClient进行封装,主 ...

  5. C#高性能大容量SOCKET并发(十):SocketAsyncEventArgs线程模型

    原文:C#高性能大容量SOCKET并发(十):SocketAsyncEventArgs线程模型 线程模型 SocketAsyncEventArgs编程模式不支持设置同时工作线程个数,使用的NET的IO ...

  6. C#高性能大容量SOCKET并发(六):超时Socket断开(守护线程)和心跳包

    原文:C#高性能大容量SOCKET并发(六):超时Socket断开(守护线程)和心跳包 守护线程 在服务端版Socket编程需要处理长时间没有发送数据的Socket,需要在超时多长时间后断开连接,我们 ...

  7. C#高性能大容量SOCKET并发(五):粘包、分包、解包

    原文:C#高性能大容量SOCKET并发(五):粘包.分包.解包 粘包 使用TCP长连接就会引入粘包的问题,粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一 ...

  8. C#高性能大容量SOCKET并发(三):接收、发送

    原文:C#高性能大容量SOCKET并发(三):接收.发送 异步数据接收有可能收到的数据不是一个完整包,或者接收到的数据超过一个包的大小,因此我们需要把接收的数据进行缓存.异步发送我们也需要把每个发送的 ...

  9. C#高性能大容量SOCKET并发(九):断点续传

    原文:C#高性能大容量SOCKET并发(九):断点续传 上传断点续传 断点续传主要是用在上传或下载文件,一般做法是开始上传的时候,服务器返回上次已经上传的大小,如果上传完成,则返回-1:下载开始的时候 ...

随机推荐

  1. JS调用ATL COM中的C++接口的做法

    作者:朱金灿 来源:http://blog.csdn.net/clever101 首先创建一个ATL COM对象,其过程参考下面文章: C#调用ATLCOM 其实给COM对象添加方法和属性可以不用界面 ...

  2. js如何操作表格(常用属性方法汇总)

    js如何操作表格(常用属性方法汇总) 一.总结 一句话总结: 二.表格相关的属性和方法 1.1 Table 对象集合 cells[] 返回包含表格中所有单元格的一个数组. 语法:tableObject ...

  3. Newtonsoft.Json 序列化反序列化

    public class People { public string name { get; set; } public string age { get; set; } public string ...

  4. 小强的HTML5移动开发之路(21)—— PhoneGap

    一.PhoneGap是什么 PhoneGap 是一个用基于 HTML,CSS 和 JavaScript 的,创建移动跨平台移动应用程序的快速开发框架.它使开发者能够利用 iPhone,Android, ...

  5. 在Windows中安装MinGW-w64(有图,一步一步)

    在Windows中安装MinGW-w64 发表回复 如需配合Sublime Text 3编译C程序, 请参考本站文章: 使用Sublime Text 3与MinGW-w64编译C语言程序 MinGW, ...

  6. C# powshell 调用

    原文:C# powshell 调用 本文告诉大家如何在 ps 脚本使用 C# 代码. 首先创建一个 C# 的控制台项目,注意修改输出为类库. 现在的 Powershell 还不支持 dotnet co ...

  7. Windows PowerShell 学习之——Cmdlet处理生命周期

    这一次介绍一下Cmdlet处理过程的生命周期 总共分为六个部分 1.概述 2. 命令行输入绑定参数(parameters) 3. 开始指令处理 4. 接受管道输入绑定参数 5. 处理记录 6. 处理记 ...

  8. Xcode 4.5( iOS6 SDK)、旧版本号cocos2d,支持iPhone5解析度

    支持iPhone5全屏 1假设没有支持iPhone5是否.直接运行程序可以准备提交.开放iPhone5模拟器,你会发现上面有黑色的程序.没有矩形. 2真正运行该程序时,.你会发现程序回程屏幕高度.它是 ...

  9. .NET/C# 使窗口永不激活(No Activate 永不获得焦点)

    原文 .NET/C# 使窗口永不激活(No Activate 永不获得焦点) 有些窗口天生就是为了辅助其它程序而使用的,典型的如“输入法窗口”.这些窗口不希望抢夺其它窗口的焦点. 有 Win32 方法 ...

  10. 使用bcc32做在windowXP上qt3.2.1编译环境的配置

    1.安装borland C++编译器,编译器文件所在目录下的文件如下: 其中bcc32.cfg和ilink32.cfg文件是自己加进去的,bcc32.cfg内容是-I"C:\Borland\ ...