同系列文章:
源代码:
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; } }