本系列仅介绍可用于生产环境的C#异步Socket框架,如果您在其他地方看到类似的代码,不要惊讶,那可能就是我在参考开源代码时,直接“剽窃”过来的。
1、在脑海里思考一下整个socket的链接的处理流程,于是便有了下图。
2、首先就开始监听,代码如下:
public override bool Start() { this._socket = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Stream, PRotocolType.Tcp); //设置KeeyAlive,如果客户端不主动发消息时,Tcp本身会发一个心跳包,来通知服务器,这是一个保持通讯的链接。 //避免等到下一次通讯时,才知道链接已经断开。 this._socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); this._socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true); try { this._socket.Bind(base.SocketConfig.Point); this._socket.Listen(base.SocketConfig.Backlog); this._socket_args = new SocketAsyncEventArgs(); this._socket_args.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptSocketCompleted); //在链接过来的时候,如果IO没有挂起,则AcceptAsync为False,表明同步完成。 if (!this._socket.AcceptAsync(this._socket_args)) { AcceptSocketCompleted(this._socket, this._socket_args); } return true; } catch (Exception ex) { return false; } }void AcceptSocketCompleted(object sender, SocketAsyncEventArgs e) { System.Net.Sockets.Socket socket = null; if (e.SocketError != SocketError.Success) { return; } else { socket = e.AcceptSocket; } e.AcceptSocket = null; bool willRaiseEvent = false; try { //继续监听该端口,在处理逻辑时,不影响其他链接的数据传送。 willRaiseEvent = this._socket.AcceptAsync(e); } catch (Exception ex) { willRaiseEvent = true; } if (socket != null) OnNewClientAccepted(socket, null); if (!willRaiseEvent) AcceptSocketCompleted(null, e); }View Code
3、这个时候链接过来了,就要开始入队列了,如果没有这方面的需求,这一步可以忽略,代码如下:
public class SocketProxy { public System.Net.Sockets.Socket Client; public DateTime Timeout = DateTime.Now; }public class SocketConnectionQueue : IDisposable { private Queue<SocketProxy> _queue; private readonly object _syncObject = new object(); private bool _isStop = false; private Thread _thread; public Action<SocketProxy> Connected; public SocketConnectionQueue() { if (_queue == null) { _queue = new Queue<SocketProxy>(); } if (_thread == null) { _thread = new Thread(Thread_Work) { IsBackground = true, Priority = ThreadPriority.Highest }; _thread.Start(); } } public void Push(SocketProxy connect) { lock (_syncObject) { if (_queue != null) { _queue.Enqueue(connect); } } } public void Thread_Work() { while (!_isStop) { SocketProxy[] socketConnect = null; lock (_syncObject) { if (_queue.Count > 0) { socketConnect = new SocketProxy[_queue.Count]; _queue.CopyTo(socketConnect, 0); _queue.Clear(); } } if (socketConnect != null && socketConnect.Length > 0) { foreach (var client in socketConnect) { if (Connected != null) { Connected.Invoke(client); } } } Thread.Sleep(10); } } public void Dispose() { _isStop = true; if (_thread != null) { _thread.Join(); } } }View Code
4、入完队列,就要开始从链接池子里面分配资源了,你也可以不做链接池,在每次请求过来的时候去实例化一个链接,然后将这个链接入池,我的做法是在程序初始化的时候就分配好一定的资源,代码如下:
public class SocketConnectionPool : IDisposable { private ServerConfig _serverConfig; public IAppServer AppServer; private ConcurrentStack<SocketConnection> _connectPool; private long connect_id = 0; private byte[] _buffer; private readonly object _syncObject = new object(); private SocketConnectionQueue _queue; public Action<System.Net.Sockets.Socket, SocketConnection> Connected; public long GenerateId() { if (connect_id == long.MaxValue) { connect_id = 0; } connect_id++; return connect_id; } public SocketConnectionPool(IAppServer server) { this.AppServer = server; this._serverConfig = server.AppConfig; } public void Init() { var connects = new List<SocketConnection>(this._serverConfig.MaxConnectionNumber); _buffer = new byte[this._serverConfig.BufferSize]; SocketAsyncEventArgs arg; for (var i = 0; i < this._serverConfig.MaxConnectionNumber; i++) { arg = new SocketAsyncEventArgs(); arg.SetBuffer(_buffer, 0, _buffer.Length); connects.Add(new SocketConnection(arg, this)); } _connectPool = new ConcurrentStack<SocketConnection>(connects); if (_queue == null) { _queue = new SocketConnectionQueue(); } _queue.Connected = OnConnected; } public void Push(System.Net.Sockets.Socket socket) { SocketProxy proxy = new SocketProxy() { Client = socket }; _queue.Push(proxy); } public void OnConnected(SocketProxy proxy) { //如果发现队列里面的链接,在Timeout时间内,都没有分配到资源,则关掉链接并丢弃。 int timeout = (int)(DateTime.Now - proxy.Timeout).TotalSeconds; if (timeout >= this._serverConfig.Timeout) { proxy.Client.Close(); return; } else { //没有分配到资源重新入列。 SocketConnection connect = this.GetConnectionFromPool(); if (connect == null) { _queue.Push(proxy); } else { if
新闻热点
疑难解答