同系列文章:
演示程序界面:
源代码:
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; } }