原创性申明
本文作者: 小竹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
}