原創性申明
本文作者: 小竹zz 本文地址:http://blog.csdn.net/zhujunxxxxx 轉載請註明出處。
介紹
在c#中微軟已經提供了TcpListener和TcpClient來實現Tcp的通訊,這部分已經有人寫了比較好的異步服務器代碼 http://www.cnblogs.com/gaochundong/archive/2013/04/14/csharp_async_tcp_server.html 這位博主寫的博客質量真是高,我經常瀏覽他的博客總是有很多讓我驚喜的地方,他使用的是TcpListener來實現的異步服務器的。
我的socket版本其實本質上和他的沒有區別,就只是改寫了一點點,所以在這裏貼一份代碼就是了,多的不解釋了
代碼
/// <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 { get; private set; }
/// <summary>
/// 監聽的IP地址
/// </summary>
public IPAddress Address { get; private set; }
/// <summary>
/// 監聽的端口
/// </summary>
public int Port { get; private set; }
/// <summary>
/// 通信使用的編碼
/// </summary>
public Encoding Encoding { get; set; }
#endregion
#region Ctors
/// <summary>
/// 異步Socket TCP服務器
/// </summary>
/// <param name="listenPort">監聽的端口</param>
public AsyncServer(int listenPort)
: this(IPAddress.Any, listenPort,1024)
{
}
/// <summary>
/// 異步Socket TCP服務器
/// </summary>
/// <param name="localEP">監聽的終結點</param>
public AsyncServer(IPEndPoint localEP)
: this(localEP.Address, localEP.Port,1024)
{
}
/// <summary>
/// 異步Socket TCP服務器
/// </summary>
/// <param name="localIPAddress">監聽的IP地址</param>
/// <param name="listenPort">監聽的端口</param>
/// <param name="maxClient">最大客戶端數量</param>
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">
/// 服務器所允許的掛起連接序列的最大長度
/// </param>
/// <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"></param>
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"></param>
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
#region Send
/// <summary>
/// 發送數據
/// </summary>
/// <param name="session">接收數據的客戶端會話</param>
/// <param name="data">數據報文</param>
public void Send(Session session, byte[] data)
{
Send(session.ClientSocket,data);
}
/// <summary>
/// 異步發送數據至指定的客戶端
/// </summary>
/// <param name="client">客戶端</param>
/// <param name="data">報文</param>
public void Send(Socket client, byte[] data)
{
if (!IsRunning)
throw new InvalidProgramException("This TCP Scoket server has not been started.");
if (client == null)
throw new ArgumentNullException("client");
if (data == null)
throw new ArgumentNullException("data");
client.BeginSend(data, 0, data.Length, SocketFlags.None,
new AsyncCallback(SendDataEnd), client);
}
/// <summary>
/// 發送數據完成處理函數
/// </summary>
/// <param name="ar">目標客戶端Socket</param>
private void SendDataEnd(IAsyncResult ar)
{
((Socket)ar.AsyncState).EndSend(ar);
}
#endregion
#region Events
/// <summary>
/// 接收到數據事件
/// </summary>
public event EventHandler<EventArgs> DataReceived;
private void RaiseDataReceived(Session session)
{
if (DataReceived != null)
{
DataReceived(this, new AsyncEventArgs(session));
}
}
/// <summary>
/// 與客戶端的連接已建立事件
/// </summary>
public event EventHandler<AsyncEventArgs> ClientConnected;
/// <summary>
/// 與客戶端的連接已斷開事件
/// </summary>
public event EventHandler<AsyncEventArgs> ClientDisconnected;
/// <summary>
/// 觸發客戶端連接事件
/// </summary>
/// <param name="session"></param>
private void RaiseClientConnected(Session session)
{
if (ClientConnected != null)
{
ClientConnected(this, new AsyncEventArgs(session));
}
}
/// <summary>
/// 觸發客戶端連接斷開事件
/// </summary>
/// <param name="client"></param>
private void RaiseClientDisconnected(Socket client)
{
if (ClientDisconnected != null)
{
ClientDisconnected(this, new AsyncEventArgs("連接斷開"));
}
}
/// <summary>
/// 網絡錯誤事件
/// </summary>
public event EventHandler<AsyncEventArgs> NetError;
/// <summary>
/// 觸發網絡錯誤事件
/// </summary>
/// <param name="client"></param>
private void RaiseNetError(Session session)
{
if (NetError != null)
{
NetError(this, new AsyncEventArgs(session));
}
}
/// <summary>
/// 異常事件
/// </summary>
public event EventHandler<AsyncEventArgs> ServerException;
/// <summary>
/// 觸發異常事件
/// </summary>
/// <param name="client"></param>
private void RaiseServerException(Session session)
{
if (ServerException != null)
{
ServerException(this, new AsyncEventArgs(session));
}
}
#endregion
#region Close
/// <summary>
/// 關閉一個與客戶端之間的會話
/// </summary>
/// <param name="closeClient">需要關閉的客戶端會話對象</param>
public void CloseSession(Session session)
{
if (session != null)
{
session.Datagram = null;
session.RecvDataBuffer = null;
_clients.Remove(session);
_clientCount--;
//TODO 觸發關閉事件
session.Close();
}
}
/// <summary>
/// 關閉所有的客戶端會話,與所有的客戶端連接會斷開
/// </summary>
public void CloseAllClient()
{
foreach (Session client in _clients)
{
CloseSession(client);
}
_clientCount = 0;
_clients.Clear();
}
/// <summary>
/// Performs application-defined tasks associated with freeing,
/// releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release
/// both managed and unmanaged resources; <c>false</c>
/// to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
try
{
Stop();
if (_serverSock != null)
{
_serverSock = null;
}
}
catch (SocketException ex)
{
//TODO
RaiseServerException(null);
}
}
disposed = true;
}
}
#endregion
}
/// <summary>
/// 客戶端與服務器之間的會話類
/// </summary>
public class Session
{
#region 字段
/// <summary>
/// 接收數據緩衝區
/// </summary>
private byte[] _recvBuffer;
/// <summary>
/// 客戶端發送到服務器的報文
/// 注意:在有些情況下報文可能只是報文的片斷而不完整
/// </summary>
private string _datagram;
/// <summary>
/// 客戶端的Socket
/// </summary>
private Socket _clientSock;
#endregion
#region 屬性
/// <summary>
/// 接收數據緩衝區
/// </summary>
public byte[] RecvDataBuffer
{
get
{
return _recvBuffer;
}
set
{
_recvBuffer = value;
}
}
/// <summary>
/// 存取會話的報文
/// </summary>
public string Datagram
{
get
{
return _datagram;
}
set
{
_datagram = value;
}
}
/// <summary>
/// 獲得與客戶端會話關聯的Socket對象
/// </summary>
public Socket ClientSocket
{
get
{
return _clientSock;
}
}
#endregion
/// <summary>
/// 構造函數
/// </summary>
/// <param name="cliSock">會話使用的Socket連接</param>
public Session(Socket cliSock)
{
_clientSock = cliSock;
}
/// <summary>
/// 關閉會話
/// </summary>
public void Close()
{
//關閉數據的接受和發送
_clientSock.Shutdown(SocketShutdown.Both);
//清理資源
_clientSock.Close();
}
}
事件類
class AsyncEventArgs : EventArgs
{
/// <summary>
/// 提示信息
/// </summary>
public string _msg;
public Session _sessions;
/// <summary>
/// 是否已經處理過了
/// </summary>
public bool IsHandled { get; set; }
public AsyncEventArgs(string msg)
{
this._msg = msg;
IsHandled = false;
}
public AsyncEventArgs(Session session)
{
this._sessions = session;
IsHandled = false;
}
public AsyncEventArgs(string msg, Session session)
{
this._msg = msg;
this._sessions = session;
IsHandled = false;
}
}
Socket版本的異步客戶端代碼
/// <summary>
/// 異步客戶端
/// </summary>
public class AsyncClient
{
#region Fields
private Socket _client;
private bool disposed = false;
private int retries = 0;
#endregion
#region Properties
/// <summary>
/// 是否已與服務器建立連接
/// </summary>
public bool Connected { get { return _client.Connected; } }
/// <summary>
/// 遠端服務器的IP地址列表
/// </summary>
public IPAddress[] Addresses { get; private set; }
/// <summary>
/// 遠端服務器的端口
/// </summary>
public int Port { get; private set; }
/// <summary>
/// 連接重試次數
/// </summary>
public int Retries { get; set; }
/// <summary>
/// 連接重試間隔
/// </summary>
public int RetryInterval { get; set; }
/// <summary>
/// 遠端服務器終結點
/// </summary>
public IPEndPoint RemoteIPEndPoint
{
get { return new IPEndPoint(Addresses[0], Port); }
}
/// <summary>
/// 本地客戶端終結點
/// </summary>
protected IPEndPoint LocalIPEndPoint { get; private set; }
/// <summary>
/// 通信所使用的編碼
/// </summary>
public Encoding Encoding { get; set; }
#endregion
#region 構造函數
/// <summary>
/// 異步TCP客戶端
/// </summary>
/// <param name="remoteEP">遠端服務器終結點</param>
public AsyncClient(IPEndPoint remoteEP)
: this(new[] { remoteEP.Address }, remoteEP.Port)
{
}
/// <summary>
/// 異步TCP客戶端
/// </summary>
/// <param name="remoteEP">遠端服務器終結點</param>
/// <param name="localEP">本地客戶端終結點</param>
public AsyncClient(IPEndPoint remoteEP, IPEndPoint localEP)
: this(new[] { remoteEP.Address }, remoteEP.Port, localEP)
{
}
/// <summary>
/// 異步TCP客戶端
/// </summary>
/// <param name="remoteIPAddress">遠端服務器IP地址</param>
/// <param name="remotePort">遠端服務器端口</param>
public AsyncClient(IPAddress remoteIPAddress, int remotePort)
: this(new[] { remoteIPAddress }, remotePort)
{
}
/// <summary>
/// 異步TCP客戶端
/// </summary>
/// <param name="remoteIPAddress">遠端服務器IP地址</param>
/// <param name="remotePort">遠端服務器端口</param>
/// <param name="localEP">本地客戶端終結點</param>
public AsyncClient(
IPAddress remoteIPAddress, int remotePort, IPEndPoint localEP)
: this(new[] { remoteIPAddress }, remotePort, localEP)
{
}
/// <summary>
/// 異步TCP客戶端
/// </summary>
/// <param name="remoteHostName">遠端服務器主機名</param>
/// <param name="remotePort">遠端服務器端口</param>
public AsyncClient(string remoteHostName, int remotePort)
: this(Dns.GetHostAddresses(remoteHostName), remotePort)
{
}
/// <summary>
/// 異步TCP客戶端
/// </summary>
/// <param name="remoteHostName">遠端服務器主機名</param>
/// <param name="remotePort">遠端服務器端口</param>
/// <param name="localEP">本地客戶端終結點</param>
public AsyncClient(
string remoteHostName, int remotePort, IPEndPoint localEP)
: this(Dns.GetHostAddresses(remoteHostName), remotePort, localEP)
{
}
/// <summary>
/// 異步TCP客戶端
/// </summary>
/// <param name="remoteIPAddresses">遠端服務器IP地址列表</param>
/// <param name="remotePort">遠端服務器端口</param>
public AsyncClient(IPAddress[] remoteIPAddresses, int remotePort)
: this(remoteIPAddresses, remotePort, null)
{
}
/// <summary>
/// 異步TCP客戶端
/// </summary>
/// <param name="remoteIPAddresses">遠端服務器IP地址列表</param>
/// <param name="remotePort">遠端服務器端口</param>
/// <param name="localEP">本地客戶端終結點</param>
public AsyncClient(
IPAddress[] remoteIPAddresses, int remotePort, IPEndPoint localEP)
{
this.Addresses = remoteIPAddresses;
this.Port = remotePort;
this.LocalIPEndPoint = localEP;
this.Encoding = Encoding.Default;
if (this.LocalIPEndPoint != null)
{
_client = new Socket(LocalIPEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
}
Retries = 3;
RetryInterval = 5;
}
#endregion
#region Connect
/// <summary>
/// 連接到服務器
/// </summary>
/// <returns>異步TCP客戶端</returns>
public AsyncClient Connect()
{
if (!Connected)
{
// start the async connect operation
_client.BeginConnect(
Addresses, Port, HandleTcpServerConnected, _client);
}
return this;
}
/// <summary>
/// 關閉與服務器的連接
/// </summary>
/// <returns>異步TCP客戶端</returns>
public AsyncClient Close()
{
if (Connected)
{
retries = 0;
_client.Close();
RaiseServerDisconnected(Addresses, Port);
}
return this;
}
#endregion
#region Receive
private void HandleTcpServerConnected(IAsyncResult ar)
{
try
{
_client.EndConnect(ar);
RaiseServerConnected(Addresses, Port);
retries = 0;
}
catch (Exception ex)
{
}
// we are connected successfully and start asyn read operation.
byte[] buffer = new byte[_client.ReceiveBufferSize];
_client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, HandleDatagramReceived, buffer);
}
private void HandleDatagramReceived(IAsyncResult ar)
{
try
{
int recv = _client.EndReceive(ar);
byte[] buffer = (byte[])ar.AsyncState;
byte[] receivedBytes = new byte[recv];
Buffer.BlockCopy(buffer, 0, receivedBytes, 0, recv);
RaiseDatagramReceived(_client, receivedBytes);
// then start reading from the network again
_client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, HandleDatagramReceived, buffer);
}
catch (Exception)
{
}
}
#endregion
#region Events
/// <summary>
/// 接收到數據報文事件
/// </summary>
public event EventHandler<EventArgs> DatagramReceived;
private void RaiseDatagramReceived(Socket sender, byte[] datagram)
{
if (DatagramReceived != null)
{
DatagramReceived(this, new EventArgs());
}
}
/// <summary>
/// 與服務器的連接已建立事件
/// </summary>
public event EventHandler<EventArgs> ServerConnected;
/// <summary>
/// 與服務器的連接已斷開事件
/// </summary>
public event EventHandler<EventArgs> ServerDisconnected;
/// <summary>
/// 與服務器的連接發生異常事件
/// </summary>
public event EventHandler<EventArgs> ServerExceptionOccurred;
private void RaiseServerConnected(IPAddress[] ipAddresses, int port)
{
if (ServerConnected != null)
{
ServerConnected(this, new EventArgs());
}
}
private void RaiseServerDisconnected(IPAddress[] ipAddresses, int port)
{
if (ServerDisconnected != null)
{
ServerDisconnected(this, new EventArgs());
}
}
private void RaiseServerExceptionOccurred(
IPAddress[] ipAddresses, int port, Exception innerException)
{
if (ServerExceptionOccurred != null)
{
ServerExceptionOccurred(this, new EventArgs());
}
}
#endregion
#region Send
/// <summary>
/// 發送報文
/// </summary>
/// <param name="datagram">報文</param>
public void Send(byte[] datagram)
{
if (datagram == null)
throw new ArgumentNullException("datagram");
if (!Connected)
{
RaiseServerDisconnected(Addresses, Port);
throw new InvalidProgramException(
"This client has not connected to server.");
}
_client.BeginSend(datagram, 0, datagram.Length, SocketFlags.None, HandleDataSend, _client);
}
private void HandleDataSend(IAsyncResult ar)
{
((Socket)ar.AsyncState).EndSend(ar);
}
/// <summary>
/// 發送報文
/// </summary>
/// <param name="datagram">報文</param>
public void Send(string datagram)
{
Send(this.Encoding.GetBytes(datagram));
}
#endregion
#region IDisposable Members
/// <summary>
/// Performs application-defined tasks associated with freeing,
/// releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed
/// and unmanaged resources; <c>false</c>
/// to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
try
{
Close();
if (_client != null)
{
_client = null;
}
}
catch (SocketException)
{
}
}
disposed = true;
}
}
#endregion
}