写自己的Socket框架(一)
本系列仅介绍可用于生产环境的C#异步Socket框架,如果您在其他地方看到类似的代码,不要惊讶,那可能就是我在参考开源代码时,直接“剽窃”过来的。
1、在脑海里思考一下整个socket的链接的处理流程,于是便有了下图。

2、首先就开始监听,代码如下:
public override bool Start()
{
this._socket = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//设置KeeyAlive,如果客户端不主动发消息时,Tcp本身会发一个心跳包,来通知服务器,这是一个保持通讯的链接。
//避免等到下一次通讯时,才知道链接已经断开。
this._socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
this._socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);
try
{
this._socket.Bind(base.SocketConfig.Point);
this._socket.Listen(base.SocketConfig.Backlog); this._socket_args = new SocketAsyncEventArgs();
this._socket_args.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptSocketCompleted); //在链接过来的时候,如果IO没有挂起,则AcceptAsync为False,表明同步完成。
if (!this._socket.AcceptAsync(this._socket_args))
{
AcceptSocketCompleted(this._socket, this._socket_args);
}
return true;
}
catch (Exception ex)
{
return false;
}
} void AcceptSocketCompleted(object sender, SocketAsyncEventArgs e)
{
System.Net.Sockets.Socket socket = null;
if (e.SocketError != SocketError.Success)
{
return;
}
else
{
socket = e.AcceptSocket;
}
e.AcceptSocket = null;
bool willRaiseEvent = false;
try
{
//继续监听该端口,在处理逻辑时,不影响其他链接的数据传送。
willRaiseEvent = this._socket.AcceptAsync(e);
}
catch (Exception ex)
{
willRaiseEvent = true;
} if (socket != null)
OnNewClientAccepted(socket, null); if (!willRaiseEvent)
AcceptSocketCompleted(null, e);
}
3、这个时候链接过来了,就要开始入队列了,如果没有这方面的需求,这一步可以忽略,代码如下:
public class SocketProxy
{
public System.Net.Sockets.Socket Client; public DateTime Timeout = DateTime.Now; } public class SocketConnectionQueue : IDisposable
{
private Queue<SocketProxy> _queue; private readonly object _syncObject = new object(); private bool _isStop = false; private Thread _thread; public Action<SocketProxy> Connected; public SocketConnectionQueue()
{
if (_queue == null)
{
_queue = new Queue<SocketProxy>();
} if (_thread == null)
{
_thread = new Thread(Thread_Work)
{
IsBackground = true,
Priority = ThreadPriority.Highest };
_thread.Start();
}
} public void Push(SocketProxy connect)
{
lock (_syncObject)
{
if (_queue != null)
{
_queue.Enqueue(connect);
}
}
} public void Thread_Work()
{
while (!_isStop)
{
SocketProxy[] socketConnect = null;
lock (_syncObject)
{
if (_queue.Count > )
{
socketConnect = new SocketProxy[_queue.Count];
_queue.CopyTo(socketConnect, );
_queue.Clear();
}
} if (socketConnect != null && socketConnect.Length > )
{
foreach (var client in socketConnect)
{
if (Connected != null)
{
Connected.Invoke(client);
}
}
}
Thread.Sleep();
}
} public void Dispose()
{
_isStop = true;
if (_thread != null)
{
_thread.Join();
}
}
}
4、入完队列,就要开始从链接池子里面分配资源了,你也可以不做链接池,在每次请求过来的时候去实例化一个链接,然后将这个链接入池,我的做法是在程序初始化的时候就分配好一定的资源,代码如下:
public class SocketConnectionPool : IDisposable
{
private ServerConfig _serverConfig; public IAppServer AppServer; private ConcurrentStack<SocketConnection> _connectPool; private long connect_id = ;
private byte[] _buffer;
private readonly object _syncObject = new object(); private SocketConnectionQueue _queue; public Action<System.Net.Sockets.Socket, SocketConnection> Connected; public long GenerateId()
{
if (connect_id == long.MaxValue)
{
connect_id = ;
}
connect_id++;
return connect_id;
} public SocketConnectionPool(IAppServer server)
{
this.AppServer = server;
this._serverConfig = server.AppConfig; } public void Init()
{
var connects = new List<SocketConnection>(this._serverConfig.MaxConnectionNumber);
_buffer = new byte[this._serverConfig.BufferSize];
SocketAsyncEventArgs arg;
for (var i = ; i < this._serverConfig.MaxConnectionNumber; i++)
{
arg = new SocketAsyncEventArgs();
arg.SetBuffer(_buffer, , _buffer.Length);
connects.Add(new SocketConnection(arg, this));
}
_connectPool = new ConcurrentStack<SocketConnection>(connects);
if (_queue == null)
{
_queue = new SocketConnectionQueue();
} _queue.Connected = OnConnected;
} public void Push(System.Net.Sockets.Socket socket)
{
SocketProxy proxy = new SocketProxy()
{
Client = socket
};
_queue.Push(proxy);
} public void OnConnected(SocketProxy proxy)
{
//如果发现队列里面的链接,在Timeout时间内,都没有分配到资源,则关掉链接并丢弃。
int timeout = (int)(DateTime.Now - proxy.Timeout).TotalSeconds;
if (timeout >= this._serverConfig.Timeout)
{
proxy.Client.Close();
return;
}
else
{
//没有分配到资源重新入列。
SocketConnection connect = this.GetConnectionFromPool();
if (connect == null)
{
_queue.Push(proxy);
}
else
{
if (this.Connected != null)
{
this.Connected(proxy.Client, connect);
}
}
}
} /// <summary>
/// 从链接池去取链接(LIFO)
/// </summary>
/// <returns></returns>
public SocketConnection GetConnectionFromPool()
{
//_queue.Push();
SocketConnection connect;
if (!_connectPool.TryPop(out connect))
{
return null;
}
lock (_syncObject)
{
long connect_id = this.GenerateId();
connect.ConnectId = connect_id;
}
return connect;
}
/// <summary>
/// 释放链接,并放回链接池
/// </summary>
/// <param name="connect"></param>
public void ReleaseConnection(SocketConnection connect)
{
_connectPool.Push(connect);
LogHelper.Debug(connect.ConnectId + "放回ConnectPool");
} public void Dispose()
{
_queue.Dispose();
}
}
在Init()里面初始化了很多个SocketConnection,这个就是我们用来管理具体的单个链接的class,代码如下:
public class SocketConnection
{
public SocketFlag Flag { get; private set; } public SocketConnectionPool Pool { get { return _pool; } private set { } }
private SocketConnectionPool _pool; public SocketAsyncEventArgs RecevieEventArgs { get; set; } public long ConnectId { get; set; } public SocketConnection()
{
this.Flag = SocketFlag.Error;
} public SocketConnection(SocketAsyncEventArgs args, SocketConnectionPool pool)
{
RecevieEventArgs = args;
RecevieEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(SocketEventArgs_Completed); this.Flag = SocketFlag.Busy;
this._pool = pool;
} void SocketEventArgs_Completed(object sender, SocketAsyncEventArgs e)
{
var socketSession = e.UserToken as SocketSession;
if (socketSession == null)
{
this.Flag = SocketFlag.Error;
this.Close();
return;
} switch (e.LastOperation)
{
case SocketAsyncOperation.Receive:
socketSession.ReceiveData(e);
break;
default:
break;
}
} public void Initialise(SocketSession session)
{
this.RecevieEventArgs.UserToken = session;
this.Flag = SocketFlag.Busy; session.Closed += () =>
{
this.Close();
};
} public void Reset()
{
//ConnectId = 0;
this.RecevieEventArgs.UserToken = null;
this.Flag = SocketFlag.Idle;
} private void Close()
{
this.Reset();
LogHelper.Debug(ConnectId + " reset");
this._pool.ReleaseConnection(this);
}
}
写自己的Socket框架(一)的更多相关文章
- linux可用的跨平台C# .net standard2.0 写的高性能socket框架
能在window(IOCP)/linux(epoll)运行,基于C# .net standard2.0 写的socket框架,可使用于.net Framework/dotnet core程序集,.使用 ...
- 写自己的Socket框架(三)
在通信写完了以后,应用层接收到Socket抛上来的byte[],这个时候对于实际的写逻辑的开发者来说,这样的数据并不友好,我们就需要在应用层统一一个包的规则(应用层协议),处理完以后,然后再传给实际的 ...
- 写自己的socket框架(二)
1.开始正常监听以后,就要开始接受数据了,整体流程图如下: 2.上一节看到我们在程序初始化的时候,初始化了很多个SocketConnection,用于管理客户端的链接,那应用层如何来操作,又什么时候来 ...
- ZYSocket 4.3.5 SOCKET框架组 发布[NEW]
最新代码请到 github: https://github.com/luyikk/ZYSOCKET 更新 4.3.5更新说明: 修复各种BUG. 重写了一份 protobuf-net 有什么用呢,不需 ...
- Workerman:PHP的socket框架
hi,我们今天来讲讲Workerman,什么是Workerman呢? 看看官网上的介绍 Workerman是一款开源高性能异步PHP socket框架.支持高并发,超高稳定性,被广泛的用于手机app. ...
- 看过《大湿教我写.net通用权限框架(1)之菜单导航篇》之后发生的事(续)——主界面
引言 在UML系列学习中的小插曲:看过<大湿教我写.net通用权限框架(1)之菜单导航篇>之后发生的事 在上篇中只拿登录界面练练手,不把主界面抠出来,实在难受,严重的强迫症啊.之前一直在总 ...
- atitit.软件开发--socket框架选型--netty vs mina j
atitit.软件开发--socket框架选型--netty vs mina j . Netty是由JBOSS提供的一个java开源框架 Apache mina 三.文档比较 mina文档多,,, 好 ...
- 简洁实用Socket框架DotNettySocket
目录 简介 产生背景 使用方式 TcpSocket WebSocket UdpSocket 结尾 简介 DotNettySocket是一个.NET跨平台Socket框架(支持.NET4.5+及.NET ...
- 自己动手写Android插件化框架
自己动手写Android插件化框架 转 http://www.imooc.com/article/details/id/252238 最近在工作中接触到了Android插件内的开发,发现自己这种技 ...
随机推荐
- 【Machine Learning】KNN算法虹膜图片识别
K-近邻算法虹膜图片识别实战 作者:白宁超 2017年1月3日18:26:33 摘要:随着机器学习和深度学习的热潮,各种图书层出不穷.然而多数是基础理论知识介绍,缺乏实现的深入理解.本系列文章是作者结 ...
- 【知识必备】内存泄漏全解析,从此拒绝ANR,让OOM远离你的身边,跟内存泄漏say byebye
一.写在前面 对于C++来说,内存泄漏就是new出来的对象没有delete,俗称野指针:而对于java来说,就是new出来的Object放在Heap上无法被GC回收:而这里就把我之前的一篇内存泄漏的总 ...
- Enterprise Solution 3.1 企业应用开发框架 .NET ERP/CRM/MIS 开发框架,C/S架构,SQL Server + ORM(LLBL Gen Pro) + Infragistics WinForms
行业:基于数据库的制造行业管理软件,包含ERP.MRP.CRM.MIS.MES等企业管理软件 数据库平台:SQL Server 2005或以上 系统架构:C/S 开发技术 序号 领域 技术 1 数据库 ...
- android http 抓包
有时候想开发的时候想看APP发出的http请求和响应是什么,这就需要抓包了,这可以得到一些不为人知的api,比如还可以干些“坏事”... 需要工具: Fiddler2 抓包(点击下载) Android ...
- 如何进行python性能分析?
在分析python代码性能瓶颈,但又不想修改源代码的时候,ipython shell以及第三方库提供了很多扩展工具,可以不用在代码里面加上统计性能的装饰器,也能很方便直观的分析代码性能.下面以我自己实 ...
- 代码的坏味道(15)——冗余类(Lazy Class)
坏味道--冗余类(Lazy Class) 特征 理解和维护类总是费时费力的.如果一个类不值得你花费精力,它就应该被删除. 问题原因 也许一个类的初始设计是一个功能完全的类,然而随着代码的变迁,变得没什 ...
- 游走 bzoj 3143
游走(2s 128MB)walk [问题描述] [输入格式] [输出格式] [样例输入] 3 3 2 3 1 2 1 3 [样例输出] 3.333 [样例说明] 题解: 主要算法:贪心:高斯消元: 题 ...
- ie6 ie7 ie8 ie9兼容问题终极解决方案
放下包袱,解决低版本兼容问题 这是一个老生常谈的问题,自然解决这个问题的方案也比较多,下面整理了一些解决方法: 1.强制使用高版本渲染模式. 强制使用Edge模式来解析网页代码 <meta ...
- 【干货分享】流程DEMO-离职流程
流程名: 离职申请 流程相关文件: 流程包.xml WebService业务服务.xml WebService.asmx WebService.cs 流程说明: 流程中集成了webservic ...
- Android开发学习—— Fragment
#Fragment* 用途:在一个Activity里切换界面,切换界面时只切换Fragment里面的内容* 生命周期方法跟Activity一致,可以理解把其为就是一个Activity* 定义布局文件作 ...