本文作者:小飞Jim,转载请写明出处:http://blog.csdn.net/lanmangfeige/article/details/53118043
C#实现Socket通讯有同步模式与异步模式,异步模式的效率比同步模式高。在异步模式中,SocketAsyncEventArgs 类提供了增强功能。
这些增强功能可以避免重复分配的和在大量异步套接字 I/O 内进行同步的对象。通过创建一个SocketAsyncEventArgsPool 池来提高客户端连接速率。
源码下载地址:http://download.csdn.net/detail/lanmangfeige/9679294
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace CsharpIOCP
{
class IOCP
{
///
/// Socket-Server
///
Socket s_Server;
///
/// 通讯SAEA池
///
SAEAPool saeaPool_Receive;
///
/// 侦听客户端
///
public void ListenClient()
{
try
{
int iClientMaxCount = 1000; ;//最大客户端数量
s_Server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
s_Server.Bind(new IPEndPoint(IPAddress.Any, 8801));
s_Server.Listen(iClientMaxCount);
int ibufferSize = 1024; //每个缓冲区大小
BufferManager bufferManager = new BufferManager(ibufferSize * iClientMaxCount, ibufferSize);
saeaPool_Receive = new SAEAPool(iClientMaxCount);
for (int i = 0; i < iClientMaxCount; i++) //填充SocketAsyncEventArgs池
{
SocketAsyncEventArgs saea_New = new SocketAsyncEventArgs();
saea_New.Completed += new EventHandler(OnIOCompleted);
bufferManager.SetBuffer(saea_New);
SAEAUserToken userToken = new SAEAUserToken();
SocketAsyncEventArgs saea_Send = new SocketAsyncEventArgs();
saea_Send.Completed += new EventHandler(OnIOCompleted);
userToken.SAEA_Send = saea_Send;
userToken.HeartbeatTime = DateTime.Now;
saea_New.UserToken = userToken;
saeaPool_Receive.Add(saea_New);
}
Thread tCheckClientHeartbeat = new Thread(CheckClientHeartbeat);
tCheckClientHeartbeat.IsBackground = true;
tCheckClientHeartbeat.Start();
StartAccept(null);
}
catch { }
}
///
/// 接受来自客户机的连接请求操作
///
private void StartAccept(SocketAsyncEventArgs saea_Accept)
{
if (saea_Accept == null)
{
saea_Accept = new SocketAsyncEventArgs();
saea_Accept.Completed += new EventHandler(OnAcceptCompleted);
}
else
saea_Accept.AcceptSocket = null; //重用前进行对象清理
if (!s_Server.AcceptAsync(saea_Accept))
ProcessAccept(saea_Accept);
}
///
/// 连接完成异步操作回调
///
private void OnAcceptCompleted(object sender, SocketAsyncEventArgs e)
{
ProcessAccept(e);
}
///
/// 接收或发送完成异步操作回调
///
private void OnIOCompleted(object sender, SocketAsyncEventArgs e)
{
switch (e.LastOperation)
{
case SocketAsyncOperation.Receive:
ProcessReceive(e);
break;
case SocketAsyncOperation.Send:
ProcessSend(e);
break;
}
}
///
/// 异步连接操作完成后调用该方法
///
private void ProcessAccept(SocketAsyncEventArgs e)
{
Socket s = e.AcceptSocket;
if (s != null && s.Connected)
{
try
{
string sClientIP = ((IPEndPoint)s.RemoteEndPoint).Address.ToString();
Console.WriteLine(sClientIP + " Client online");
SocketAsyncEventArgs saea_Receive = saeaPool_Receive.Pull();
if (saea_Receive != null)
{
Console.WriteLine("Online Client total:" + saeaPool_Receive.GetUsedSAEACount());
SAEAUserToken userToken = (SAEAUserToken)saea_Receive.UserToken;
userToken.S = s;
if (!userToken.S.ReceiveAsync(saea_Receive))
ProcessReceive(saea_Receive);
}
else
{
s.Close();
Console.WriteLine(sClientIP + " Can't connect server,because connection pool has been finished !");
}
}
catch { }
}
StartAccept(e);
}
///
/// 异步接收操作完成后调用该方法
///
private void ProcessReceive(SocketAsyncEventArgs e)
{
try
{
if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
{
SAEAUserToken userToken = (SAEAUserToken)e.UserToken;
userToken.HeartbeatTime = DateTime.Now;
string sClientIP = ((IPEndPoint)userToken.S.RemoteEndPoint).Address.ToString();
try
{
byte[] abFactReceive = new byte[e.BytesTransferred];
Array.Copy(e.Buffer, e.Offset, abFactReceive, 0, e.BytesTransferred);
Console.WriteLine("From the " + sClientIP + " to receive " + e.BytesTransferred + " bytes of data:" + BitConverter.ToString(abFactReceive));
}
catch { }
finally
{
if (!userToken.S.ReceiveAsync(e))
ProcessReceive(e);
}
}
else
CloseClientSocket(e);
}
catch { }
}
///
/// 异步发送操作完成后调用该方法
///
private void ProcessSend(SocketAsyncEventArgs e)
{
}
///
/// Socket 断开处理
///
private void CloseClientSocket(SocketAsyncEventArgs saea)
{
try
{
SAEAUserToken userToken = (SAEAUserToken)saea.UserToken;
if (!saeaPool_Receive.Push(saea))
return;
if (userToken.S != null)
{
if (userToken.S.Connected)
{
try
{
userToken.S.Shutdown(SocketShutdown.Both);
}
catch { }
string sClientIP = ((IPEndPoint)userToken.S.RemoteEndPoint).Address.ToString();
Console.WriteLine(sClientIP + " disconnect !");
}
userToken.S.Close();
}
Console.WriteLine("Online Client total:" + saeaPool_Receive.GetUsedSAEACount());
}
catch { }
}
///
/// 客户端心跳检测
///
private void CheckClientHeartbeat()
{
while (true)
{
try
{
int iCheckInterval = 10 * 1000; //10秒检测间隔
Thread.Sleep(iCheckInterval);
List lUserdSAEA = saeaPool_Receive.GetUsedSAEA();
if (lUserdSAEA != null && lUserdSAEA.Count > 0)
{
foreach (SocketAsyncEventArgs saea in lUserdSAEA)
{
SAEAUserToken userToken = (SAEAUserToken)saea.UserToken;
if (userToken.HeartbeatTime.AddMilliseconds(iCheckInterval).CompareTo(DateTime.Now) < 0)
{
if (userToken.S != null)
{
try
{
string sClientIP = ((IPEndPoint)userToken.S.RemoteEndPoint).Address.ToString();
Console.WriteLine(sClientIP + " the heartbeat timeout !");
}
catch { }
userToken.S.Close(); //服务端主动关闭心跳超时连接,在此关闭连接,会触发OnIOCompleted回调
}
}
}
}
}
catch { }
}
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
namespace CsharpIOCP
{
///
/// 与每个客户Socket相关联,进行Send和Receive投递时所需要的参数
///
internal sealed class SAEAPool
{
List pool; //为每一个Socket客户端分配一个SocketAsyncEventArgs,用一个List管理,在程序启动时建立
Int32 capacity; //pool对象池的容量
Int32 boundary; //已分配和未分配对象的边界
internal SAEAPool(Int32 capacity)
{
pool = new List();
boundary = 0;
this.capacity = capacity;
}
///
/// 往pool对象池中增加新建立的对象,因为这个程序在启动时会建立好所有对象,
/// 故这个方法只在初始化时会被调用,因此,没有加锁。
///
///
///
internal bool Add(SocketAsyncEventArgs arg)
{
if (arg != null && pool.Count < capacity)
{
pool.Add(arg);
boundary++;
return true;
}
else
return false;
}
///
/// 获取正在使用的 SocketAsyncEventArgs 对象集合
///
///
internal List GetUsedSAEA()
{
lock (pool)
{
List lUsedSAEA = new List();
for (int i = boundary; i < capacity; i++)
lUsedSAEA.Add(pool[i]);
return lUsedSAEA;
}
}
///
/// 获取已使用的 SocketAsyncEventArgs 对象总数
///
///
internal string GetUsedSAEACount()
{
return (capacity - boundary).ToString();
}
///
/// 从对象池中取出一个对象,交给一个socket来进行投递请求操作
///
///
internal SocketAsyncEventArgs Pull()
{
lock (pool)
{
if (boundary > 0)
{
--boundary;
return pool[boundary];
}
else
return null;
}
}
///
/// 一个socket客户断开,与其相关的SocketAsyncEventArgs被释放,重新投入Pool中,以备用。
///
///
///
internal bool Push(SocketAsyncEventArgs arg)
{
if (arg != null)
{
lock (pool)
{
int index = pool.IndexOf(arg, boundary); //找出被断开的客户
if (index >= 0)
{
if (index == boundary) //正好是边界元素
boundary++;
else
{
pool[index] = pool[boundary]; //将断开客户移到边界上,边界右移
pool[boundary++] = arg;
}
}
else
return false;
}
return true;
}
else
return false;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
namespace CsharpIOCP
{
///
/// SocketAsyncEventArgs 缓冲区管理类,
/// 创建一个大型缓冲区,该缓冲区可以进行分割并指定给 SocketAsyncEventArgs 对象以便用在每个套接字 I/O 操作中。
/// 这样可以很方便地重用缓冲区,并防止堆内存碎片化。
///
internal class BufferManager
{
int m_numBytes; //缓冲池的总容量
byte[] m_buffer; //缓冲池
Stack m_freeIndexPool; //后进先出数据结构
int m_currentIndex; //当前缓冲池索引
int m_bufferSize; //单个缓冲区容量
///
/// 缓冲池重载
///
/// 缓冲池的总容量
/// 单个缓冲区容量
public BufferManager(int totalBytes, int bufferSize)
{
m_numBytes = totalBytes;
m_currentIndex = 0;
m_bufferSize = bufferSize;
m_freeIndexPool = new Stack();
InitBuffer();
}
///
/// 初始化缓冲池
///
private void InitBuffer()
{
m_buffer = new byte[m_numBytes]; //创建大型缓冲池
}
///
/// 从缓冲池中分配一个缓冲区给指定SocketAsyncEventArgs对象
///
///
///
public bool SetBuffer(SocketAsyncEventArgs args)
{
lock(m_freeIndexPool)
{
if (m_freeIndexPool.Count > 0)
{
args.SetBuffer(m_buffer, m_freeIndexPool.Pop(), m_bufferSize);
}
else
{
if ((m_numBytes - m_bufferSize) < m_currentIndex)
return false;
args.SetBuffer(m_buffer, m_currentIndex, m_bufferSize);
m_currentIndex += m_bufferSize;
}
return true;
}
}
///
/// 将缓冲区释放到缓冲池中
///
///
public void FreeBuffer(SocketAsyncEventArgs args)
{
lock (m_freeIndexPool)
{
m_freeIndexPool.Push(args.Offset);
args.SetBuffer(null, 0, 0);
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
namespace CsharpIOCP
{
///
/// SAEA用户标记类
///
internal class SAEAUserToken
{
///
/// 用于发送数据的SocketAsyncEventArgs
///
public SocketAsyncEventArgs SAEA_Send;
///
/// 连接套接字
///
public Socket S;
///
/// 最新一次心跳时间
///
public DateTime HeartbeatTime;
}
}