C#实现一个简单的IOCP模式的服务端监听框架

项目托管地址:https://github.com/hooow-does-it-work/iocp-sharp

本类直接继承了SocketAsyncEventArgs,重写其内部的OnCompleted方法来处理客户端连接。
只是一个基础的监听框架,具体的业务逻辑需要在子类实现NewClient方法。

博主后续的网络相关的文章,都会基于本框架作为服务器。

一个例子
public class Socks5Server : TcpIocpServer
{
    /// <summary>
    /// 实现NewClient方法
    /// </summary>
    /// <param name="client"></param>
    protected override void NewClient(Socket client)
    {
    	//连接后就关闭
        Stream clientStream = new NetworkStream(client, true);
        clientStream.close();
    }
}
完整的框架代码
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace Socks5.Server
{
    /// <summary>
    /// 直接继承SocketAsyncEventArgs,作为服务器
    /// </summary>
    public abstract class TcpIocpServer : SocketAsyncEventArgs
    {
        private Socket _socket = null;
        private IPEndPoint _localEndPoint = null;

        /// <summary>
        /// 基础Socket
        /// </summary>
        protected Socket Socket => _socket;

        /// <summary>
        /// 本地监听终结点
        /// </summary>
        public IPEndPoint LocalEndPoint => _localEndPoint;

        /// <summary>
        /// 实例化服务器
        /// </summary>
        public TcpIocpServer() : base()
        {
        }

        /// <summary>
        /// 启动服务器
        /// </summary>
        /// <returns>true成功,false失败</returns>
        protected virtual void Start()
        {
            if (_localEndPoint == null) throw new ArgumentNullException("LocalEndPoint");

            _socket = new Socket(_localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

            _socket.Bind(_localEndPoint);
            _socket.Listen(256);
            _localEndPoint = _socket.LocalEndPoint as IPEndPoint;

            StartAccept();
            Started();
        }

        /// <summary>
        /// 使用本地终结点启动服务器
        /// </summary>
        /// <param name="localEndPoint">本地终结点</param>
        /// <returns></returns>
        public void Start(EndPoint localEndPoint)
        {
            _localEndPoint = localEndPoint as IPEndPoint;
            Start();
        }


        /// <summary>
        /// 使用IP地址和端口启动服务器
        /// </summary>
        /// <param name="iPAddress">ip地址</param>
        /// <param name="port">监听端口,0系统自动使用可用端口,可以在服务器启动后,通过LocalEndPoint属性获取到真正监听的端口</param>
        public void Start(IPAddress iPAddress, int port = 0)
        {
            Start(new IPEndPoint(iPAddress, port));
        }

        /// <summary>
        /// 使用IP地址和端口启动服务器
        /// </summary>
        /// <param name="iPAddress">ip地址</param>
        /// <param name="port">监听端口,0系统自动使用可用端口,可以在服务器启动后,通过LocalEndPoint属性获取到真正监听的端口</param>
        public void Start(string iPAddress, int port = 0)
        {
            Start(new IPEndPoint(IPAddress.Parse(iPAddress), port));
        }

        /// <summary>
        /// 服务器启动后调用
        /// </summary>
        protected virtual void Started()
        {

        }
        /// <summary>
        /// 服务器被停止时调用
        /// </summary>
        protected virtual void Stoped()
        {

        }

        /// <summary>
        /// 停止服务器
        /// </summary>
        public virtual void Stop()
        {
            try
            {
                _socket?.Close();
            }
            catch { }
            Stoped();
        }

        /// <summary>
        /// 服务器发生异常时调用
        /// </summary>
        /// <param name="e">异常</param>
        protected virtual void Error(Exception e)
        {

        }

        /// <summary>
        /// 开始接受客户端请求
        /// </summary>
        private void StartAccept()
        {
            AcceptSocket = null;

            try
            {
                if (!_socket.AcceptAsync(this))
                {
                    OnCompleted(this);
                }
            }
            catch (SocketException e)
            {
                Error(e);
            }
            catch (ObjectDisposedException)
            {
            }
        }

        /// <summary>
        /// 重写OnCompleted方法
        /// </summary>
        /// <param name="e"></param>
        protected sealed override void OnCompleted(SocketAsyncEventArgs e)
        {
            if (SocketError != SocketError.Success)
            {
                if (SocketError == SocketError.OperationAborted) return;
                Error(new SocketException((int)SocketError));
                StartAccept();
                return;
            }

            Socket client = AcceptSocket;
            StartAccept();
            client.NoDelay = true;

            /*
             * 使用新线程处理新的连接请求
             * 使用IOCP可一定程度上提升性能
             */
            ThreadPool.UnsafeQueueUserWorkItem(state =>
            {
                NewClient(state as Socket);
            }, client);
        }

        /// <summary>
        /// 接收到新客户端时调用,需要在子类实现其他业务逻辑
        /// </summary>
        /// <param name="client">客户端Socket</param>
        protected abstract void NewClient(Socket client);
    }
}

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
最近有项目要做一个高性能网络服务器,去网络上搜到到的都是C++版本而且是英文或者简单的DEMO,所以自己动手写了C# 的DEMO。 网络上只写接收到的数据,没有说怎么处理缓冲区数据,本DEMO简单的介绍如何处理接收到的数据。简单易用,希望对大家有用. 1、在C#中,不用去面对完成端口的操作系统内核对象,Microsoft已经为我们提供了SocketAsyncEventArgs类,它封装了IOCP的使用。请参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socketasynceventargs.aspx?cs-save-lang=1&cs-lang=cpp#code-snippet-1。 2、我的SocketAsyncEventArgsPool类使用List对象来存储对客户端来通信的SocketAsyncEventArgs对象,它相当于直接使用内核对象时的IoContext。我这样设计比用堆栈来实现的好处理是,我可以在SocketAsyncEventArgsPool池中找到任何一个与服务器连接的客户,主动向它发信息。而用堆栈来实现的话,要主动给客户发信息,则还要设计一个结构来存储已连接上服务器的客户。 3、对每一个客户端不管还发送还是接收,我使用同一个SocketAsyncEventArgs对象,对每一个客户端来说,通信是同步进行的,也就是说服务器高度保证同一个客户连接上要么在投递发送请求,并等待;或者是在投递接收请求,等待中。本例只做echo服务器,还未考虑由服务器主动向客户发送信息。 4、SocketAsyncEventArgs的UserToken被直接设定为被接受的客户端Socket。 5、没有使用BufferManager 类,因为我在初始化时给每一个SocketAsyncEventArgsPool中的对象分配一个缓冲区,发送时使用Arrary.Copy来进行字符拷贝,不去改变缓冲区的位置,只改变使用的长度,因此在下次投递接收请求时恢复缓冲区长度就可以了!如果要主动给客户发信息的话,可以new一个SocketAsyncEventArgs对象,或者在初始化中建立几个来专门用于主动发送信息,因为这种需求一般是进行信息群发,建立一个对象可以用于很多次信息发送,总体来看,这种花销不大,还减去了字符拷贝和消耗。 6、测试结果:(在我的笔记本上时行的,我的本本是T420 I7 8G内存) 100客户 100,000(十万次)不间断的发送接收数据(发送和接收之间没有Sleep,就一个一循环,不断的发送与接收) 耗时3004.6325 秒完成 总共 10,000,000 一千万次访问 平均每分完成 199,691.6 次发送与接收 平均每秒完成 3,328.2 次发送与接收 整个运行过程中,内存消耗在开始两三分种后就保持稳定不再增涨。 看了一下对每个客户端的延迟最多不超过2秒。
C# OPC UA 服务端一个用于构建基于 OPC UA 标准的服务端应用程序的开发框架。通过使用 C# 和 OPC Foundation 提供的 OPC UA 标准库,你可以创建一个支持 OPC UA 协议的服务端。 要创建一个 C# OPC UA 服务端,你可以按照以下步骤进行操作: 1. 安装 OPC Foundation 提供的 OPC UA .NET 标准库。你可以从 OPC Foundation 的官方网站下载并安装这个库。 2. 在你的 C# 项目中添加对 OPC UA .NET 标准库的引用。你可以在 Visual Studio 中右键点击项目,选择“添加引用”,然后选择 OPC UA .NET 标准库。 3. 创建一个 OPC UA 服务器对象,并配置服务器的设置。你可以设置服务器的名称、描述、地址等参数,以及添加需要暴露的节点和变量。 4. 在服务器对象中注册需要提供的节点和变量。你可以创建自定义的节点类型,并为每个节点设置相应的属性和值。 5. 启动服务器并监听客户端的连接请求。你可以使用服务器对象的方法来启动服务器,并监听客户端的连接。 6. 处理客户端的请求和数据读写操作。通过事件和回调函数,你可以处理客户端的请求,例如读取节点的值、写入节点的值等。 7. 实现安全性和权限控制。通过 OPC UA 的安全机制,你可以实现对客户端的身份验证、加密通信和权限控制。 通过这些步骤,你可以创建一个 C# OPC UA 服务端,并提供基于 OPC UA 的数据访问和通信功能。在开发过程中,你可以参考 OPC Foundation 提供的文档和示例代码,以及使用 C# 的相关编程知识和技术来实现你的需求。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Anlige

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值