nettyDemo
- 这一篇是对于使用netty的最小应用的代码
- 在代码核心类都添加了 一些我的理解,以及对ChannelHandler的一些理解
- 当然,有些可能片面 有些可能不很精准,后续可能会有详细的netty架构分析
/**
* @program: quartzLearn
* @description:
* @author: zyc
* @create: 2018-08-02 17:57
**/
package com.zjpavt.test.needtodo;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.internal.ConcurrentSet;
import java.nio.charset.Charset;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
public class NettyServerTest {
public static final String ENCODE = "utf-8";
private static AtomicInteger connectNum;
private static ConcurrentSet<Channel> connectSet = new ConcurrentSet<>();
private static Channel connect;
public static void main(String[] args) {
NettyServerTest nettyServerTest = new NettyServerTest();
nettyServerTest.startServer();
}
private void startServer() {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.channel(NioServerSocketChannel.class)
.childHandler(new Init());
b.group(bossGroup, workGroup)
.bind(7897);
}
/**
* 我理解为:该类配置了当前socket服务器所需要经过的处理的流程
* 每个实现了channleHandler的类,都会对上一层传输过来的数据的有一个处理
*
*
* 在以下的例子中
* 目前 我所深入到的处理流程中是从这一步开始的,初始的数据类型为ByteBuf
*
* 我们先通过LineBasedFrameDecoder类 传输过来的Byte数据
* 做粘包和拆包处理 通过是否为\n作为单条数据的判断依据
* byteBuf --> ByteBuf
*
* 然后通过StringDecoder 将拆分好的ByteBuf数据转码为String 编码可设置
* ByteBuf --> String
*
* TcpHandler是我们自定义的具体实现类 我们可以其中实现我们的代码逻辑 可以看到我们继承了
* SimpleChannelInboundHandler 这是一个简单的输入处理类。范型使用String,因为上层
* 的StringDecoder已经将 SocketChannel内的字节ByteByf转化为了String。
* 我们在tcpHandler中处理接收到的字符串。
*
* StringEncoder 继承自 ChannelOutboundHandlerAdapter 属于输出处理类。
* 在数据输入阶段不起作用,当我们在程序中调用ChannelHandlerContext.write()时
* 会将我们的数据转化为ByteBuf 缓存字节内容到内存 然后通过Socket进行输出
* objetc --> byteBuf
*
*/
private class Init extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringDecoder(Charset.forName(ENCODE)));
pipeline.addLast(new TcpHandler());
pipeline.addLast(new StringEncoder(Charset.forName(ENCODE)));
}
}
/**
*
* 这里只使用了三个基本事件
* 分别是 建立连接 连接断开 和 接受到数据
* 数据输出只需要使用我们与Handler绑定的ChannelHandlerContext 调用writeAndFlush方法
*
* 然后我们的context会调用channel 上注册的 OutBoundhandler 的实现类。
*
*/
private class TcpHandler extends SimpleChannelInboundHandler<String> {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
connect = ctx.channel();
System.out.println(connectNum.incrementAndGet() + "新建连接");
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
connect = null;
System.out.println(connectNum.decrementAndGet());
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println(msg);
connect.writeAndFlush(msg + "\n");
}
}
}
/**
* @program: quartzLearn
* @description:
* @author: zyc
* @create: 2018-08-07 10:32
**/
package com.zjpavt.test.needtodo;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import java.nio.charset.Charset;
import java.util.Timer;
import java.util.TimerTask;
public class NettyClientTest {
public static final String HOST = "127.0.0.1";
public static final int PORT = 7897;
public static final String ENCODE = "utf-8";
private static Channel connet;
public static void main(String[] args) {
NettyClientTest nettyClientTest = new NettyClientTest();
nettyClientTest.startClient();
}
/**
*/
private void startClient() {
EventLoopGroup workGroup = new NioEventLoopGroup();
Bootstrap b = new Bootstrap();
b.group(workGroup);
b.channel(NioSocketChannel.class);
b.handler(new InitChannel());
b.connect(HOST, PORT);
new Timer().schedule(
new TimerTask() {
@Override
public void run() {
if (connet != null && connet.isActive()) {
connet.writeAndFlush("fasdf\n");
}
}
}
,0,1000L);
}
/**
* 参见服务端 这里主要增加了 TcpClientHandler NewObjectHandler 两个CHannelHandler类
*
* 我在TcpClientHandler的测试方法中使用了ctx.fireChannelRead(no);
* 将封装好的NewObject对象传给NewObjectHandler
* 在pipeline中注册的ChannelHandler 以链表的方式逐个执行
*
* 具体实现方式:
* Handler对象绑定了一个ChannelHandlerContext, 通过context.fireChannelRead
* 调用下一个context包装的Handler的ChannelRead方法
*
* 此过程中 需要判断ChannelHandler是否为Inbound 或者 outBound
* 出站的数据处理类和入栈的数据处理类都绑定在了一个Pipeline上
*
*/
private class InitChannel extends ChannelInitializer<Channel>{
@Override
protected void initChannel(Channel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringDecoder(Charset.forName(ENCODE)));
pipeline.addLast(new TcpClientHandler());
pipeline.addLast(new NewObjectHandler());
pipeline.addLast(new StringEncoder(Charset.forName(ENCODE)));
}
}
private class NewObject{
String mag ;
int code;
public NewObject(String mag, int code) {
this.mag = mag;
this.code = code;
}
public String getMag() {
return mag;
}
public void setMag(String mag) {
this.mag = mag;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
}
/**
* 操作类
*/
private class TcpClientHandler extends SimpleChannelInboundHandler<String>{
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
connet = ctx.channel();
super.channelActive(ctx);
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println(msg);
NewObject no = new NewObject(msg, 1);
}
}
/**
* 操作类
*/
private class NewObjectHandler extends SimpleChannelInboundHandler<NewObject> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, NewObject msg) throws Exception {
System.out.println(msg.code + " " + msg.mag);
ctx.fireChannelRead(msg.mag);
}
}
}
package com.zjpavt.test;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.concurrent.Executors;
/** 通过java Nio 提供的socket方法来测试*/
public class SocketDemo {
private static final String HOST = "127.0.0.1";
private static final int PORT = 7897;
public static void main(String[] args){
//socketServerInit();
socketClientSendMessage("messageevent:1\r\t\n1\n");
}
static void socketServerInit(){
Executors.newSingleThreadExecutor().execute(() -> {
try {
ServerSocket ss = null;
ss = new ServerSocket(PORT);
//sendMessage(ss);
//getMessage(ss);
System.out.println("finish");
} catch (IOException e) {
e.printStackTrace();
}
});
}
static void getMessage(ServerSocket ss) throws IOException {
Socket socket = ss.accept();
InputStream is = socket.getInputStream();
byte[] buffer = new byte[200];
int length = 0;
while (-1 != (length = is.read(buffer, 0, buffer.length)))
{
String str = new String(buffer, 0, length);
System.out.println("welcome "+str);
System.out.println("1");
}
getMessage(ss);
SocketChannel sc = socket.getChannel();
ByteBuffer buffer1 = ByteBuffer.allocate(1024);
sc.read(buffer1);
while (sc.read(buffer1) != -1) {
buffer1.flip();
byte[] bytes = buffer1.array();
buffer1.clear();
}
}
static void socketClientSendMessage(String msg){
try {
Socket socket = new Socket(InetAddress.getByName(HOST),PORT);
OutputStream os = socket.getOutputStream();
os.write(msg.getBytes());
//os.close();//客户端使用完流之后记得要关闭!!
InputStream inputStream = socket.getInputStream();
int data;
while ((data = inputStream.read()) != -1) {
System.out.println(((char) data ) + " " + data);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}