- Netty 系列之 Netty 高性能之道:https://www.infoq.cn/article/netty-high-performance
- Netty 系列之 Netty 线程模型:https://www.infoq.cn/article/netty-threading-model
Netty 和 RPC 框架线程模型分析:https://www.infoq.cn/article/9Ib3hbKSgQaALj02-90y
- Netty源码解析-概述篇:https://segmentfault.com/a/1190000018248509
- 源码之下无秘密 ── 做最好的 Netty 源码分析教程
新手入门:目前为止最透彻的的Netty高性能原理和框架架构解析:https://www.jianshu.com/p/f16698aa8be2?utm_source=oschina-app
NIO框架入门(一):服务端基于Netty4的UDP双向通信Demo演示 [附件下载]:http://www.52im.net/forum.php?mod=viewthread&tid=367&highlight=netty
Netty简单使用
原文链接:Netty——简单创建服务器、客户端通讯:https://www.cnblogs.com/lemon-flm/p/7813484.html
类HelloWorldServer
import java.net.InetAddress;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
/**
* Netty4 服务端代码
*
*/
public class HelloWorldServer {
public static void main(String[] args) {
// EventLoop 代替原来的 ChannelFactory
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap(); // 创建 一个netty 服务器
// server端采用简洁的连写方式,client端才用分段普通写法。
serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) // 指定channel[通道]类型
.childHandler(new ChannelInitializer < SocketChannel > () { // 指定Handler [操纵者]
@Override
public void initChannel(SocketChannel ch) throws Exception {
// 以("\0")为结尾分割的 解码器
ch.pipeline().addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.nulDelimiter()));
// 字符串 解码 和 编码 默认的 StringDecoder 字符串形式输出
ch.pipeline().addLast("decoder", new StringDecoder());
ch.pipeline().addLast("encoder", new StringEncoder());
ch.pipeline().addLast(new HelloServerHandler()); // 添加自己的对 上传数据的处理
}
}).option(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture f = serverBootstrap.bind(8000).sync(); // 绑定 8000 端口
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
} finally {
workerGroup.shutdownGracefully(); // 销毁 netty
bossGroup.shutdownGracefully();
}
}
/**
* 自己对 处理数据
*
* @author flm
* 2017年11月10日
*/
private static class HelloServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// 收到消息直接打印输出
System.out.println(ctx.channel().remoteAddress() + " Say : " + msg);
// 返回客户端消息 - 我已经接收到了你的消息
ctx.writeAndFlush("server Received your message !");
}
/**
*
* 覆盖 channelActive 方法 在channel被启用的时候触发 (在建立连接的时候)
*
* channelActive 和 channelInActive 在后面的内容中讲述,这里先不做详细的描述
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("RamoteAddress : " + ctx.channel().remoteAddress() + " active !");
ctx.writeAndFlush("Welcome to " + InetAddress.getLocalHost().getHostName() + " service!\n"); //回复
super.channelActive(ctx);
}
}
}
类HelloWorldClient
import java.net.InetSocketAddress;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
/**
* Netty4 客户端代码
*
*/
public class HelloWorldClient {
public static void main(String args[]) {
// Bootstrap,且构造函数变化很大,这里用无参构造。
Bootstrap bootstrap = new Bootstrap();
// 指定channel[通道]类型
bootstrap.channel(NioSocketChannel.class);
// 指定Handler [操纵者]
bootstrap.handler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
/*
* 这个地方的 必须和服务端对应上。否则无法正常解码和编码
*
*/
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.nulDelimiter()));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
// 客户端的逻辑,自己对数据处理
pipeline.addLast(new HelloClientHandler());
}
});
// 指定EventLoopGroup [事件 组]
bootstrap.group(new NioEventLoopGroup());
//设置TCP协议的属性
bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
bootstrap.option(ChannelOption.TCP_NODELAY, true);
bootstrap.option(ChannelOption.SO_TIMEOUT, 5000);
// 连接到本地的8000端口的服务端
bootstrap.connect(new InetSocketAddress("192.168.50.177", 4545));
}
private static class HelloClientHandler extends ChannelInboundHandlerAdapter {
/*
* 监听 服务器 发送来的数据
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if("0002".equals(msg.toString().substring(4, 8))){
// 握手成功,主动发送心跳消息
} else if ("9999".equals(msg.toString().substring(4, 8))) {
String keepAlive = "00209999000 " + "\0";
Thread.sleep(10000);
ctx.writeAndFlush(keepAlive);
} else if("0061".equals(msg.toString().substring(4, 8))){
System.out.println("发送给所有客户端");
}
System.out.println("say:" + msg);
}
/*
* 启动客户端 时触发
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("Client active ");
String start = "00200001003 ";
String subscribe02 = "002000600031 02 ";
String subscribe03 = "002000600031 03 ";
ctx.writeAndFlush(start + "\0");
ctx.writeAndFlush(subscribe02 + "\0");
ctx.writeAndFlush(subscribe03 + "\0");
super.channelActive(ctx);
}
/*
* 关闭 客户端 触发
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("Client close ");
super.channelInactive(ctx);
}
}
}
写一个长连接的client
类TorqueGunClient
import java.net.InetSocketAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
@Component
public class TorqueGunClient {
private static Logger logger = LoggerFactory.getLogger(TorqueGunClient.class);
@Autowired
TorqueGunConfig torqueGunConfig;
public void start() {
logger.debug("开始,扭力枪通信");
// Bootstrap,且构造函数变化很大,这里用无参构造。
Bootstrap bootstrap = new Bootstrap();
// 指定channel[通道]类型
bootstrap.channel(NioSocketChannel.class);
// 指定Handler [操纵者]
bootstrap.handler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
/*
* 这个地方的 必须和服务端对应上。否则无法正常解码和编码
*
*/
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.nulDelimiter()));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
// 客户端的逻辑,自己对数据处理
pipeline.addLast(new TorqueGunClientHandler());
}
});
// 指定EventLoopGroup [事件组]
bootstrap.group(new NioEventLoopGroup());
// 连接到本地的8000端口的服务端
bootstrap.connect(new InetSocketAddress(torqueGunConfig.getIp(), torqueGunConfig.getPort()));
}
}
类TorqueGunClientHandler
import java.util.Map;
import java.util.Set;
import org.apache.mina.core.session.IoSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import cn.com.tcb.uavpcs.datacenter.comm.desktop.process.WorkplaceClientInvotor;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class TorqueGunClientHandler extends ChannelInboundHandlerAdapter {
private static Logger logger = LoggerFactory.getLogger(TorqueGunClientHandler.class);
private String keepAlive = "00209999000 " + "\0";
private String start = "00200001003 " + "\0";
private String subscribe02 = "002000600031 02 " + "\0";
private String subscribe03 = "002000600031 03 " + "\0";
/*
* 监听 服务器 发送来的数据
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
logger.debug("say:" + msg);
if ("0002".equals(msg.toString().substring(4, 8))) {
// 握手成功,主动发送一次心跳消息
ctx.writeAndFlush(keepAlive);
} else if ("9999".equals(msg.toString().substring(4, 8))) {
Thread.sleep(10000);
ctx.writeAndFlush(keepAlive);
} else if ("0061".equals(msg.toString().substring(4, 8))) {
logger.debug(msg.toString());
WorkplaceClientInvotor workplaceClientInvotor = new WorkplaceClientInvotor();
Map<String, IoSession> map = WorkplaceClientInvotor.STATION_SESSIONS;
Set<String> set = map.keySet();
for (String key : set) {
workplaceClientInvotor.reportTorqueData(msg.toString(), key);
}
}
}
/*
* 启动客户端 时触发
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
logger.debug("Client active ");
ctx.writeAndFlush(start);
ctx.writeAndFlush(subscribe02);
ctx.writeAndFlush(subscribe03);
super.channelActive(ctx);
}
/*
* 关闭 客户端 触发
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
logger.debug("Client close ");
super.channelInactive(ctx);
}
}
类TorqueGunConfig
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
@PropertySource(value = "classpath:define.properties")
@ConfigurationProperties(prefix = "torquegun.socketserver")
public class TorqueGunConfig {
private String ip;
private int port;
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
}