Netty Socket demo

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();
        //新建一个bootStrap的核心类
        ServerBootstrap b = new ServerBootstrap();
        //设置当前nettyChannle的类型
        b.channel(NioServerSocketChannel.class)
                //设置channelPipeline类
                .childHandler(new Init());
        //社会boss和work的线程组
        b.group(bossGroup, workGroup)
                //绑定监听端口,即对外开放的socket端口
                .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);
        /*
        匿名类中只有单一的方法需要实现时才能使用lambda表达式
        new Timer().schedule(()->{
            if (connet != null && connet.isActive()) {
                connet.writeAndFlush("fasdf");
            }
        },0,1000L);*/

        //测试任务 发送字符串 因为有LineBasedFrameDecoder 所以发送字符串需要换行
        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);
           // ctx.fireChannelRead(no);
        }
    }

    /**
     *  操作类
     */
    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();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值