异步tcp通信——APM.Core 服务端概述
为什么使用异步
异步线程是由线程池负责管理,而多线程,我们可以自己控制,当然在多线程中我们也可以使用线程池。就拿网络扒虫而言,如果使用异步模式去实现,它使用线程池进行管理。异步操作执行时,会将操作丢给线程池中的某个工作线程来完成。当开始I/O操作的时候,异步会将工作线程还给线程池,这意味着获取网页的工作不会再占用任何CPU资源了。直到异步完成,即获取网页完毕,异步才会通过回调的方式通知线程池。可见,异步模式借助于线程池,极大地节约了CPU的资源。
注:DMA(Direct Memory Access)直接内存存取,顾名思义DMA功能就是让设备可以绕过处理器,直接由内存来读取资料。通过直接内存访问的数据交换几乎可以不损耗CPU的资源。在硬件中,硬盘、网卡、声卡、显卡等都有直接内存访问功能。异步编程模型就是让我们充分利用硬件的直接内存访问功能来释放CPU的压力。
两者的应用场景:
计算密集型工作,采用多线程。
IO密集型工作,采用异步机制。
C#中实现异步tcp通信
socket中仅仅需要将Blocking=false即可轻松实现异步,部分示例如下:
/// <summary>
/// 启动tcp监听
/// </summary>
public void Start()
{
if (!_isStarted)
{
_isStarted = true;
_server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); #region socket配置
LingerOption lingerOption = new LingerOption(true, );
_server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, lingerOption);
#endregion _server.Blocking = false;
_server.ExclusiveAddressUse = false;
_server.Bind(new IPEndPoint(IPAddress.Any, this._port));
_server.Listen();
Parallel.For(, , i =>
{
_server.BeginAccept(new AsyncCallback(ProcessAccept), _server);
});
}
}
tcp异步中处理接io操作最关键的参数:IAsyncResult,使用一般用begin开始,end结束。
接收数据处理如下:
/// <summary>
/// 处理传入的连接请求
/// </summary>
private void ProcessAccept(IAsyncResult ar)
{
var s = (Socket)ar.AsyncState;
var remote = s.EndAccept(ar);
var user = new UserToken(this._maxBufferSize) { ID = remote.RemoteEndPoint.ToString(), Client = remote };
remote.BeginReceive(user.ReceiveBuffer, , user.ReceiveBuffer.Length, SocketFlags.None, new AsyncCallback(ProcessReceive),
user);
s.BeginAccept(new AsyncCallback(ProcessAccept), s);
}
private void ProcessReceive(IAsyncResult ar)
{
var user = (UserToken)ar.AsyncState;
var remote = user.Client;
try
{
if (remote.Connected)
{
var ns = remote.EndReceive(ar); if (ns > )
{
var buffer = new byte[ns]; Buffer.BlockCopy(user.ReceiveBuffer, , buffer, , buffer.Length); user.UnPackage(buffer, (p) =>
{
Interlocked.Increment(ref this._receiveCount);
this.RaiseOnOnReceived(user, p);
}); user.ClearReceiveBuffer(); buffer = null; remote.BeginReceive(user.ReceiveBuffer, , user.ReceiveBuffer.Length, SocketFlags.None, new AsyncCallback(ProcessReceive), user);
}
}
else
{
this.RaiseOnDisConnected(user, new Exception("客户端已断开连接"));
this.CloseClient(user);
}
}
catch (SocketException sex)
{
this.RaiseOnDisConnected(user, sex);
this.CloseClient(user);
}
catch (Exception ex)
{
this.RaiseOnError(user, ex);
this.CloseClient(user);
}
}
发送数据处理如下:
/// <summary>
/// 发送信息
/// </summary>
/// <param name="remote"></param>
/// <param name="data"></param>
/// <param name="type"></param>
/// <param name="auth"></param>
private void SendAsync(UserToken remote, byte[] data, TransportType type = TransportType.Heart)
{
try
{
using (var pakage = new TcpPackage(data, type, remote.Auth))
{
remote.Client.BeginSend(pakage.Data, , pakage.Data.Length, SocketFlags.None, new AsyncCallback(EndSend), remote);
} }
catch (SocketException sex)
{
this.RaiseOnDisConnected(remote, sex);
}
catch (Exception ex)
{
this.RaiseOnError(remote, ex);
}
}
private void EndSend(IAsyncResult ar)
{
var remote = (UserToken)ar.AsyncState;
remote.Client.EndSend(ar);
Interlocked.Increment(ref this._sendCount);
}
心跳、消息、文件等逻辑都可以基于发送逻辑来完成
/// <summary>
/// 回复心跳
/// </summary>
/// <param name="remote"></param>
/// <param name="package"></param>
private void ReplyHeart(UserToken remote, TcpPackage package)
{
this.SendAsync(remote, null, TransportType.Heart);
}
/// <summary>
/// 发送信息
/// </summary>
/// <param name="remote"></param>
/// <param name="msg"></param>
public void SendMsg(UserToken remote, byte[] msg)
{
this.SendAsync(remote, msg, TransportType.Message);
}
/// <summary>
/// 发送文件
/// </summary>
/// <param name="remote"></param>
/// <param name="filePath"></param>
public void SendFile(UserToken remote, string filePath)
{
using (var file = new TransferFileInfo()
{
ID = remote.ID,
FileBytes = File.ReadAllBytes(filePath),
Name = filePath.Substring(filePath.LastIndexOf("\\") + ),
CreateTime = DateTime.Now.Ticks
})
{
var buffer = TransferFileInfo.Serialize(file);
this.SendAsync(remote, buffer, TransportType.File);
buffer = null;
}
}
/// <summary>
/// 发送文件
/// </summary>
/// <param name="remote"></param>
/// <param name="fileName"></param>
/// <param name="file"></param>
public void SendFile(UserToken remote, string fileName, byte[] file)
{
using (var fileInfo = new TransferFileInfo()
{
ID = remote.ID,
FileBytes = file,
Name = fileName,
CreateTime = DateTime.Now.Ticks
})
{
var buffer = TransferFileInfo.Serialize(fileInfo);
this.SendAsync(remote, buffer, TransportType.File);
buffer = null;
}
}
异步tcp通信——APM.Core 服务端概述
异步tcp通信——APM.Core 解包
异步tcp通信——APM.Server 消息推送服务的实现
异步tcp通信——APM.ConsoleDemo
转载请标明本文来源:http://www.cnblogs.com/yswenli/
更多内容欢迎star作者的github:https://github.com/yswenli/APM
如果发现本文有什么问题和任何建议,也随时欢迎交流~
异步tcp通信——APM.Core 服务端概述的更多相关文章
- 异步tcp通信——APM.Core 解包
TCP通信解包 虽说这是一个老生长谈的问题,不过网上基本很少见完整业务:或多或少都没有写完或者存在bug.接收到的数据包可以简单分成:小包.大包.跨包三种情况,根据这三种情况作相对应的拆包处理,示例如 ...
- 异步tcp通信——APM.Server 消息推送服务的实现
消息推送服务 服务器推送目前流行就是私信.发布/订阅等模式,基本上都是基于会话映射,消息对列等技术实现的:高性能.分布式可以如下解决:会话映射可采用redis cluster等技术实现,消息对列可使用 ...
- 异步tcp通信——APM.ConsoleDemo
APM测试 俗话说麻雀虽小,五脏俱全.apm虽然简单,但是可以实现单机高性能消息推送(可以采用redis.kafka等改造成大型分布式消息推送服务器). 测试demo: using System; u ...
- TCP协议下的服务端并发,GIL全局解释器锁,死锁,信号量,event事件,线程q
TCP协议下的服务端并发,GIL全局解释器锁,死锁,信号量,event事件,线程q 一.TCP协议下的服务端并发 ''' 将不同的功能尽量拆分成不同的函数,拆分出来的功能可以被多个地方使用 TCP服务 ...
- Vue.js与 ASP.NET Core 服务端渲染功能整合
http://mgyongyosi.com/2016/Vuejs-server-side-rendering-with-aspnet-core/ 原作者:Mihály Gyöngyösi 译者:oop ...
- 网络编程 UDP协议 TCP局域网客户端与服务端上传下载电影示例
UDP协议 (了解) 称之为数据包协议,又称不可靠协议. 特点: 1) 不需要建立链接. 2) 不需要知道对方是否收到. 3) 数据不安全 4) 传输速度快 5)能支持并发 6) 不会粘包 7) 无需 ...
- c++ 网络编程(一)TCP/UDP windows/linux 下入门级socket通信 客户端与服务端交互代码
原文作者:aircraft 原文地址:https://www.cnblogs.com/DOMLX/p/9601511.html c++ 网络编程(一)TCP/UDP 入门级客户端与服务端交互代码 网 ...
- 实验09——java基于TCP实现客户端与服务端通信
TCP通信 需要先创建连接 - 并且在创建连接的过程中 需要经过三次握手 底层通过 流 发送数据 数据没有大小限制 可靠的传输机制 - 丢包重发 包的顺序的 ...
- Socket通信客户端和服务端代码
这两天研究了下Socket通信,简单实现的客户端和服务端代码 先上winfrom图片,客户端和服务端一样 服务端代码: using System; using System.Collections.G ...
随机推荐
- Oracle高级查询,事物,过程及函数
一 数值函数 数值 abs,ceil,floor,round,trunc字符串 instr,substr SQL>SELECT 'ABS':'|| ABS(-12.3) FROM DUAL; 运 ...
- 扩展PHP内置的异常处理类
在try代码块中,需要使用throw语句抛出一个异常对象,才能跳到转到catch代码块中执行,并在catch代码块中捕获并使用这个异常类的对象.虽然在PHP中提供的内置异常处理类Exception,已 ...
- python运维开发之第五天
一.模块五大部分 定义,导入方法,import本质(路径搜索),导入优化,模块的分类 1.定义: 模块是用来从逻辑上组织python代码(变量,函数,类,逻辑实现一个功能),本质就是.py结尾的pyt ...
- python种的builtin函数详解-第三篇
exec_stmt ::= "exec" or_expr ["in" expression ["," expression]] eval(e ...
- IOS--UIPageControl的使用方法详细
IOS--UIPageControl的使用方法详细 // UIPageControl的常用方法 UIPageControl *onePageControl = [[UIPageControl al ...
- 在CentOS6上使用YUM安装php5.5.x
这里使用 Webtatic EL6的YUM源来安装php5.5,我们首页安装Webtatic EL6 YUM源 rpm -Uvh http://repo.webtatic.com/yum/el6/la ...
- chrome_php logger 的实现原理
chrome_php是什么 1.chrome_php 是什么? 一款 Chrome 下用来配合调试 PHP 的工具,可以通过,console来查看php的信息 1.2用法 用法特别简单,有一个chro ...
- logstash (?m) 经典例子
在和 codec/multiline 搭配使用的时候,需要注意一个问题,grok 正则和普通正则一样,默认是不支持匹配回车换行的.就像你需要 =~ //m 一样也需要单独指定,具体写法是在表达式开始位 ...
- BZOJ2749: [HAOI2012]外星人
2749: [HAOI2012]外星人 Time Limit: 3 Sec Memory Limit: 128 MBSubmit: 377 Solved: 199[Submit][Status] ...
- UVa 11292 - Dragon of Loowater(排序贪心)
Once upon a time, in the Kingdom of Loowater, a minor nuisance turned into a major problem.The shore ...