写自己的Socket框架(一)

本系列仅介绍可用于生产环境的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);
        }

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();
            }
        }
    }

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 (this.Connected != null)
                    {
                        this.Connected(proxy.Client, connect);
                    }
                }
            }
        }

        /// <summary>
        /// 从链接池去取链接(LIFO)
        /// </summary>
        /// <returns></returns>
        public SocketConnection GetConnectionFromPool()
        {
            //_queue.Push();
            SocketConnection connect;
            if (!_connectPool.TryPop(out connect))
            {
                return null;
            }
            lock (_syncObject)
            {
                long connect_id = this.GenerateId();
                connect.ConnectId = connect_id;
            }
            return connect;
        }
        /// <summary>
        /// 释放链接,并放回链接池
        /// </summary>
        /// <param name="connect"></param>
        public void ReleaseConnection(SocketConnection connect)
        {
            _connectPool.Push(connect);
            LogHelper.Debug(connect.ConnectId + "放回ConnectPool");
        }

        public void Dispose()
        {
            _queue.Dispose();
        }
    }

在Init()里面初始化了很多个SocketConnection,这个就是我们用来管理具体的单个链接的class,代码如下:

public class SocketConnection
    {
        public SocketFlag Flag { get; private set; }

        public SocketConnectionPool Pool { get { return _pool; } private set { } }
        private SocketConnectionPool _pool;

        public SocketAsyncEventArgs RecevieEventArgs { get; set; }

        public long ConnectId { get; set; }

        public SocketConnection()
        {
            this.Flag = SocketFlag.Error;
        }

        public SocketConnection(SocketAsyncEventArgs args, SocketConnectionPool pool)
        {
            RecevieEventArgs = args;
            RecevieEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(SocketEventArgs_Completed);

            this.Flag = SocketFlag.Busy;
            this._pool = pool;
        }

        void SocketEventArgs_Completed(object sender, SocketAsyncEventArgs e)
        {
            var socketSession = e.UserToken as SocketSession;
            if (socketSession == null)
            {
                this.Flag = SocketFlag.Error;
                this.Close();
                return;
            }

            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Receive:
                    socketSession.ReceiveData(e);
                    break;
                default:
                    break;
            }
        }

        public void Initialise(SocketSession session)
        {
            this.RecevieEventArgs.UserToken = session;
            this.Flag = SocketFlag.Busy;

            session.Closed += () =>
            {
                this.Close();
            };
        }

        public void Reset()
        {
            //ConnectId = 0;
            this.RecevieEventArgs.UserToken = null;
            this.Flag = SocketFlag.Idle;
        }

        private void Close()
        {
            this.Reset();
            LogHelper.Debug(ConnectId + " reset");
            this._pool.ReleaseConnection(this);
        }
    }

写自己的Socket框架(一)

时间: 2024-10-10 03:21:49

写自己的Socket框架(一)的相关文章

写自己的socket框架(二)

1.开始正常监听以后,就要开始接受数据了,整体流程图如下: 2.上一节看到我们在程序初始化的时候,初始化了很多个SocketConnection,用于管理客户端的链接,那应用层如何来操作,又什么时候来接受数据?于是我们便有了SocketSession,用于给应用层来管理整个会话过程,代码如下: public class SocketSession : IDisposable { public string SessionId { get; private set; } private Syste

写自己的Socket框架(三)

在通信写完了以后,应用层接收到Socket抛上来的byte[],这个时候对于实际的写逻辑的开发者来说,这样的数据并不友好,我们就需要在应用层统一一个包的规则(应用层协议),处理完以后,然后再传给实际的逻辑层去处理. 以下是一个常用的Command模式.既接收到传递过来的包以后,根据Command(命令)来执行对应的Command(逻辑). 我们假定我们的包(以下所有的包都指的是应用层的包,而非Socket层的包)分为 命令头/数据 两块. public class InterUnit { pub

atitit.软件开发--socket框架选型--netty vs mina j

atitit.软件开发--socket框架选型--netty vs mina j . Netty是由JBOSS提供的一个java开源框架 Apache mina 三.文档比较 mina文档多,,, 好几倍... 作者:: 老哇的爪子 Attilax 艾龙,  EMAIL:[email protected] 转载请注明来源: http://blog.csdn.net/attilax 四.UDP协议传输 1. netty将UDP无连接的特性暴露出来:而mina对UDP进行了高级层次的抽象,可以把UD

socket框架续,异步socket

上一篇对socket进行了简单的封装,但是由于send和recv都是同步的,真正使用的时候一般都需要异步的,如果让应用层自己负责维护异步线程,那这个框架就不实用了,所以异步收发还是要有的. 不知道怎么说起,先占坑以后再慢慢说吧. 我的实现方法: 1.创建SendBuffer和RecvBuffer,包装发送和接收的消息. 2.使用循环队列创建发送队列和接收队列,异步调用只需要将消息加入队列.(循环队列的实现可以参考 http://www.cnblogs.com/wolfred7464/p/4337

高性能异步Socket框架

这是一个L/V(Length/Value)模型的异步Socket框架.L是指发送byte数组的长度,L固定为10个byte,V是指要发送的byte数组.可以用于处理超长的消息,处理完成的响应也按照L/V模型发送,客户端接收可以参考服务端解析过程. eg: 1.第一次收到的byte[]:0 0 0 0 0 0 1 1 1 2 28 30 18 26 88 99 77 那么先取0 0 0 0 0 0 1 1 1 2,得到长度为1112,对后面的byte(从index10以后)进行存储 2.第二次收到

我写的一个mvc框架讲解之一

从最原始的在jsp页面里面写代码到使用框架写代码,一路走来,大大小小的项目做了许多,接触过的mvc框架也有很多,目前开发界比较主流的mvc框架是struts2和spring mvc,都有各自缺点和优点,在项目使用过程中总有不尽人意的地方,下面主要讲解一下struts2和spring mvc在项目使用的不足之处,最终引入一个我自己写的一个mvc框架,虽然本框架还不够完善,也不敢说有多好,只是说比较合适于我的开发方式,并且已经在多个项目中使用.什么是mvc以及mvc原理,我不做讲解,自己百度 str

分享一直在维护简单实用高效的C++Socket框架Swa-server(开源+源码)

Swa-server 开源框架* 适用于中小型游戏,如:养成.RPG.棋牌等:应用软件,如:聊天室等* 已经封套好底层socket管理,sql请求处理.数据加密解密* 拿来即可开工写业务* Swa-server是面向小型快速开发的框架,所以采用单进程模式,以后更新也是单进程方案去解决所遇到的问题,这样可以减少开发时间与人力(其实很多服务器用单进程就够了) * 支持IO异步(基于boost库IO)* 支持sql异步执行(有回调函数)* 玩家管理器* 数据库管理器 项目中例子* 1.请求获得动态密码

自己写的轻量级PHP框架trig与laravel,yii性能对比

看了下当前最热门的php开发框架,想对比一下自己写的框架与这些框架的性能对比. 看结果对比. laravel 5.1: yii2: trig: 自己写的框架速度是lavavel 5.1的8倍,是yii2的5.3倍.

Android 框架炼成 教你怎样写组件间通信框架EventBus

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/41096639 .本文出自:[张鸿洋的博客] 1.概述 关于Eventbus的介绍.前面已经有两篇:Android EventBus实战 没听过你就out了和Android EventBus源代码解析 带你深入理解EventBus . 假设你觉得还有问题,没关系,接下来我带大家手把手打造从无到有的编写这种框架~~~ 首先我们回想一下,这玩意就是在register时,扫描类中复合命