C#:SM4加密通信在DotNetty上的实现演示(客户端)

同系列文章:

演示程序界面:

源代码:
NettyTcpClientForm.cs

/* ----------------------------------------------------------
 * 文件名称:NettyTcpClientForm.cs
 * 
 * 作者:秦建辉
 * 
 * 微信:splashcn
 * 
 * 博客:http://www.firstsolver.com/wordpress/
 * 
 * 开发环境:
 *      Visual Studio 2017
 *      .NET Framework 4.5.2
 *      DotNetty 0.4.5
 *      
 * 版本历史:
 *      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.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Splash
{
    public partial class NettyTcpClientForm : Form
    {
        public NettyTcpClientForm()
        {
            InitializeComponent();
        }

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

        private void buttonExecute_Click(object sender, EventArgs e)
        {
            try
            {
                string Command = textBoxCommand.Text;
                if (string.IsNullOrEmpty(Command)) return;

                IPAddress ServerIP = IPAddress.Parse(textBoxServerIP.Text); // 服务器地址
                int ServerPort = int.Parse(textBoxServerPort.Text); // 服务器端口
                string SecretKey = textBoxSecretKey.Text;   // 通信密钥
                int ConnectTimeout = int.Parse(textBoxConnectTimeout.Text); // 连接等待时间
                int ReplyTimeout = int.Parse(textBoxReplyTimeout.Text);   // 回复等待时间

                // 线程池任务
                ThreadPool.QueueUserWorkItem(ThreadPoolCallback,
                    new TcpClientParams()
                    {
                        ServerIP = ServerIP,
                        ServerPort = ServerPort,
                        SecretKey = SecretKey,
                        ConnectTimeout = ConnectTimeout,
                        ReplyTimeout = ReplyTimeout,
                        ReceiveCompletedEvent = new ManualResetEvent(false),
                        Command = Command
                    });
            }
            catch (Exception exception)
            {
                SetText("Exception: " + exception.Message);
            }
        }

        private void ThreadPoolCallback(object state)
        {
            TcpClientParams Args = state as TcpClientParams;
            RunTcpClientAsync(Args).Wait();
        }

        private async Task RunTcpClientAsync(TcpClientParams args)
        {
            MultithreadEventLoopGroup workerGroup = new MultithreadEventLoopGroup();
            try
            {
                Bootstrap bootstrap = new Bootstrap();
                bootstrap.Group(workerGroup)
                    .Channel<TcpSocketChannel>()
                    .Option(ChannelOption.TcpNodelay, true)
                    .Option(ChannelOption.ConnectTimeout, new TimeSpan(0, 0, 0, 0, args.ConnectTimeout))
                    .Handler(new ActionChannelInitializer<ISocketChannel>(channel =>
                    {
                        IChannelPipeline pipeline = channel.Pipeline;
                        pipeline.AddLast(new SM4TextToMessageCodec());      // SM4文本编解码器
                        pipeline.AddLast(new TcpClientHandler(this, args)); // 消息处理器
                    }));

                // 建立连接
                IChannel bootstrapChannel = await bootstrap.ConnectAsync(new IPEndPoint(args.ServerIP, args.ServerPort));

                // 发送命令
                await bootstrapChannel.WriteAndFlushAsync(args.Command);

                // 等待回复
                if (!args.ReceiveCompletedEvent.WaitOne(args.ReplyTimeout, true))
                {
                    SetText("Reply Timeout!");
                }

                // 断开连接
                await bootstrapChannel.CloseAsync();
            }
            catch(Exception exception)
            {
                SetText("Exception: " + exception.Message);
            }
            finally
            {
                await workerGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1));
            }
        }

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

TcpClientHandler.cs

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

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

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

        public TcpClientHandler(NettyTcpClientForm owner, TcpClientParams 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);
            Args.ReceiveCompletedEvent.Set();
        }

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

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

TcpClientParams.cs

using System.Net;
using System.Threading;

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

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

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

        /// <summary>
        /// 连接超时等待时间
        /// </summary>
        public int ConnectTimeout;

        /// <summary>
        /// 回复等待时间(毫秒)
        /// </summary>
        public int ReplyTimeout = -1;

        /// <summary>
        /// 回复数据接收完成事件
        /// </summary>
        public ManualResetEvent ReceiveCompletedEvent;

        /// <summary>
        /// 命令字符串
        /// </summary>
        public string Command;
    }
}

Comments are closed.