服务器核心代码 AsyncServer.cs
/// <summary>
/// 异步SOCKET 服务器
/// </summary>
public class AsyncServer : IDisposable
{
#region Fields
/// <summary>
/// 服务器程序允许的最大客户端连接数
/// </summary>
private int _maxClient
/// <summary>
/// 当前的连接的客户端数
/// </summary>
private int _clientCount
/// <summary>
/// 服务器使用的异步socket
/// </summary>
private Socket _serverSock
/// <summary>
/// 客户端会话列表
/// </summary>
private List<session>_clients
private bool disposed = false
#endregion
#region Properties
/// <summary>
/// 服务器是否正在运行
/// </summary>
public bool IsRunning { getprivate set}
/// <summary>
/// 监听的IP地址
/// </summary>
public IPAddress Address { getprivate set}
/// <summary>
/// 监听的端口
/// </summary>
public int Port { getprivate set}
/// <summary>
/// 通信使用的编码
/// </summary>
public Encoding Encoding { getset}
#endregion
#region Ctors
/// <summary>
/// 异步Socket TCP服务器
/// </summary>
///<param name="listenPort">监听的端口
public AsyncServer(int listenPort)
: this(IPAddress.Any, listenPort,1024)
{
}
/// <summary>
/// 异步Socket TCP服务器
/// </summary>
///<param name="localEP">监听的终结点
public AsyncServer(IPEndPoint localEP)
: this(localEP.Address, localEP.Port,1024)
{
}
/// <summary>
/// 异步Socket TCP服务器
/// </summary>
///<param name="localIPAddress">监听的IP地址
///<param name="listenPort">监听的端口
///<param name="maxClient">最大客户端数量
public AsyncServer(IPAddress localIPAddress, int listenPort,int maxClient)
{
this.Address = localIPAddress
this.Port = listenPort
this.Encoding = Encoding.Default
_maxClient = maxClient
_clients = new List<session>()
_serverSock = new Socket(localIPAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp)
}
#endregion
#region Server
/// <summary>
/// 启动服务器
/// </summary>
/// <returns>异步TCP服务器</returns>
public AsyncServer Start()
{
if (!IsRunning)
{
IsRunning = true
_serverSock.Bind(new IPEndPoint(this.Address, this.Port))
_serverSock.Listen(1024)
_serverSock.BeginAccept(new AsyncCallback(HandleAcceptConnected), _serverSock)
}
return this
}
/// <summary>
/// 启动服务器
/// </summary>
///<param name="backlog">
/// 服务器所允许的挂起连接序列的最大长度
///
/// <returns>异步TCP服务器</returns>
public AsyncServer Start(int backlog)
{
if (!IsRunning)
{
IsRunning = true
_serverSock.Bind(new IPEndPoint(this.Address, this.Port))
_serverSock.Listen(backlog)
_serverSock.BeginAccept(new AsyncCallback(HandleAcceptConnected), _serverSock)
}
return this
}
/// <summary>
/// 停止服务器
/// </summary>
/// <returns>异步TCP服务器</returns>
public AsyncServer Stop()
{
if (IsRunning)
{
IsRunning = false
_serverSock.Close()
//TODO 关闭对所有客户端的连接
}
return this
}
#endregion
#region Receive
/// <summary>
/// 处理客户端连接
/// </summary>
///<param name="ar">
private void HandleAcceptConnected(IAsyncResult ar)
{
if (IsRunning)
{
Socket server = (Socket)ar.AsyncState
Socket client = server.EndAccept(ar)
//检查是否达到最大的允许的客户端数目
if (_clientCount == _maxClient)
{
//TODO 触发事件
RaiseServerException(null)
}
else
{
Session session = new Session(client)
lock (_clients)
{
_clients.Add(session)
_clientCount++
RaiseClientConnected(session)//触发客户端连接事件
}
session.RecvDataBuffer = new byte[client.ReceiveBufferSize]
//开始接受来自该客户端的数据
client.BeginReceive(session.RecvDataBuffer, 0, session.RecvDataBuffer.Length, SocketFlags.None,
new AsyncCallback(HandleDataReceived), session)
}
//接受下一个请求
server.BeginAccept(new AsyncCallback(HandleAcceptConnected), ar.AsyncState)
}
}
/// <summary>
/// 处理客户端数据
/// </summary>
///<param name="ar">
private void HandleDataReceived(IAsyncResult ar)
{
if (IsRunning)
{
Session session = (Session)ar.AsyncState
Socket client = session.ClientSocket
try
{
//如果两次开始了异步的接收,所以当客户端退出的时候
//会两次执行EndReceive
int recv = client.EndReceive(ar)
if (recv == 0)
{
//TODO 触发事件 (关闭客户端)
CloseSession(session)
RaiseNetError(session)
return
}
//TODO 处理已经读取的数据 ps:数据在session的RecvDataBuffer中
RaiseDataReceived(session)
//TODO 触发数据接收事件
}
catch (SocketException ex)
{
//TODO 异常处理
RaiseNetError(session)
}
finally
{
//继续接收来自来客户端的数据
client.BeginReceive(session.RecvDataBuffer, 0, session.RecvDataBuffer.Length, SocketFlags.None,
new AsyncCallback(HandleDataReceived), session)
}
}
}
#endregion
看到一些资料,说Windows的IOCP后发制人,比Linux的epoll优越很多。那么优势到底在哪里?如何选择Linux或Windows作为服务器呢?也看到有资料说,同样在Linux上,使用reactor机制的网络库性能比使用Proactor机制的Asio性能好接近1/5,这个例子可能与题目无关,不过我在文中可能会做一些相关的分析。
系统I/O模型 可分为三类:
IOCP基于非阻塞异步模型,而epoll基于非阻塞同步模型。
参考 Hyunjik Bae, A game programmer since 1995 说的:
参考[4]比较了Windows IOCP和Linux epoll的性能,结论是如果使用Linux,应该使用支持RSS(multi-queue)的NIC,这样可以达到与IOCP类似的性能。
Boost.Asio为了兼容Windows和Linux,在Linux上用epoll和select去模拟proactor模式,影响了它的效率和实现复杂度。其效率不及使用原生I/O机制的其它实现为Reactor模式的网络库。
引用来自参考[3]的话:
[1] Why doesn't Linux have a system like IOCP or Rio does? , 2014.
[2] 两种高性能I/O设计模式(Reactor/Proactor)的比较 - 文章 - 伯乐在线
[3] Practical difference between epoll and Windows IO Completion Ports (IOCP)
[4] Windows IOCP vs Linux EPOLL Performance Comparison
欢迎分享,转载请注明来源:夏雨云
评论列表(0条)