C#:SM4加密通信在DotNetty上的实现演示(服务器端)

同系列文章:

演示程序界面:

源代码:
NettyTcpServerForm.cs

/* ----------------------------------------------------------
 * 文件名称:NettyTcpServerForm.cs
 * 
 * 作者:秦建辉
 * 
 * 微信:splashcn
 * 
 * 博客:http://www.firstsolver.com/wordpress/
 * 
 * 开发环境:
 *      Visual Studio 2017
 *      .NET Framework 4.5.2
 *      DotNetty 0.4.5
 *      
 * 版本历史:
 *      V1.1    2017年07月17日
 *              完善代码
 *              
 *      V1.0    2017年07月07日
 *              基于DotNetty框架的Tcp连接服务器端演示
 *
 * 参考资料:
 *      https://github.com/Azure/DotNetty
------------------------------------------------------------ */
using Com.FirstSolver.Netty;
using DotNetty.Transport.Bootstrapping;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;
using System;
using System.Collections.Generic;
using System.Management;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Splash
{
    public partial class NettyTcpServerForm : Form
    {
        /// <summary>
        /// 服务器是否已运行
        /// </summary>
        private bool IsServerRunning = false;

        /// <summary>
        /// 关闭侦听器事件
        /// </summary>
        private ManualResetEvent ClosingArrivedEvent = new ManualResetEvent(false);

        public NettyTcpServerForm()
        {
            InitializeComponent();
        }

        private void buttonClear_Click(object sender, EventArgs e)
        {
            textBoxRecords.Clear();
        }

        private void buttonStart_Click(object sender, EventArgs e)
        {
            try
            {
                if (IsServerRunning)
                {
                    buttonStart.Enabled = false;
                    ClosingArrivedEvent.Set();  // 停止侦听
                }
                else
                {
                    IPAddress ServerIP = IPAddress.Parse(comboBoxServerIP.Text); // 服务器地址
                    int ServerPort = int.Parse(textBoxServerPort.Text); // 服务器端口
                    int Backlog = int.Parse(textBoxBacklog.Text); // 最大连接等待数
                    string SecretKey = textBoxSecretKey.Text; // 通信密钥

                    // 线程池任务
                    ThreadPool.QueueUserWorkItem(ThreadPoolCallback,
                        new TcpServerParams()
                        {
                            ServerIP = ServerIP,
                            ServerPort = ServerPort,
                            Backlog = Backlog,
                            SecretKey = SecretKey
                        });
                }
            }
            catch (Exception exception)
            {
                SetText("Exception: " + exception.Message);
            }
        }

        private void ThreadPoolCallback(object state)
        {
            TcpServerParams Args = state as TcpServerParams;
            RunTcpServerAsync(Args).Wait();
        }

        private async Task RunTcpServerAsync(TcpServerParams args)
        {
            MultithreadEventLoopGroup bossGroup = new MultithreadEventLoopGroup(1);
            MultithreadEventLoopGroup workerGroup = new MultithreadEventLoopGroup();
            try
            {
                ServerBootstrap bootstrap = new ServerBootstrap();
                bootstrap
                    .Group(bossGroup, workerGroup)
                    .Channel<TcpServerSocketChannel>()
                    .Option(ChannelOption.SoBacklog, args.Backlog)
                    .ChildHandler(new ActionChannelInitializer<ISocketChannel>(channel =>
                    {
                        IChannelPipeline pipeline = channel.Pipeline;
                        pipeline.AddLast(new SM4TextToMessageCodec());      // SM4文本编解码器
                        pipeline.AddLast(new TcpServerHandler(this, args)); // 消息处理器
                    }));

                IChannel boundChannel = await bootstrap.BindAsync(args.ServerIP, args.ServerPort);

                ClosingArrivedEvent.Reset();
                SetStatus(true);
                ClosingArrivedEvent.WaitOne();
                await boundChannel.CloseAsync();
            }
            catch (Exception exception)
            {
                SetText("Exception: " + exception.Message);
            }
            finally
            {
                await Task.WhenAll(
                    bossGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1)),
                    workerGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1)));

                SetStatus(false);
            }
        }

        public void SetText(string text)
        {
            this.BeginInvoke(new Action<string>((msg) =>
            {
                this.textBoxRecords.AppendText(msg + "\r\n");
            }), text);
        }

        public void SetStatus(bool isRunning)
        {
            this.BeginInvoke(new Action<bool>((status) =>
            {
                if (status)
                {
                    IsServerRunning = true;
                    buttonStart.Text = "停止侦听";
                }
                else
                {
                    IsServerRunning = false;
                    buttonStart.Text = "开启侦听";
                    buttonStart.Enabled = true;
                }
            }), isRunning);
        }

        private void NettyTcpServerForm_Load(object sender, EventArgs e)
        {
            // 列举本机所有的有效IP地址(自动过滤掉虚拟网卡IP地址)
            string[] IPCollection = GetLocalIPv4Address();
            if (IPCollection != null)
            {
                comboBoxServerIP.DataSource = IPCollection;
                comboBoxServerIP.Enabled = !(IPCollection.Length == 1);  // 如果本机只有一个有效IP地址,则直接锁定
            }
        }

        private void NettyTcpServerForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            ClosingArrivedEvent.Set();  // 停止侦听
        }

        /// <summary>
        /// 获取本机IP地址列表
        /// </summary>
        /// <param name="isIncludeUsb">是否包含USB网卡,默认为不包含</param>
        /// <returns>本机真实网卡信息</returns>
        public static string[] GetLocalIPv4Address(bool isIncludeUsb = false)
        {   // IPv4正则表达式
            const string IPv4RegularExpression = "^(?:(?:25[0-5]|2[0-4]\\d|((1\\d{2})|([1-9]?\\d)))\\.){3}(?:25[0-5]|2[0-4]\\d|((1\\d{2})|([1-9]?\\d)))$";

            // 注意:只获取已连接的网卡
            string NetworkAdapterQueryString;
            if (isIncludeUsb)
                NetworkAdapterQueryString = "SELECT * FROM Win32_NetworkAdapter WHERE (NetConnectionStatus = 2) AND (MACAddress IS NOT NULL) AND (NOT (PNPDeviceID LIKE 'ROOT%'))";
            else
                NetworkAdapterQueryString = "SELECT * FROM Win32_NetworkAdapter WHERE (NetConnectionStatus = 2) AND (MACAddress IS NOT NULL) AND (NOT (PNPDeviceID LIKE 'ROOT%')) AND (NOT (PNPDeviceID LIKE 'USB%'))";

            ManagementObjectCollection NetworkAdapterQueryCollection = new ManagementObjectSearcher(NetworkAdapterQueryString).Get();
            if (NetworkAdapterQueryCollection == null) return null;

            List<string> IPv4AddressList = new List<string>(NetworkAdapterQueryCollection.Count);
            foreach (ManagementObject mo in NetworkAdapterQueryCollection)
            {
                // 获取网卡配置信息
                string ConfigurationQueryString = "SELECT * FROM Win32_NetworkAdapterConfiguration WHERE Index = " + mo["Index"];
                ManagementObjectCollection ConfigurationQueryCollection = new ManagementObjectSearcher(ConfigurationQueryString).Get();
                if (ConfigurationQueryCollection == null) continue;

                foreach (ManagementObject nacmo in ConfigurationQueryCollection)
                {
                    if ((bool)nacmo["IPEnabled"])
                    {
                        string[] IPCollection = nacmo["IPAddress"] as string[]; // IP地址
                        if (IPCollection != null)
                        {
                            foreach (string adress in IPCollection)
                            {
                                Match match = Regex.Match(adress, IPv4RegularExpression);
                                if (match.Success) { IPv4AddressList.Add(adress); break; }
                            }
                        }
                    }
                }
            }

            if (IPv4AddressList.Count > 0) return IPv4AddressList.ToArray(); else return null;
        }
    }
}

TcpServerHandler.cs

using Com.FirstSolver.Netty;
using DotNetty.Transport.Channels;
using System;

namespace Splash
{
    public class TcpServerHandler : SimpleChannelInboundHandler<string>
    {
        /// <summary>
        /// 消息交互窗口
        /// </summary>
        private readonly NettyTcpServerForm Owner;

        /// <summary>
        /// 相关参数
        /// </summary>
        private readonly TcpServerParams Args;

        public TcpServerHandler(NettyTcpServerForm owner, TcpServerParams args)
        {
            Owner = owner;
            Args = args;
        }

        public override void ChannelActive(IChannelHandlerContext context)
        {
            SM4TextToMessageCodec.SetSecretKey(context, Args.SecretKey);
        }

        protected override void ChannelRead0(IChannelHandlerContext context, string message)
        {
            Owner.SetText(message);

            // 服务器回应,显示接收时间
            context.WriteAndFlushAsync("Return(result=\"success\" time=\"" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "\")");
        }

        public override void ChannelReadComplete(IChannelHandlerContext context) => context.Flush();

        public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)
        {
            Owner.SetText("Exception: " + exception);
            context.CloseAsync();
        }
    }
}

TcpServerParams.cs

using System.Net;

namespace Splash
{
    public class TcpServerParams
    {
        /// <summary>
        /// 接收服务器地址
        /// </summary>
        public IPAddress ServerIP;

        /// <summary>
        /// 接收服务器端口
        /// </summary>
        public int ServerPort;

        /// <summary>
        /// 最大连接等待数
        /// </summary>
        public int Backlog;

        /// <summary>
        /// 通信密钥
        /// </summary>
        public string SecretKey;
    }
}

Comments are closed.