Netty 是一个利用 Java 的高级网络的能力,隐藏其背后的复杂性而提供一个易于使用的 API 的高性能客户端/服务器 通讯框架。

Netty的优势:

  1. 并发高
  2. 传输快
  3. 封装好

还有一个叫做 内存零拷贝技术。

整体架构大致如下图:

DotNetty 高性能NIO通讯模型 服务端和客户端案例版_.netcore

从架构图来看,NIO非阻塞模型的连接数要多许多。

DotNetty

然而,我是偏向.Net技术栈的,所以,我采用DotNetty来使用Netty技术实现通讯的高性能,我也抽时间对里面的细节做了研究,但是,针对于内存零拷贝技术(DMA 内存直接访问),Windows平台好像没有相应的接口,所以,也不了了之,有知道的大佬,也可以告诉我,不吝感激。

实际项目

分为服务端和客户端

服务端 Server

先安装Nuget包

Install-Package DotNetty.Codecs  -Version 0.7.2
Install-Package DotNetty.Transport -Version 0.7.2

然后,实现服务端代码,用第三方就是方便,几行代码就搞定了。

NettyServer.cs

/// <summary>
/// 服务
/// </summary>
public class NettyServer
{
public async Task Listen(int port)
{
var boos = new MultithreadEventLoopGroup(1);
var work = new MultithreadEventLoopGroup();

var bootstrap = new ServerBootstrap();
bootstrap.Group(boos, work)
.Channel<TcpServerSocketChannel>()
.Option(ChannelOption.SoBacklog, 100)
.ChildHandler(new ActionChannelInitializer<ISocketChannel>(channel => {
IChannelPipeline pipeline = channel.Pipeline;

pipeline.AddLast(new ServerMessageHandler());
}));
IChannel boundChannel = await bootstrap.BindAsync(port);
}
}

ServerMessageHandler.cs

实现具体的业务

public class ServerMessageHandler : ChannelHandlerAdapter
{
public override void ChannelRead(IChannelHandlerContext context, object message)
{
var buffer = message as IByteBuffer;
if (buffer != null)
{
string receiveData = buffer.ToString(Encoding.UTF8);
Console.WriteLine("服务端获取到:" + receiveData);
byte[] messageBytes = Encoding.UTF8.GetBytes(receiveData);
IByteBuffer byteBuffer = Unpooled.Buffer(256);
byteBuffer.WriteBytes(messageBytes);
context.WriteAndFlushAsync(byteBuffer);
}
}
public override void ChannelReadComplete(IChannelHandlerContext context) => context.Flush();

public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)
{
context.CloseAsync();
}
}

测试运行代码

static void Main(string[] args)
{
var server = new NettyServer();
server.Listen(9999).Wait();
Console.WriteLine("服务启动");
Console.ReadLine();
}

客户端实现 Client

来先安装Nuget包

Install-Package DotNetty.Codecs  -Version 0.7.2
Install-Package DotNetty.Handlers -Version 0.7.2
Install-Package DotNetty.Transport -Version 0.7.2

客户端实现 NettyClinet.cs

/// <summary>
/// 客户端
/// </summary>
public class NettyClinet
{
public static IChannel Channel { set; get; }
public static IEventLoopGroup Group { set; get; }
public static ClientMessageHandler clientMessageHandler = new ClientMessageHandler();
public async void Connect(string host, int port)
{
Group = new MultithreadEventLoopGroup();

var bootstrap = new Bootstrap();
bootstrap
.Group(Group)
.Channel<TcpSocketChannel>()
.Option(ChannelOption.TcpNodelay, true)
.Handler(new ActionChannelInitializer<ISocketChannel>(channel =>
{
IChannelPipeline pipeline = channel.Pipeline;
pipeline.AddLast(clientMessageHandler);
}));
Channel = await bootstrap.ConnectAsync(new IPEndPoint(IPAddress.Parse(host), port));
}
public async void Close()
{
await Channel.CloseAsync();
await Group.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1));
}
/// <summary>
/// 发送消息
/// </summary>
/// <param name="message"></param>
public bool SendMessage(string message)
{
if (message == null)
{
return false;
}
IByteBuffer buffer = Unpooled.Buffer(256);

byte[] messageBytes = Encoding.UTF8.GetBytes(message);
buffer.WriteBytes(messageBytes);
Channel.WriteAndFlushAsync(buffer);

return true;
}
}

ClientMessageHandler.cs

实现客户端读取数据的业务

public class ClientMessageHandler : ChannelHandlerAdapter
{
public override void ChannelRead(IChannelHandlerContext context, object message)
{
var byteBuffer = message as IByteBuffer;

if (byteBuffer != null)
{
var data = byteBuffer.ToString(Encoding.UTF8);
Console.WriteLine("客户端收到:" + data);
}
}

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

public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)
{
Console.WriteLine("Exception: " + exception);
context.CloseAsync();
}
} public class ClientMessageHandler : ChannelHandlerAdapter
{
public override void ChannelRead(IChannelHandlerContext context, object message)
{
var byteBuffer = message as IByteBuffer;

if (byteBuffer != null)
{
var data = byteBuffer.ToString(Encoding.UTF8);
Console.WriteLine("客户端收到:" + data);
}
}

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

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

客户端测试

static void Main(string[] args)
{
var clinet = new NettyClinet();
clinet.Connect("127.0.0.1", 9999);
Console.WriteLine("客户端开始连接!");
string content = "";
while (!(content = Console.ReadLine()).Contains("Exit", StringComparison.InvariantCultureIgnoreCase))
{
clinet.SendMessage(content);
}
Console.ReadLine();
}

至此,已经把Netty的服务端与客户端写完了。
来看下效果

Netty 通讯效果

先启动服务端,然后,再启动客户端

我们看下效果

效果还是很明显的

DotNetty 高性能NIO通讯模型 服务端和客户端案例版_Netty_02

总结

基于Netty高性能的通讯框架搭建好了,也测试了。至此,已经对大多数通讯有了大致的了解,更加深入的知识点,还需要看文档,看源码,以及有针对性的解决了。

代码地址

​https://github.com/kesshei/NettyDemo.git​

​https://gitee.com/kesshei/NettyDemo.git​