C#高性能大容量SOCKET并发(十):SocketAsyncEventArgs线程模型
原文:C#高性能大容量SOCKET并发(十):SocketAsyncEventArgs线程模型
线程模型
SocketAsyncEventArgs编程模式不支持设置同时工作线程个数,使用的NET的IO线程,由NET底层提供,这点和直接使用完成端口API编程不同。NET底层IO线程也是每个异步事件都是由不同的线程返回到Completed事件,因此在Completed事件需要对用户对象进行加锁,避免同一个用户对象同时触发两个Completed事件。
void IO_Completed(object sender, SocketAsyncEventArgs asyncEventArgs)
{
AsyncSocketUserToken userToken = asyncEventArgs.UserToken as AsyncSocketUserToken;
userToken.ActiveDateTime = DateTime.Now;
try
{
lock (userToken) //避免同一个userToken同时有多个线程操作
{
if (asyncEventArgs.LastOperation == SocketAsyncOperation.Receive)
ProcessReceive(asyncEventArgs);
else if (asyncEventArgs.LastOperation == SocketAsyncOperation.Send)
ProcessSend(asyncEventArgs);
else
throw new ArgumentException("The last operation completed on the socket was not a receive or send");
}
}
catch (Exception E)
{
Program.Logger.ErrorFormat("IO_Completed {0} error, message: {1}", userToken.ConnectSocket, E.Message);
Program.Logger.Error(E.StackTrace);
}
}使用ProceXP可以看到服务端在比较忙的时候,服务的线程会越多。在Completed事件加锁有好处是后续逻辑处理都是串行的,可以不用考虑线程同步。还有一个地方需要注意的是断开超时连接,由于超时连接会调用Shutdown函数来强行中断SOCKET,因此在守护线程调用时,也需要锁住userToken对象。
public void DaemonThreadStart()
{
while (m_thread.IsAlive)
{
AsyncSocketUserToken[] userTokenArray = null;
m_asyncSocketServer.AsyncSocketUserTokenList.CopyList(ref userTokenArray);
for (int i = 0; i < userTokenArray.Length; i++)
{
if (!m_thread.IsAlive)
break;
try
{
if ((DateTime.Now - userTokenArray[i].ActiveDateTime).Milliseconds > m_asyncSocketServer.SocketTimeOutMS) //超时Socket断开
{
lock (userTokenArray[i])
{
m_asyncSocketServer.CloseClientSocket(userTokenArray[i]);
}
}
}
catch (Exception E)
{
Program.Logger.ErrorFormat("Daemon thread check timeout socket error, message: {0}", E.Message);
Program.Logger.Error(E.StackTrace);
}
} for (int i = 0; i < 60 * 1000 / 10; i++) //每分钟检测一次
{
if (!m_thread.IsAlive)
break;
Thread.Sleep(10);
}
}
}在CloseClientSocket方法中,对m_asyncSocketUserTokenPool和m_asyncSocketUserTokenList进行处理的时候都有加锁,代码如下:
public void CloseClientSocket(AsyncSocketUserToken userToken)
{
if (userToken.ConnectSocket == null)
return;
string socketInfo = string.Format("Local Address: {0} Remote Address: {1}", userToken.ConnectSocket.LocalEndPoint,
userToken.ConnectSocket.RemoteEndPoint);
Program.Logger.InfoFormat("Client connection disconnected. {0}", socketInfo);
try
{
userToken.ConnectSocket.Shutdown(SocketShutdown.Both);
}
catch (Exception E)
{
Program.Logger.ErrorFormat("CloseClientSocket Disconnect client {0} error, message: {1}", socketInfo, E.Message);
}
userToken.ConnectSocket.Close();
userToken.ConnectSocket = null; //释放引用,并清理缓存,包括释放协议对象等资源 m_maxNumberAcceptedClients.Release();
m_asyncSocketUserTokenPool.Push(userToken);
m_asyncSocketUserTokenList.Remove(userToken);
}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 void Remove(AsyncSocketUserToken userToken)
{
lock (m_list)
{
m_list.Remove(userToken);
}
}在有些性能要求更高的系统,特别是在一些C++写的完成端口中,会使用原子操作来代替锁,这样做的好处是不用进行系统内核和用户态切换,性能会高。不过技术比较偏门,不易维护,而且实际表现需要进行多方面测试,这类优化更建议优化业务逻辑,并尽量减少内存分配和释放。
DEMO下载地址:http://download.csdn.net/detail/sqldebug_fan/7467745
免责声明:此代码只是为了演示C#完成端口编程,仅用于学习和研究,切勿用于商业用途。水平有限,C#也属于初学,错误在所难免,欢迎指正和指导。邮箱地址:fansheng_hx@163.com。
C#高性能大容量SOCKET并发(十):SocketAsyncEventArgs线程模型的更多相关文章
- C#高性能大容量SOCKET并发(六):超时Socket断开(守护线程)和心跳包
原文:C#高性能大容量SOCKET并发(六):超时Socket断开(守护线程)和心跳包 守护线程 在服务端版Socket编程需要处理长时间没有发送数据的Socket,需要在超时多长时间后断开连接,我们 ...
- C#高性能大容量SOCKET并发(二):SocketAsyncEventArgs封装
原文:C#高性能大容量SOCKET并发(二):SocketAsyncEventArgs封装 1.SocketAsyncEventArgs介绍 SocketAsyncEventArgs是微软提供的高性能 ...
- C#高性能大容量SOCKET并发(转)
C#高性能大容量SOCKET并发(零):代码结构说明 C#高性能大容量SOCKET并发(一):IOCP完成端口例子介绍 C#高性能大容量SOCKET并发(二):SocketAsyncEventArgs ...
- C#高性能大容量SOCKET并发(零):代码结构说明
原文:C#高性能大容量SOCKET并发(零):代码结构说明 C#版完成端口具有以下特点: 连接在线管理(提供在线连接维护,连接会话管理,数据接收,连接断开等相关事件跟踪): 发送数据智能合并(组件会根 ...
- C#高性能大容量SOCKET并发(九):断点续传
原文:C#高性能大容量SOCKET并发(九):断点续传 上传断点续传 断点续传主要是用在上传或下载文件,一般做法是开始上传的时候,服务器返回上次已经上传的大小,如果上传完成,则返回-1:下载开始的时候 ...
- C#高性能大容量SOCKET并发(三):接收、发送
原文:C#高性能大容量SOCKET并发(三):接收.发送 异步数据接收有可能收到的数据不是一个完整包,或者接收到的数据超过一个包的大小,因此我们需要把接收的数据进行缓存.异步发送我们也需要把每个发送的 ...
- C#高性能大容量SOCKET并发(四):缓存设计
原文:C#高性能大容量SOCKET并发(四):缓存设计 在编写服务端大并发的应用程序,需要非常注意缓存设计,缓存的设计是一个折衷的结果,需要通过并发测试反复验证.有很多服务程序是在启动时申请足够的内存 ...
- C#高性能大容量SOCKET并发(十一):编写上传客户端
原文:C#高性能大容量SOCKET并发(十一):编写上传客户端 客户端封装整体框架 客户端编程基于阻塞同步模式,只有数据正常发送或接收才返回,如果发生错误则抛出异常,基于TcpClient进行封装,主 ...
- C#高性能大容量SOCKET并发(七):协议字符集
原文:C#高性能大容量SOCKET并发(七):协议字符集 UTF-8 UTF-8是UNICODE的一种变长字符编码又称万国码,由Ken Thompson于1992年创建.现在已经标准化为RFC 362 ...
随机推荐
- 小强的HTML5移动开发之路(37)——jqMobi快速入门
在<小强的HTML5移动开发之路(33)-- jqMobi基础>中我们了解了什么是jqMobi,并从官方下载了jqMobi开发包,下载后解压目录如下: 拷贝上面的/css目录./plugi ...
- HDU 1244 Max Sum Plus Plus Plus - dp
传送门 题目大意: 给一个序列,要求将序列分成m段,从左至右每一段分别长l1,l2,...lm,求最大的和是多少. 题目分析: 和最大m段子段和相似,先枚举\(i \in [1,m]\),然后$j \ ...
- 利用WPF建立自己的3d gis软件(非axhost方式)(四)在地图上添加FrameworkElement
原文:利用WPF建立自己的3d gis软件(非axhost方式)(四)在地图上添加FrameworkElement 先下载SDK:https://pan.baidu.com/s/1M9kBS6ouUw ...
- Android中判断网络是否连接并提示设置
/** * 判断网络是否连通 * @param context * @return */ public static boolean isNetworkConnected(Context contex ...
- 【JDBC】java PreparedStatement操作oracle数据库
************************************************************************ ****原文:blog.csdn.net/clark_ ...
- Asp.NetCore程序发布到CentOs(含安装部署netcore)--最佳实践
原文:Asp.NetCore程序发布到CentOs(含安装部署netcore)--最佳实践 环境 本地 win7 服务器:Virtual Box 上的Centos ssh工具: Xshell 文件传输 ...
- sgu209:Areas(计算几何)
意甲冠军: 给一些直.这架飞机被分成了很多这些线性块.每个块的需求面积封闭曲线图. 分析: ①我们应要求交点22的直线: ②每行上的交点的重排序,借此来离散一整行(正反两条边): ③对于连向一个点的几 ...
- APP和服务端-架构设计(二)
1. App架构设计经验谈:接口的设计 App与服务器的通信接口如何设计得好,需要考虑的地方挺多的,在此根据我的一些经验做一些总结分享,旨在抛砖引玉. 1.1 安全机制的设计 现在,大部分App的接口 ...
- go语言刷leetcode - 53 Maximum Subarray
package main import ( "fmt" "math" ) func maxSubArray(nums []int) int { var larg ...
- PV操作例题解析
虽然自己看了书,老师讲了课,以为对PV操作理解了,可是遇到题的时候还是不会思考.以下这道题,花了非常长时间才弄明确,如今把思路写出来,大家共同探讨下. 大家都来思考: 信号量S1.S2.S3.S4分别 ...