C#高效能Socket伺服器SocketAsyncEventArgs的實現(IOCP)
原創性申明
本文作者:小竹zz 部落格地址:http://blog.csdn.net/zhujunxxxxx/article/details/43573879轉載請註明出處
引言
我一直在探尋一個高效能的Socket客戶端程式碼。以前,我使用Socket類寫了一些基於傳統非同步程式設計模型的程式碼(BeginSend、BeginReceive,等等)也看過很多部落格的知識,在linux中有poll和epoll來實現,在windows下面
微軟MSDN中也提供了SocketAsyncEventArgs這個類來實現IOCP 地址:https://msdn.microsoft.com/zh-cn/library/system.net.sockets.socketasynceventargs.aspx
NET Framework中的APM也稱為Begin/End模式。這是因為會呼叫Begin方法來啟動非同步操作,然後返回一個IAsyncResult 物件。可以選擇將一個代理作為引數提供給Begin方法,非同步操作完成時會呼叫該方法。或者,一個執行緒可以等待 IAsyncResult.AsyncWaitHandle。當回撥被呼叫或發出等待訊號時,就會呼叫End方法來獲取非同步操作的結果。這種模式很靈活,使用相對簡單,在 .NET Framework 中非常常見。
但是,您必須注意,如果進行大量非同步套接字操作,是要付出代價的。針對每次操作,都必須建立一個IAsyncResult物件,而且該物件不能被重複使用。由於大量使用物件分配和垃圾收集,這會影響效能。為了解決這個問題,新版本提供了另一個使用套接字上執行非同步I/O的方法模式。這種新模式並不要求為每個套接字操作分配操作上下文物件。
微軟MSDN中也提供了SocketAsyncEventArgs這個類來實現IOCP 地址:https://msdn.microsoft.com/zh-cn/library/system.net.sockets.socketasynceventargs.aspx
NET Framework中的APM也稱為Begin/End模式。這是因為會呼叫Begin方法來啟動非同步操作,然後返回一個IAsyncResult 物件。可以選擇將一個代理作為引數提供給Begin方法,非同步操作完成時會呼叫該方法。或者,一個執行緒可以等待 IAsyncResult.AsyncWaitHandle。當回撥被呼叫或發出等待訊號時,就會呼叫End方法來獲取非同步操作的結果。這種模式很靈活,使用相對簡單,在 .NET Framework 中非常常見。
但是,您必須注意,如果進行大量非同步套接字操作,是要付出代價的。針對每次操作,都必須建立一個IAsyncResult物件,而且該物件不能被重複使用。由於大量使用物件分配和垃圾收集,這會影響效能。為了解決這個問題,新版本提供了另一個使用套接字上執行非同步I/O的方法模式。這種新模式並不要求為每個套接字操作分配操作上下文物件。
程式碼下載:http://download.csdn.net/detail/zhujunxxxxx/8431289 這裡的程式碼優化了的
目標
在上面微軟提供的例子我覺得不是很完整,沒有具體一個流程,只是受到客戶端訊息後傳送相同內容給客戶端,初學者不容易看懂流程,因為我花了一天的時間來實現一個功能齊全的IOCP伺服器,
效果如下
程式碼
首先是ICOPServer.cs 這個類是IOCP伺服器的核心類,目前這個類是網路上比較全的程式碼,MSDN上面的例子都沒有我的全
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
namespace ServerTest
{
/// <summary>
/// IOCP SOCKET伺服器
/// </summary>
public class IOCPServer : IDisposable
{
const int opsToPreAlloc = 2;
#region Fields
/// <summary>
/// 伺服器程式允許的最大客戶端連線數
/// </summary>
private int _maxClient;
/// <summary>
/// 監聽Socket,用於接受客戶端的連線請求
/// </summary>
private Socket _serverSock;
/// <summary>
/// 當前的連線的客戶端數
/// </summary>
private int _clientCount;
/// <summary>
/// 用於每個I/O Socket操作的緩衝區大小
/// </summary>
private int _bufferSize = 1024;
/// <summary>
/// 訊號量
/// </summary>
Semaphore _maxAcceptedClients;
/// <summary>
/// 緩衝區管理
/// </summary>
BufferManager _bufferManager;
/// <summary>
/// 物件池
/// </summary>
SocketAsyncEventArgsPool _objectPool;
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>
/// 非同步IOCP SOCKET伺服器
/// </summary>
/// <param name="listenPort">監聽的埠</param>
/// <param name="maxClient">最大的客戶端數量</param>
public IOCPServer(int listenPort,int maxClient)
: this(IPAddress.Any, listenPort, maxClient)
{
}
/// <summary>
/// 非同步Socket TCP伺服器
/// </summary>
/// <param name="localEP">監聽的終結點</param>
/// <param name="maxClient">最大客戶端數量</param>
public IOCPServer(IPEndPoint localEP, int maxClient)
: this(localEP.Address, localEP.Port,maxClient)
{
}
/// <summary>
/// 非同步Socket TCP伺服器
/// </summary>
/// <param name="localIPAddress">監聽的IP地址</param>
/// <param name="listenPort">監聽的埠</param>
/// <param name="maxClient">最大客戶端數量</param>
public IOCPServer(IPAddress localIPAddress, int listenPort, int maxClient)
{
this.Address = localIPAddress;
this.Port = listenPort;
this.Encoding = Encoding.Default;
_maxClient = maxClient;
_serverSock = new Socket(localIPAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
_bufferManager = new BufferManager(_bufferSize * _maxClient * opsToPreAlloc,_bufferSize);
_objectPool = new SocketAsyncEventArgsPool(_maxClient);
_maxAcceptedClients = new Semaphore(_maxClient, _maxClient);
}
#endregion
#region 初始化
/// <summary>
/// 初始化函式
/// </summary>
public void Init()
{
// Allocates one large byte buffer which all I/O operations use a piece of. This gaurds
// against memory fragmentation
_bufferManager.InitBuffer();
// preallocate pool of SocketAsyncEventArgs objects
SocketAsyncEventArgs readWriteEventArg;
for (int i = 0; i < _maxClient; i++)
{
//Pre-allocate a set of reusable SocketAsyncEventArgs
readWriteEventArg = new SocketAsyncEventArgs();
readWriteEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(OnIOCompleted);
readWriteEventArg.UserToken = null;
// assign a byte buffer from the buffer pool to the SocketAsyncEventArg object
_bufferManager.SetBuffer(readWriteEventArg);
// add SocketAsyncEventArg to the pool
_objectPool.Push(readWriteEventArg);
}
}
#endregion
#region Start
/// <summary>
/// 啟動
/// </summary>
public void Start()
{
if (!IsRunning)
{
Init();
IsRunning = true;
IPEndPoint localEndPoint = new IPEndPoint(Address, Port);
// 建立監聽socket
_serverSock = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
//_serverSock.ReceiveBufferSize = _bufferSize;
//_serverSock.SendBufferSize = _bufferSize;
if (localEndPoint.AddressFamily == AddressFamily.InterNetworkV6)
{
// 配置監聽socket為 dual-mode (IPv4 & IPv6)
// 27 is equivalent to IPV6_V6ONLY socket option in the winsock snippet below,
_serverSock.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, false);
_serverSock.Bind(new IPEndPoint(IPAddress.IPv6Any, localEndPoint.Port));
}
else
{
_serverSock.Bind(localEndPoint);
}
// 開始監聽
_serverSock.Listen(this._maxClient);
// 在監聽Socket上投遞一個接受請求。
StartAccept(null);
}
}
#endregion
#region Stop
/// <summary>
/// 停止服務
/// </summary>
public void Stop()
{
if (IsRunning)
{
IsRunning = false;
_serverSock.Close();
//TODO 關閉對所有客戶端的連線
}
}
#endregion
#region Accept
/// <summary>
/// 從客戶端開始接受一個連線操作
/// </summary>
private void StartAccept(SocketAsyncEventArgs asyniar)
{
if (asyniar == null)
{
asyniar = new SocketAsyncEventArgs();
asyniar.Completed += new EventHandler<SocketAsyncEventArgs>(OnAcceptCompleted);
}
else
{
//socket must be cleared since the context object is being reused
asyniar.AcceptSocket = null;
}
_maxAcceptedClients.WaitOne();
if (!_serverSock.AcceptAsync(asyniar))
{
ProcessAccept(asyniar);
//如果I/O掛起等待非同步則觸發AcceptAsyn_Asyn_Completed事件
//此時I/O操作同步完成,不會觸發Asyn_Completed事件,所以指定BeginAccept()方法
}
}
/// <summary>
/// accept 操作完成時回撥函式
/// </summary>
/// <param name="sender">Object who raised the event.</param>
/// <param name="e">SocketAsyncEventArg associated with the completed accept operation.</param>
private void OnAcceptCompleted(object sender, SocketAsyncEventArgs e)
{
ProcessAccept(e);
}
/// <summary>
/// 監聽Socket接受處理
/// </summary>
/// <param name="e">SocketAsyncEventArg associated with the completed accept operation.</param>
private void ProcessAccept(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
Socket s = e.AcceptSocket;//和客戶端關聯的socket
if (s.Connected)
{
try
{
Interlocked.Increment(ref _clientCount);//原子操作加1
SocketAsyncEventArgs asyniar = _objectPool.Pop();
asyniar.UserToken = s;
Log4Debug(String.Format("客戶 {0} 連入, 共有 {1} 個連線。", s.RemoteEndPoint.ToString(), _clientCount));
if (!s.ReceiveAsync(asyniar))//投遞接收請求
{
ProcessReceive(asyniar);
}
}
catch (SocketException ex)
{
Log4Debug(String.Format("接收客戶 {0} 資料出錯, 異常資訊: {1} 。", s.RemoteEndPoint, ex.ToString()));
//TODO 異常處理
}
//投遞下一個接受請求
StartAccept(e);
}
}
}
#endregion
#region 傳送資料
/// <summary>
/// 非同步的傳送資料
/// </summary>
/// <param name="e"></param>
/// <param name="data"></param>
public void Send(SocketAsyncEventArgs e, byte[] data)
{
if (e.SocketError == SocketError.Success)
{
Socket s = e.AcceptSocket;//和客戶端關聯的socket
if (s.Connected)
{
Array.Copy(data, 0, e.Buffer, 0, data.Length);//設定傳送資料
//e.SetBuffer(data, 0, data.Length); //設定傳送資料
if (!s.SendAsync(e))//投遞傳送請求,這個函式有可能同步傳送出去,這時返回false,並且不會引發SocketAsyncEventArgs.Completed事件
{
// 同步傳送時處理髮送完成事件
ProcessSend(e);
}
else
{
CloseClientSocket(e);
}
}
}
}
/// <summary>
/// 同步的使用socket傳送資料
/// </summary>
/// <param name="socket"></param>
/// <param name="buffer"></param>
/// <param name="offset"></param>
/// <param name="size"></param>
/// <param name="timeout"></param>
public void Send(Socket socket, byte[] buffer, int offset, int size, int timeout)
{
socket.SendTimeout = 0;
int startTickCount = Environment.TickCount;
int sent = 0; // how many bytes is already sent
do
{
if (Environment.TickCount > startTickCount + timeout)
{
//throw new Exception("Timeout.");
}
try
{
sent += socket.Send(buffer, offset + sent, size - sent, SocketFlags.None);
}
catch (SocketException ex)
{
if (ex.SocketErrorCode == SocketError.WouldBlock ||
ex.SocketErrorCode == SocketError.IOPending ||
ex.SocketErrorCode == SocketError.NoBufferSpaceAvailable)
{
// socket buffer is probably full, wait and try again
Thread.Sleep(30);
}
else
{
throw ex; // any serious error occurr
}
}
} while (sent < size);
}
/// <summary>
/// 傳送完成時處理函式
/// </summary>
/// <param name="e">與傳送完成操作相關聯的SocketAsyncEventArg物件</param>
private void ProcessSend(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
Socket s = (Socket)e.UserToken;
//TODO
}
else
{
CloseClientSocket(e);
}
}
#endregion
#region 接收資料
/// <summary>
///接收完成時處理函式
/// </summary>
/// <param name="e">與接收完成操作相關聯的SocketAsyncEventArg物件</param>
private void ProcessReceive(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)//if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
{
// 檢查遠端主機是否關閉連線
if (e.BytesTransferred > 0)
{
Socket s = (Socket)e.UserToken;
//判斷所有需接收的資料是否已經完成
if (s.Available == 0)
{
//從偵聽者獲取接收到的訊息。
//String received = Encoding.ASCII.GetString(e.Buffer, e.Offset, e.BytesTransferred);
//echo the data received back to the client
//e.SetBuffer(e.Offset, e.BytesTransferred);
byte[] data = new byte[e.BytesTransferred];
Array.Copy(e.Buffer, e.Offset, data, 0, data.Length);//從e.Buffer塊中複製資料出來,保證它可重用
string info=Encoding.Default.GetString(data);
Log4Debug(String.Format("收到 {0} 資料為 {1}",s.RemoteEndPoint.ToString(),info));
//TODO 處理資料
//增加伺服器接收的總位元組數。
}
if (!s.ReceiveAsync(e))//為接收下一段資料,投遞接收請求,這個函式有可能同步完成,這時返回false,並且不會引發SocketAsyncEventArgs.Completed事件
{
//同步接收時處理接收完成事件
ProcessReceive(e);
}
}
}
else
{
CloseClientSocket(e);
}
}
#endregion
#region 回撥函式
/// <summary>
/// 當Socket上的傳送或接收請求被完成時,呼叫此函式
/// </summary>
/// <param name="sender">激發事件的物件</param>
/// <param name="e">與傳送或接收完成操作相關聯的SocketAsyncEventArg物件</param>
private void OnIOCompleted(object sender, SocketAsyncEventArgs e)
{
// Determine which type of operation just completed and call the associated handler.
switch (e.LastOperation)
{
case SocketAsyncOperation.Accept:
ProcessAccept(e);
break;
case SocketAsyncOperation.Receive:
ProcessReceive(e);
break;
default:
throw new ArgumentException("The last operation completed on the socket was not a receive or send");
}
}
#endregion
#region Close
/// <summary>
/// 關閉socket連線
/// </summary>
/// <param name="e">SocketAsyncEventArg associated with the completed send/receive operation.</param>
private void CloseClientSocket(SocketAsyncEventArgs e)
{
Log4Debug(String.Format("客戶 {0} 斷開連線!",((Socket)e.UserToken).RemoteEndPoint.ToString()));
Socket s = e.UserToken as Socket;
CloseClientSocket(s, e);
}
/// <summary>
/// 關閉socket連線
/// </summary>
/// <param name="s"></param>
/// <param name="e"></param>
private void CloseClientSocket(Socket s, SocketAsyncEventArgs e)
{
try
{
s.Shutdown(SocketShutdown.Send);
}
catch (Exception)
{
// Throw if client has closed, so it is not necessary to catch.
}
finally
{
s.Close();
}
Interlocked.Decrement(ref _clientCount);
_maxAcceptedClients.Release();
_objectPool.Push(e);//SocketAsyncEventArg 物件被釋放,壓入可重用佇列。
}
#endregion
#region Dispose
/// <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 事件
}
}
disposed = true;
}
}
#endregion
public void Log4Debug(string msg)
{
Console.WriteLine("notice:"+msg);
}
}
}
BufferManager.cs 這個類是快取管理類,是採用MSDN上面的例子一樣的 地址: https://msdn.microsoft.com/zh-cn/library/bb517542.aspx
SocketAsyncEventArgsPool.cs 這個類也是來自MSDN的 地址:https://msdn.microsoft.com/zh-cn/library/system.net.sockets.socketasynceventargs.aspx
需要的話自己到MSDN網站上去取,我就不貼出來了
伺服器端
static void Main(string[] args)
{
IOCPServer server = new IOCPServer(8088, 1024);
server.Start();
Console.WriteLine("伺服器已啟動....");
System.Console.ReadLine();
}
客戶端
客戶端程式碼也是很簡單
static void Main(string[] args)
{
IPAddress remote=IPAddress.Parse("192.168.3.4");
client c = new client(8088,remote);
c.connect();
Console.WriteLine("伺服器連線成功!");
while (true)
{
Console.Write("send>");
string msg=Console.ReadLine();
if (msg == "exit")
break;
c.send(msg);
}
c.disconnect();
Console.ReadLine();
}
public class client
{
public TcpClient _client;
public int port;
public IPAddress remote;
public client(int port,IPAddress remote)
{
this.port = port;
this.remote = remote;
}
public void connect()
{
this._client=new TcpClient();
_client.Connect(remote, port);
}
public void disconnect()
{
_client.Close();
}
public void send(string msg)
{
byte[] data=Encoding.Default.GetBytes(msg);
_client.GetStream().Write(data, 0, data.Length);
}
}
IOCPClient類,使用SocketAsyncEventArgs類建立一個Socket客戶端。雖然MSDN說這個類特別設計給網路伺服器應用,但也沒有限制在客戶端程式碼中使用APM。下面給出了IOCPClient類的樣例程式碼:
public class IOCPClient
{
/// <summary>
/// 連線伺服器的socket
/// </summary>
private Socket _clientSock;
/// <summary>
/// 用於伺服器執行的互斥同步物件
/// </summary>
private static Mutex mutex = new Mutex();
/// <summary>
/// Socket連線標誌
/// </summary>
private Boolean _connected = false;
private const int ReceiveOperation = 1, SendOperation = 0;
private static AutoResetEvent[]
autoSendReceiveEvents = new AutoResetEvent[]
{
new AutoResetEvent(false),
new AutoResetEvent(false)
};
/// <summary>
/// 伺服器監聽端點
/// </summary>
private IPEndPoint _remoteEndPoint;
public IOCPClient(IPEndPoint local,IPEndPoint remote)
{
_clientSock = new Socket(local.AddressFamily,SocketType.Stream, ProtocolType.Tcp);
_remoteEndPoint = remote;
}
#region 連線伺服器
/// <summary>
/// 連線遠端伺服器
/// </summary>
public void Connect()
{
SocketAsyncEventArgs connectArgs = new SocketAsyncEventArgs();
connectArgs.UserToken = _clientSock;
connectArgs.RemoteEndPoint = _remoteEndPoint;
connectArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnConnected);
mutex.WaitOne();
if (!_clientSock.ConnectAsync(connectArgs))//非同步連線
{
ProcessConnected(connectArgs);
}
}
/// <summary>
/// 連線上的事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void OnConnected(object sender, SocketAsyncEventArgs e)
{
mutex.ReleaseMutex();
//設定Socket已連線標誌。
_connected = (e.SocketError == SocketError.Success);
}
/// <summary>
/// 處理連線伺服器
/// </summary>
/// <param name="e"></param>
private void ProcessConnected(SocketAsyncEventArgs e)
{
//TODO
}
#endregion
#region 傳送訊息
/// <summary>
/// 向伺服器傳送訊息
/// </summary>
/// <param name="data"></param>
public void Send(byte[] data)
{
SocketAsyncEventArgs asyniar = new SocketAsyncEventArgs();
asyniar.Completed += new EventHandler<SocketAsyncEventArgs>(OnSendComplete);
asyniar.SetBuffer(data, 0, data.Length);
asyniar.UserToken = _clientSock;
asyniar.RemoteEndPoint = _remoteEndPoint;
autoSendReceiveEvents[SendOperation].WaitOne();
if (!_clientSock.SendAsync(asyniar))//投遞傳送請求,這個函式有可能同步傳送出去,這時返回false,並且不會引發SocketAsyncEventArgs.Completed事件
{
// 同步傳送時處理髮送完成事件
ProcessSend(asyniar);
}
}
/// <summary>
/// 傳送操作的回撥方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnSendComplete(object sender, SocketAsyncEventArgs e)
{
//發出傳送完成訊號。
autoSendReceiveEvents[SendOperation].Set();
ProcessSend(e);
}
/// <summary>
/// 傳送完成時處理函式
/// </summary>
/// <param name="e">與傳送完成操作相關聯的SocketAsyncEventArg物件</param>
private void ProcessSend(SocketAsyncEventArgs e)
{
//TODO
}
#endregion
#region 接收訊息
/// <summary>
/// 開始監聽服務端資料
/// </summary>
/// <param name="e"></param>
public void StartRecive(SocketAsyncEventArgs e)
{
//準備接收。
Socket s = e.UserToken as Socket;
byte[] receiveBuffer = new byte[255];
e.SetBuffer(receiveBuffer, 0, receiveBuffer.Length);
e.Completed += new EventHandler<SocketAsyncEventArgs>(OnReceiveComplete);
autoSendReceiveEvents[ReceiveOperation].WaitOne();
if (!s.ReceiveAsync(e))
{
ProcessReceive(e);
}
}
/// <summary>
/// 接收操作的回撥方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnReceiveComplete(object sender, SocketAsyncEventArgs e)
{
//發出接收完成訊號。
autoSendReceiveEvents[ReceiveOperation].Set();
ProcessReceive(e);
}
/// <summary>
///接收完成時處理函式
/// </summary>
/// <param name="e">與接收完成操作相關聯的SocketAsyncEventArg物件</param>
private void ProcessReceive(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
// 檢查遠端主機是否關閉連線
if (e.BytesTransferred > 0)
{
Socket s = (Socket)e.UserToken;
//判斷所有需接收的資料是否已經完成
if (s.Available == 0)
{
byte[] data = new byte[e.BytesTransferred];
Array.Copy(e.Buffer, e.Offset, data, 0, data.Length);//從e.Buffer塊中複製資料出來,保證它可重用
//TODO 處理資料
}
if (!s.ReceiveAsync(e))//為接收下一段資料,投遞接收請求,這個函式有可能同步完成,這時返回false,並且不會引發SocketAsyncEventArgs.Completed事件
{
//同步接收時處理接收完成事件
ProcessReceive(e);
}
}
}
}
#endregion
public void Close()
{
_clientSock.Disconnect(false);
}
/// <summary>
/// 失敗時關閉Socket,根據SocketError丟擲異常。
/// </summary>
/// <param name="e"></param>
private void ProcessError(SocketAsyncEventArgs e)
{
Socket s = e.UserToken as Socket;
if (s.Connected)
{
//關閉與客戶端關聯的Socket
try
{
s.Shutdown(SocketShutdown.Both);
}
catch (Exception)
{
//如果客戶端處理已經關閉,丟擲異常
}
finally
{
if (s.Connected)
{
s.Close();
}
}
}
//丟擲SocketException
throw new SocketException((Int32)e.SocketError);
}
/// <summary>
/// 釋放SocketClient例項
/// </summary>
public void Dispose()
{
mutex.Close();
autoSendReceiveEvents[SendOperation].Close();
autoSendReceiveEvents[ReceiveOperation].Close();
if (_clientSock.Connected)
{
_clientSock.Close();
}
}
}
這個類我沒有測試,但是理論上是沒問題的。
相關文章
- C#高效能大容量SOCKET併發(一):IOCP完成埠例子介紹C#
- C# 實現socket通訊程式(伺服器端)C#伺服器
- C#中使用Socket實現簡單Web伺服器C#Web伺服器
- c#實現最簡單的socket通訊C#
- Visual C#託管Socket的實現方法C#
- Socket打造高效能伺服器的求救!求救!求救!伺服器
- epoll+socket實現 socket併發 linux伺服器Linux伺服器
- C# 通過socket實現UDP 通訊C#UDP
- 如何用PHP實現Socket伺服器PHP伺服器
- NIO Socket實現檔案伺服器伺服器
- HttpServer: 基於IOCP模型且整合Openssl的輕量級高效能web伺服器HTTPServer模型Web伺服器
- NET平臺下TCP實現IOCP例子TCP
- C# 如何實現簡單的Socket通訊(附示例)C#
- 用C#的Raw Socket實現網路封包監視C#
- 高效能IOT伺服器實現之路伺服器
- C#高效能 TCP 服務的多種實現方式C#TCP
- Netty實現Http高效能伺服器NettyHTTP伺服器
- java的nio之:java的bio流下實現的socket伺服器同步阻塞模型和socket的偽非同步的socket伺服器的通訊模型Java伺服器模型非同步
- socket 實現的 web 伺服器在 Windows 下的讀寫問題Web伺服器Windows
- socket程式設計實現tcp伺服器_C/C++程式設計TCP伺服器C++
- C#中自己動手建立一個Web Server(非Socket實現)C#WebServer
- C#中使用Socket請求Web伺服器過程C#Web伺服器
- 用C#下的Raw Socket程式設計實現網路封包監視 (轉)C#程式設計
- C#實現伺服器間檔案同步C#伺服器
- Netty實現Web SocketNettyWeb
- Tomcat實現Web SocketTomcatWeb
- socket實現聊天功能(二)
- php原生socket之IO多路複用以及實現web伺服器PHPWeb伺服器
- C#實現一個最簡單的HTTP伺服器C#HTTP伺服器
- Socket高效能IO模型淺析模型
- IOCP 完成埠
- php實現一個簡單的socketPHP
- 《初識TCP》iOS、macOS實現socket client與socket serverTCPiOSMacclientServer
- Hyperf-Socket.io實現私聊
- Spring Boot實現Web SocketSpring BootWeb
- 實現Java集合迭代的高效能Java
- C#樹的實現C#
- C#高效能陣列複製實驗C#陣列