using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.IO;
namespace Rocky.Net
{
public class AsyncTcpListener
{
#region Fields
public event ErrorEventHandler Error;
public event EventHandler Started;
public event EventHandler Stopped;
public event EventHandler<TcpEventArgs> SessionFull;
public event EventHandler<TcpEventArgs> SessionStart;
public event EventHandler<TcpEventArgs> SessionSent;
public event EventHandler<TcpEventArgs> SessionReceived;
public event EventHandler<TcpEventArgs> SessionEnd;
private volatile bool _isListening;
private ushort _maxSession;
private IPEndPoint _boundEndPoint;
private Socket _sock;
private ConcurrentDictionary<int, SessionState> _sessions;
private BufferSegment _bufferManager;
#endregion
#region Properties
public bool IsListening
{
get { return _isListening; }
}
public IPEndPoint BoundEndPoint
{
get { return _boundEndPoint; }
}
public Socket Server
{
get { return _sock; }
}
public ICollection<SessionState> Clients
{
get { return _sessions.Values; }
}
#endregion
#region Constructor
/// <summary>
/// 构造函数
/// </summary>
/// <param name="endpoint">服务器端监听的地址和端口号</param>
/// <param name="maxClient">服务器容纳Session的最大数</param>
/// <param name="bufferSize">服务器每个Session的缓冲区大小</param>
public AsyncTcpListener(IPEndPoint endpoint, ushort maxClient = 100, BufferSizeof? bufferSize = null)
{
Contract.Requires(endpoint != null);
_maxSession = maxClient;
_boundEndPoint = endpoint;
int lev = (int)(_maxSession * 0.3);
_sessions = new ConcurrentDictionary<int, SessionState>(lev, _maxSession);
if (bufferSize != null)
{
_bufferManager = new BufferSegment((int)bufferSize.Value, _maxSession * 2);
}
}
#endregion
#region EventMethods
protected virtual void OnError(ErrorEventArgs e)
{
if (Error != null)
{
Error(this, e);
}
}
protected virtual void OnStarted(EventArgs e)
{
_isListening = true;
if (Started != null)
{
Started(this, e);
}
}
protected virtual void OnStopped(EventArgs e)
{
_isListening = false;
if (Stopped != null)
{
Stopped(this, e);
}
}
protected virtual void OnSessionFull(TcpEventArgs e)
{
if (SessionFull != null)
{
SessionFull(this, e);
}
}
protected virtual void OnSessionStart(TcpEventArgs e)
{
if (SessionStart != null)
{
SessionStart(this, e);
}
}
protected virtual void OnSessionSent(TcpEventArgs e)
{
if (SessionSent != null)
{
SessionSent(this, e);
}
}
protected virtual void OnSessionReceived(TcpEventArgs e)
{
if (SessionReceived != null)
{
SessionReceived(this, e);
}
}
protected virtual void OnSessionEnd(TcpEventArgs e)
{
if (SessionEnd != null)
{
SessionEnd(this, e);
}
}
#endregion
#region PublicMethods
public void Start()
{
Contract.Requires(!this.IsListening, "服务已运行");
_sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_sock.Bind(_boundEndPoint);
_sock.Listen(_maxSession);
_sock.BeginAccept(AcceptCallback, _sock);
OnStarted(EventArgs.Empty);
}
public void Stop()
{
Contract.Requires(this.IsListening, "服务已停止");
//这个条件语句,一定要在关闭所有客户端以前
OnStopped(EventArgs.Empty);
//关闭连接,否则客户端会认为是强制关闭
if (_sock.Poll(-1, SelectMode.SelectRead))
{
_sock.Shutdown(SocketShutdown.Both);
}
foreach (var session in _sessions.Values)
{
session.Abandon();
}
_sessions.Clear();
_sock.Close();
}
public bool TryGet(int sessionID, out SessionState session)
{
return _sessions.TryGetValue(sessionID, out session);
}
public IAsyncResult Send(SessionState session)
{
Contract.Requires(this.IsListening, "服务已停止");
var buffer = session.GetBuffer(FileAccess.Write);
return session.Client.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, SendCallback, session);
}
public void Abandon(SessionState session)
{
Contract.Requires(this.IsListening, "服务已停止");
SessionState current;
if (_sessions.TryRemove(session.SessionID, out current))
{
OnSessionEnd(new TcpEventArgs(session));
current.Abandon();
}
}
#endregion
#region ProtectedMethods
protected virtual void AcceptCallback(IAsyncResult ar)
{
if (!_isListening)
{
return;
}
Socket serverSock = (Socket)ar.AsyncState;
try
{
Socket clientSock = serverSock.EndAccept(ar);
if (_sessions.Count >= _maxSession)
{
var session = new SessionState(clientSock, null);
OnSessionFull(new TcpEventArgs(session));
//Must do it 服务器满了,必须关闭新来的客户端连接
session.Abandon();
}
else
{
var session = new SessionState(clientSock, _bufferManager);
_sessions.GetOrAdd(session.SessionID, session);
//开始接受来自该客户端的数据
var buffer = session.GetBuffer(FileAccess.Read);
clientSock.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveCallback, session);
OnSessionStart(new TcpEventArgs(session));
}
_sock.BeginAccept(AcceptCallback, _sock);
}
catch (Exception ex)
{
OnError(new ErrorEventArgs(ex));
}
}
protected virtual void ReceiveCallback(IAsyncResult ar)
{
SessionState session = (SessionState)ar.AsyncState;
Socket client = session.Client;
try
{
//因为两次开始异步接收,所以当客户端退出的时候会执行两次EndReceive
int recv = client.EndReceive(ar);
SessionState current;
//正常关闭
if (!_sessions.TryGetValue(session.SessionID, out current) || recv == 0)
{
goto abandon;
}
var e = session.GetSyncArgs(FileAccess.Read);
client.ReceiveSync(e, false);
if (e.IsShutdown)
{
goto abandon;
}
OnSessionReceived(new TcpEventArgs(session));
var buffer = session.GetBuffer(FileAccess.Read);
client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveCallback, session);
return;
abandon:
session.Status = StatusType.NormalExit;
Abandon(session);
}
catch (SocketException ex)
{
//客户端强制关闭
if (ex.ErrorCode == 10054)
{
session.Status = StatusType.ExceptionExit;
Abandon(session);
return;
}
OnError(new ErrorEventArgs(ex));
}
catch (ObjectDisposedException ex)
{
//当调用Abandon()时,会结束数据接收,但是数据接收处理中会调用int recv = client.EndReceive(ar);就访问了Abandon()已经处置的对象
//这里的实现不够优雅
if (ex != null)
{
ex = null;
//DoNothing;
}
}
catch (Exception ex)
{
OnError(new ErrorEventArgs(ex));
}
}
protected virtual void SendCallback(IAsyncResult ar)
{
SessionState session = (SessionState)ar.AsyncState;
Socket client = session.Client;
try
{
long sent = client.EndSend(ar);
if (sent < 1L)
{
return;
}
var e = session.GetSyncArgs(FileAccess.Write);
sent = client.SendSync(e, false);
OnSessionSent(new TcpEventArgs(session));
}
catch (Exception ex)
{
OnError(new ErrorEventArgs(ex));
}
}
#endregion
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;
using System.Net;
using System.Net.Sockets;
using System.IO;
namespace Rocky.Net
{
public class AsyncTcpClient : Disposable
{
#region Fields
public event ErrorEventHandler Error;
public event EventHandler<TcpEventArgs> Connected;
public event EventHandler<TcpEventArgs> Sent;
public event EventHandler<TcpEventArgs> Received;
public event EventHandler<TcpEventArgs> Disconnected;
private bool _isConnected;
private SessionState _session;
#endregion
#region Properties
public bool IsConnected
{
get { return _isConnected; }
}
public SessionState Session
{
get { return _session; }
}
#endregion
#region Constructor
public AsyncTcpClient()
{
_session = new SessionState(new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp));
}
protected override void DisposeInternal(bool disposing)
{
if (disposing)
{
Session.Abandon();
_isConnected = false;
}
Connected = null;
Sent = null;
Received = null;
Disconnected = null;
}
#endregion
#region EventMethods
protected virtual void OnError(ErrorEventArgs e)
{
if (Error != null)
{
Error(this, e);
}
}
protected virtual void OnConnected(TcpEventArgs e)
{
_isConnected = true;
if (Connected != null)
{
Connected(this, e);
}
}
protected virtual void OnSent(TcpEventArgs e)
{
if (Sent != null)
{
Sent(this, e);
}
}
protected virtual void OnReceived(TcpEventArgs e)
{
if (Received != null)
{
Received(this, e);
}
}
protected virtual void OnDisconnected(TcpEventArgs e)
{
_isConnected = false;
if (Disconnected != null)
{
Disconnected(this, e);
}
}
#endregion
#region PublicMethods
public IAsyncResult Connect(string host)
{
Contract.Requires(!this.IsConnected, "已连接服务器");
base.CheckDisposed();
var ipe = SocketHelper.ParseEndPoint(host);
return _session.Client.BeginConnect(ipe, ConnectCallback, _session.Client);
}
public IAsyncResult Disconnect()
{
Contract.Requires(this.IsConnected, "未连接服务器");
base.CheckDisposed();
_session.Client.Shutdown(SocketShutdown.Both);
return _session.Client.BeginDisconnect(true, DisconnectCallback, _session.Client);
}
public IAsyncResult Send()
{
Contract.Requires(this.IsConnected, "未连接服务器");
base.CheckDisposed();
var buffer = _session.GetBuffer(FileAccess.Write);
return _session.Client.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, SendCallback, _session.Client);
}
#endregion
#region ProtectedMethods
protected virtual void ConnectCallback(IAsyncResult ar)
{
Socket sock = (Socket)ar.AsyncState;
try
{
sock.EndConnect(ar);
var buffer = _session.GetBuffer(FileAccess.Read);
_session.Client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveCallback, _session.Client);
OnConnected(new TcpEventArgs(_session));
}
catch (Exception ex)
{
OnError(new ErrorEventArgs(ex));
}
}
protected virtual void DisconnectCallback(IAsyncResult ar)
{
Socket sock = (Socket)ar.AsyncState;
try
{
sock.EndDisconnect(ar);
OnDisconnected(new TcpEventArgs(_session));
}
catch (Exception ex)
{
OnError(new ErrorEventArgs(ex));
}
}
protected virtual void ReceiveCallback(IAsyncResult ar)
{
Socket remote = (Socket)ar.AsyncState;
try
{
int recv = remote.EndReceive(ar);
if (recv == 0)
{
goto disconnected;
}
var e = _session.GetSyncArgs(FileAccess.Read);
remote.ReceiveSync(e, false);
if (e.IsShutdown)
{
goto disconnected;
}
OnReceived(new TcpEventArgs(_session));
var buffer = _session.GetBuffer(FileAccess.Read);
remote.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveCallback, remote);
return;
disconnected:
_session.Status = StatusType.NormalExit;
OnDisconnected(new TcpEventArgs(_session));
}
catch (SocketException ex)
{
if (ex.ErrorCode == 10054)
{
_session.Status = StatusType.ExceptionExit;
OnDisconnected(new TcpEventArgs(_session));
return;
}
OnError(new ErrorEventArgs(ex));
}
catch (Exception ex)
{
OnError(new ErrorEventArgs(ex));
}
}
protected virtual void SendCallback(IAsyncResult ar)
{
Socket remote = (Socket)ar.AsyncState;
try
{
long sent = remote.EndSend(ar);
if (sent < 1L)
{
return;
}
var e = _session.GetSyncArgs(FileAccess.Write);
sent = remote.SendSync(e, false);
OnSent(new TcpEventArgs(_session));
}
catch (Exception ex)
{
OnError(new ErrorEventArgs(ex));
}
}
#endregion
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;
using System.Net;
using System.Net.Sockets;
using System.IO;
namespace Rocky.Net
{
/// <summary>
/// 建议使用BinaryWriter/BinaryReader 来包装Stream
/// </summary>
public class SessionState : Disposable
{
#region Fields
private readonly BufferSegment BufferManager;
private bool _reuseSocket;
private Socket _sock;
private byte[] _sendLength, _receiveLength;
private MemoryStream _sendStream, _receiveStream;
private SocketSyncArgs _sendArgs, _receiveArgs;
#endregion
#region Properties
public int SessionID
{
get { return _sock.Handle.ToInt32(); }
}
public Socket Client
{
get { return _sock; }
}
public StatusType Status { get; set; }
#endregion
#region Constructor
protected internal SessionState(Socket client, BufferSegment bufferManager = null)
{
Contract.Requires(client != null);
_sock = client;
int length = sizeof(int);
_sendLength = new byte[length];
_receiveLength = new byte[length];
if (bufferManager == null)
{
_reuseSocket = true;
_sendStream = new MemoryStream();
_receiveStream = new MemoryStream();
}
else
{
BufferManager = bufferManager;
_sendStream = (BufferedMemoryStream)BufferManager.Take();
_receiveStream = (BufferedMemoryStream)BufferManager.Take();
}
_sendArgs = new SocketSyncArgs();
_receiveArgs = new SocketSyncArgs();
}
protected override void DisposeInternal(bool disposing)
{
if (disposing)
{
//1.Call shutdown with how=SD_SEND.
//2.Call recv until zero returned, or SOCKET_ERROR.
//3.Call closesocket.
if (_sock.Connected)
{
_sock.Shutdown(SocketShutdown.Send);
}
if (_reuseSocket)
{
_sock.Disconnect(true);
}
else
{
_sock.Close();
}
}
_sendStream.Dispose();
_receiveStream.Dispose();
_receiveArgs = _sendArgs = null;
}
public void Abandon()
{
this.Dispose();
}
#endregion
#region Methods
public bool Equals(SessionState obj)
{
return this.SessionID == obj.SessionID;
}
/// <summary>
/// 使用Socket对象的Handle值作为HashCode,它具有良好的线性特征.
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
return this.SessionID;
}
public override string ToString()
{
return string.Format("SessionID:{0};Remote={1}", this.SessionID, _sock.RemoteEndPoint);
}
internal byte[] GetBuffer(FileAccess access)
{
switch (access)
{
case FileAccess.Read:
return _receiveLength;
case FileAccess.Write:
var e = this.GetSyncArgs(access);
var pack = e.GetPackets()[1];
_sendLength.SetBytes(0, (int)pack.ContentLength);
return _sendLength;
default:
throw new NotSupportedException("FileAccess.ReadWrite");
}
}
internal SocketSyncArgs GetSyncArgs(FileAccess access)
{
long length;
switch (access)
{
case FileAccess.Read:
length = _receiveLength.ToInt32(0);
_receiveStream.Position = 0L;
_receiveStream.SetLength(length);
_receiveArgs.SetPackets(_receiveStream, length);
return _receiveArgs;
case FileAccess.Write:
length = _sendStream.Position;
if (length == 0L)
{
throw new InvalidOperationException("空包");
}
_sendStream.Position = 0L;
_sendStream.SetLength(length);
_sendArgs.SetPackets(_sendStream, length);
return _sendArgs;
default:
throw new NotSupportedException("FileAccess.ReadWrite");
}
}
public Stream GetStream(FileAccess access)
{
base.CheckDisposed();
long length;
switch (access)
{
case FileAccess.Read:
length = _receiveStream.Position;
_receiveStream.Position = 0L;
_receiveStream.SetLength(length);
return _receiveStream;
case FileAccess.Write:
//扩充buffer
_sendStream.Position = 0L;
_sendStream.SetLength(-1L);
return _sendStream;
default:
throw new NotSupportedException("FileAccess.ReadWrite");
}
}
#endregion
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Rocky.Net
{
public enum StatusType
{
/// <summary>
/// 正常
/// </summary>
Normal,
/// <summary>
/// 超时,待移除
/// </summary>
Timeout,
/// <summary>
/// 正常断开
/// </summary>
NormalExit,
/// <summary>
/// 异常断开
/// </summary>
ExceptionExit
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;
namespace Rocky.Net
{
/// <summary>
/// 网络通讯事件参数,包含了激发该事件的会话对象
/// </summary>
public class TcpEventArgs : EventArgs
{
public SessionState Session { get; private set; }
public TcpEventArgs(SessionState session)
{
Contract.Requires(session != null);
this.Session = session;
}
}
}