基础总结-socket

网络编程

网络编程就是使用IP地址,或域名,和端口连接到另一台计算机上对应的程序,按照规定的协议(数据格式)来交换数据

网络模型图

image.png

Socket入门

网络通讯其实就是Sokcet间的通讯,数据在两个Sokcet间通过IO传输。

TCP与UDP在概念上的区别:

udp:
a.是面向无连接, 将数据及源的封装成数据包中,不需要建立连接
b.每个数据报的大小在限制64k内
c.因无连接,是不可靠协议
d.不需要建立连接,速度快
tcp:
a.建议连接,形成传输数据的通道.
b.在连接中进行大数据量传输,以字节流方式
c .通过三次握手完成连接,是可靠协议
d .必须建立连接m效率会稍低

TCP协议

在TCP/IP协议中,TCP协议采用三次握手建立一个连接。四次分手结束连接
简单实现socket通信:
服务器端代码:

/**
 * @author toms
 * 服务端
 * */
public class Server {

    public static void main(String[] args) throws IOException {
System.out.println("服务器启动...");
ServerSocketserverSocket = new ServerSocket(8080);
        Socket socket = serverSocket.accept();
InputStreaminputStream = socket.getInputStream();
        byte[] bytes = new byte[1024];
        int len = inputStream.read(bytes);
        String message = new String(bytes,0,len);
System.out.println("接收消息:"+message);;
System.out.println(serverSocket.);
    }
}

客户端代码

public class Client {

    public static void main(String[] args) throws IOException {
System.out.printf("客户端启动...");
        Socket socket = new Socket("127.0.0.1",8080);
        OutputStreamoutputStream = socket.getOutputStream();
        outputStream.write("hi socket server".getBytes());
        socket.close();
    }
}

在实际的系统中,通常要面对的是两个问题:
(1) 客户端同时发送多个请求到服务端
(2)服务器端则同时要接受多个连接发送的请求

.设置线程池

ExecutorService创建线程池,构造函数参数:
Corepoolsize:提交一个任务到线程池时,线程池会创建一个核心线程来执行任务,即使其他空闲的核心线程能够执行新任务也会创建新的核心线程
Maximumpoolsize:线程池允许创建的最大线程数,只有队列满了的时候,并且线程数量小于最大值,才会创建新线程
keepAliveTime:当线程池的工作线程空闲后,保持存活的时间。这里指的是核心线程池以外的线程。
TimeUnit:TimeUnit
workQueue:

  • ArrayBlockingQueue:先进先出队列,创建时指定大小, 有界;
  • LinkedBlockingQueue:使用链表实现的先进先出队列,默认大小为Integer.MAX_VALUE;
  • SynchronousQueue:不保存提交的任务, 数据也不会缓存到队列中, 用于生产者和消费者互等对方, 一起离开.
  • PriorityBlockingQueue: 支持优先级的队列
    threadFactory:指代创建线程的工厂:可以通过线程工厂给每个创建出来的线程设置更加有意义的名字。
    RejectedExecutionHandler:饱和策略,队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务,这个策略默认情况下是AbortPolicy,
    • AbortPolicy:直接抛出异常RejectedExecutionException。
    • CallerRunsPolicy:只用调用者所在线程来运行任务,即由调用 execute方法的线程执行该任务。
    • DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
    • DiscardPolicy:不处理,丢弃掉,即丢弃且不抛出异常。
    线程池的状态:
    RUNNING:运行态,可处理新任务并执行队列中的任务
    SHUTDOW:关闭态,不接受新任务,但处理队列中的任务
    STOP:停止态,不接受新任务,不处理队列中任务,且打断运行中任务
    TIDYING:整理态,所有任务已经结束,workerCount = 0 ,将执行terminated()方法
    TERMINATED:结束态,terminated() 方法已完成
    自定义线程池:
/**
 * 自定义线程池
 * */
public class ThreadPool extends ThreadPoolExecutor {

    public ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
    }

    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        super.beforeExecute(t, r);
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
    }

    @Override
    protected void terminated() {
        System.out.println("terminated");
        super.terminated();
    }
}

线程池

/**
 * 线程池
 */
public class Pool {

    ThreadPool threadPool = new ThreadPool(
            5,
            10,
            60,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue(200),
            new ThreadPoolExecutor.DiscardPolicy()
    );

    /**
     * submit方法能提供线程执行的返回值,但只有实现了Callable才会有返回值
     */
    public void submit() throws ExecutionException, InterruptedException {
        for (int i = 0; i < 10; i++) {
            Callable callable = new Callable() {
                @Override
                public Object call() throws Exception {
                    return "i am over";
                }
            };
            Future future = threadPool.submit(callable);
            System.out.println(future.get());
        }
        ;
    }

    /**
     * execute方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功;
     */
    public void execute(Runnable object) throws InterruptedException {
        System.out.println(threadPool.getTaskCount());
        Thread.sleep(4*1000);
        threadPool.execute(object);
    }

    /**
     * 关闭线程池
     */
    public void shutdown() {
        threadPool.shutdown();
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        //客户端启动
        Pool pool = new Pool();
        for (int i = 0; i < 10; i++) {
            pool.execute(new Client());
        }
    }

}

服务器端

/**
 * @author toms
 * */
public class Server{

    private Pool pool = new Pool();
    /**
     * 启动服务器
     * */
    public void init() throws IOException, InterruptedException {
        System.out.println("服务器启动...");
        ServerSocket serverSocket = new ServerSocket(8080);
        while (true){
            //为每个客户端开一个线程
            Socket socket = serverSocket.accept();
            Jieshou jieshou = new Jieshou(socket.getInputStream(),socket.getOutputStream());
            pool.execute(jieshou);
        }
    }


    /**
     * 业务类
     * */
    class Jieshou implements Runnable{


        private InputStream inputStream;
        private OutputStream outputStream;

        public Jieshou(InputStream inputStream, OutputStream outputStream){
            this.inputStream = inputStream;
            this.outputStream = outputStream;
        }
        /**
         * 业务方法
         * */
        @Override
        public void run() {
            try {
                byte[] bytes = new byte[1024];
                int len = inputStream.read(bytes);
                String message = new String(bytes,0,len);
                System.out.println(message);
                outputStream.write("i get you".getBytes());
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                try {
                    inputStream.close();
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        public InputStream getInputStream() {
            return inputStream;
        }

        public void setInputStream(InputStream inputStream) {
            this.inputStream = inputStream;
        }
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        //服务器启动
        Server server = new Server();
        server.init();
    }

}

客户端

/**
 * @author toms
 * */
public class Client implements Runnable{
    @Override
    public void run() {
        try {
            Socket socket = new Socket("127.0.0.1",8080);
            OutputStream outputStream = socket.getOutputStream();
            outputStream.write("hi socket server".getBytes());
            InputStream inputStream = socket.getInputStream();
            byte[] bytes = new byte[1024];
            int len = inputStream.read(bytes);
            String message = new String(bytes,0,len);
            System.out.println(message);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {

        }
    }
}

future:Future是并发编程中的一种设计模式,对于多线程来说,线程A需要等待线程B的结果,它没必要一直等待B,可以先拿到一个未来的Future,等B有了结果后再取真实的结果。

ExecutorService executor = Executors.newSingleThreadExecutor();
Future future = executor.submit(callable);    //主线程需要callable线程的结果,先拿到一个未来的Future
System.out.println(future.get());    //有了结果后再根据get方法取真实的结果,当然如果此时callable线程如果没有执行完get方法会阻塞执行完,如果执行完则直接返回结果或抛出异常

关闭线程池:

原理:遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。
howdownNow首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表
showdown只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。

线程池监控

① taskCount:线程池需要执行的任务数量
executor.getTaskCount();
② completedTaskCount:线程池已完成的任务数量,小于等于taskCount
executor.getCompletedTaskCount();
③ largestPoolSize:线程池曾经创建过的最大线程数量。
executor.getLargestPoolSize();
④ getPoolSize:线程池的线程数量。
executor.getPoolSize();
⑤ getActiveCount:获取活动的线程数。
executor.getActiveCount()

NIO

java NIO(non-blocking 非阻塞IO)
学习新技术先学新名词,

  • 缓冲区
  • 通道
  • 选择器

buffer(缓冲区)

bytebuffer / charbuffer / shortbuffer /intbuffer /longbuffer /floatbuffer /doublebuffer /
api:
allocate(): 获取缓冲区
put():存入缓冲区
get(): 获取数据
flip(): 开启读模式
核心属性:
capacity:最大容量 ,一旦声明不可改变
limit: 第一个不可被读取的索引,limit后面的不可读
position : 数据正在操作的位置
mark / reset : 通过mark标记索引的position .可以使用reset恢复到这个位置

/**
 * @author toms
 * 缓冲区
 * */
public class Buffer {

    public static void main(String[] args) {
        //获取缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);
       /* System.out.println(buffer.limit());
        System.out.println(buffer.capacity());
        System.out.println(buffer.position()+"----------");*/
        //存放数据
        buffer.put("shazan".getBytes());
        /*System.out.println(buffer.limit());
        System.out.println(buffer.capacity());
        System.out.println(buffer.position()+"----------");*/
        //开启读模式
        buffer.flip();
        byte[] bytes1 = new byte[buffer.limit()];
        buffer.get(bytes1);
        System.out.println(new String(bytes1,0,bytes1.length));
        System.out.println(buffer.capacity());
        System.out.println(buffer.position()+"----------");
        //重复读
        /*buffer.rewind();
        byte[] bytes2 = new byte[buffer.limit()];
        buffer.get(bytes2);
        System.out.println(new String(bytes2,0,bytes2.length));
        System.out.println(buffer.capacity());
        System.out.println(buffer.position()+"----------");*/
        //清空缓冲区,还能读到
       /* buffer.clear();
        byte[] bytes = new byte[buffer.limit()];
        buffer.get(bytes);
        System.out.println(new String(bytes,0,bytes.length));
        System.out.println(buffer.capacity());
        System.out.println(buffer.position()+"----------");;*/
        //标记
        buffer.rewind();
        byte[] bytes3 = new byte[buffer.limit()];
        buffer.get(bytes3,0,2);
        System.out.println(new String(bytes3,0,bytes3.length));
        System.out.println(buffer.position());
        buffer.get(bytes3,2,2);
        System.out.println(new String(bytes3,0,bytes3.length));
        System.out.println(buffer.position());
        System.out.println(buffer.capacity());
        System.out.println(buffer.position()+"----------");
    }
}

channel:不能直接访问数据,只能来buffer交互
filechannel / socketChannel /serverSocketchannel /datagramchannel
api:
getchannel:获取通道

/**
 * 文件通道
 */
public class MyChannel {

    private Pool pool = new Pool();

    /**
     * 启动服务器
     */
    public void init() throws IOException, InterruptedException {
        System.out.println("NIO服务器启动,绑定8090端口...");
        ServerSocketChannel serverSocket = ServerSocketChannel.open();
        serverSocket.socket().bind(new InetSocketAddress(8080));
        //非阻塞
        serverSocket.configureBlocking(false);
        //注册选择器
        Selector selector = Selector.open();
        //接收请求
        serverSocket.register(selector, SelectionKey.OP_ACCEPT);
        Handle handle = new Handle(1024);
        while (true) {
            //轮询
            if (selector.select() > 0) {
                //获取待处理的选择键集合
                Iterator keyIterator = selector.selectedKeys().iterator();
                while (keyIterator.hasNext()) {
                    SelectionKey selectionKey = keyIterator.next();
                    //判断是什么请求,选择对应的处理方法
                    if (selectionKey.isAcceptable()) {
                        handle.accept(selectionKey);
                    }
                    //读请求
                    if (selectionKey.isReadable()) {
                        handle.read(selectionKey);
                        keyIterator.remove();
                    }
                }
            }
        }
    }


    /**
     * 业务类
     */
    class Handle implements Runnable {

        private int bufferSize;

        public Handle(int bufferSize) {
            this.bufferSize = bufferSize;
        }

        public void accept(SelectionKey selectionKey) throws IOException {
            //获取连接
            SocketChannel socketChannel = ((ServerSocketChannel) selectionKey.channel()).accept();
            //设置为非阻塞模式
            socketChannel.configureBlocking(false);
            //注册到服务器上
            socketChannel.register(selectionKey.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(bufferSize));
        }
        public void read(SelectionKey selectionKey) throws IOException {
            SocketChannel socketChannel = ((ServerSocketChannel) selectionKey.channel()).accept();
            ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
            int len = 0;
            while ((len = socketChannel.read(buffer))>0){
                buffer.flip();
                byte[] bytes = new byte[len];
                System.out.println(new String(bytes,0,len));
                buffer.clear();
            }
        }

        /**
         * 业务方法
         */
        @Override
        public void run() {

        }

    }

    public static void main(String[] args) throws IOException, InterruptedException {
        //服务器启动
        new Server().init();
    }
}

步骤有点多

netty

高性能的java nio异步通信框架
场景:
dubbo /zookeeper /rocketmq /底层rpc通信就用的是netty
常用类:
EventLoop,EventLoopGroup:EventLoop目的是为Channel处理IO操作,一个EventLoop可以为多个Channel服务,EventLoopGroup会包含多个EventLoop。
BootStrap,ServerBootstrap:
一个Netty应用通常由一个Bootstrap开始,它主要作用是配置整个Netty程序,串联起各个组件。
ChannelInitializer:
ChannelInitializer便是用来配置这些Handler,它会提供一个ChannelPipeline,并把Handler加入到ChannelPipeline。
Handler:
Handler主要用来处理各种事件,这里的事件很广泛,比如可以是连接、数据接收、异常、数据转换等。
ChannelInboundHandler:
这个Handler的作用就是处理接收到数据时的事件
Future:
通过Future和ChannelFutures,他们可以注册一个监听,当操作执行成功或失败时监听会自动触发。总之,所有的操作都会返回一个ChannelFuture。
服务器端:

/**
 * @author toms
 * 服务器端
 */
public class NettyServer {

    /**
     * 启动服务
     * */
    public void init() {
        ServerBootstrap bootstrap = new ServerBootstrap();
        // 用来接收进来的连接
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        // 用来处理已经被接收的连接
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        bootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ServerChannelInit());
        try {
            ChannelFuture future = bootstrap.bind(8080).sync();
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    /**
     * 业务类
     */
    class ServerChannelInit extends  ChannelInitializer  {

        @Override
        protected void initChannel(SocketChannel socketChannel) throws Exception {
            ChannelPipeline pipeline = socketChannel.pipeline();
            //解码与编码
            pipeline.addLast("decoder", new StringDecoder());
            pipeline.addLast("encoder", new StringEncoder());
            //自己的逻辑handle
            pipeline.addLast("handler", new ServerHandle());
        }
    }

    public static void main(String[] args) {
        new NettyServer().init();
    }
}

处理类

/**
 * @author toms
 *   一个最常用的Handler。这个Handler的作用就是处理接收到数据时的事件,
 *  也就是说,我们的业务逻辑一般就是写在这个Handler里面的
 * */
public class ServerHandle extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ctx.write("received your msg");
        System.out.println(msg);
        ctx.flush();
    }


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }

}

客户端:

/**
 * @author toms
 */
public class NettyClient {

    public void init() {
        EventLoopGroup group = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .handler(new ClientChannelInit());
        try {
            Channel channel = bootstrap.connect("127.0.0.1", 8080).sync().channel();
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            for (; ; ) {
                String msg = reader.readLine();
                if (msg == null) {
                    continue;
                }
                channel.writeAndFlush(msg + "\r\n");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            group.shutdownGracefully();
        }
    }

    /**
     * 配置Handler
     */
    class ClientChannelInit extends ChannelInitializer {

        /**
         * 这个地方的 必须和服务端对应上。否则无法正常解码和编码
         */
        @Override
        protected void initChannel(SocketChannel socketChannel) throws Exception {
            ChannelPipeline pipeline = socketChannel.pipeline();
            pipeline.addLast("decoder", new StringDecoder());
            pipeline.addLast("encoder", new StringEncoder());
            pipeline.addLast("handle", new ClientHandle());
        }
    }

    public static void main(String[] args) {
        new NettyClient().init();
    }

}

处理类

/**
 * @author toms
 * */
public class ClientHandle extends ChannelInboundHandlerAdapter {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("active");
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("dead");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("server say : "+msg.toString());
    }
}

ChannelPipeline是ChannelHandler的容器,
Netty的ChannelPipeline和ChannelHandler机制类似于Servlet 和Filter 过滤器
一个Channel中包含一个ChannelPipeline,用来处理Channel中的事件,一个ChannelPipeline中可以包含很多个handler,

tcp粘包拆包

设置定长消息,服务端每次读取既定长度的内容作为一条完整消息;
使用带消息头的协议、消息头存储消息开始标识及消息长度信息,服务端获取消息头的时候解析出消息长度,然后向后读取该长度的内容;
设置消息边界,服务端从网络流中按消息边界分离出消息内容。比如在消息末尾加上换行符用以区分消息结束。

解决方案:自定义解码器和编码器

public LengthFieldBasedFrameDecoder(ByteOrder byteOrder,
int maxFrameLength,
int lengthFieldOffset,
int lengthFieldLength,
int lengthAdjustment,
int initialBytesToStrip,
boolean failFast) {

}
byteOrder:表示字节流表示的数据是大端还是小端,用于长度域的读取;
maxFrameLength:表示的是包的最大长度,超出包的最大长度netty将会做一些特殊处理;
lengthFieldOffset:指的是长度域的偏移量,表示跳过指定长度个字节之后的才是长度域;
lengthFieldLength:记录该帧数据长度的字段本身的长度;
lengthAdjustment:该字段加长度字段等于数据帧的长度,包体长度调整的大小,长度域的数值表示的长度加上这个修正值表示的就是带header的包;
initialBytesToStrip:从数据帧中跳过的字节数,表示获取完一个完整的数据包之后,忽略前面的指定的位数个字节,应用解码器拿到的就是不带长度域的数据包;
failFast:如果为true,则表示读取到长度域,TA的值的超过maxFrameLength,就抛出一个 TooLongFrameException,而为false表示只有当真正读取完长度域的值表示的字节之后,才会抛出 TooLongFrameException,默认情况下设置为true,建议不要修改,否则可能会造成内存溢出
自定义消息类

/** 數據類型**/
    private byte type;
    /**消息長度*/
    private int len;
    /**要发送的数据*/
    private String data;

客户端

/**
 * @author toms
 */
public class NettyClient {

    public void init() {
        EventLoopGroup group = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .handler(new ClientChannelInit());
        try {
            Channel channel = bootstrap.connect("127.0.0.1", 8080).sync().channel();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            group.shutdownGracefully();
        }
    }

    /**
     * 配置Handler
     */
    class ClientChannelInit extends ChannelInitializer {
        @Override
        protected void initChannel(SocketChannel socketChannel) throws Exception {
            ChannelPipeline pipeline = socketChannel.pipeline();
            pipeline.addLast(new ClientEncode());
            pipeline.addLast(new ClientHandle());
        }
    }

    public static void main(String[] args) {
        new NettyClient().init();
    }

}

自定义编码器

/**
 * @author toms
 * 編碼器
 * */
public class ClientEncode extends MessageToByteEncoder {

    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, Message message, ByteBuf byteBuf) throws Exception {
        System.out.println("-------- 編碼-----------");
        if(message == null ){
            throw new Exception("NULL");
        }
        String data = message.toString();
        byte[] body = data.getBytes(Charset.forName("UTF-8"));
        byteBuf.writeByte(message.getType());
        byteBuf.writeByte(body.length);
        byteBuf.writeBytes(body);
    }
}

自定义handle

/**
 * @author toms
 * 在handler中我们发送了一个Message对象。然后会由NewEncoder编码发送出去
 * */
public class ClientHandle extends ChannelInboundHandlerAdapter {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("active");
        String m = "誰扔的炮仗!!!!!";
        Message message = new Message((byte) 0xCA,m.length(),m);
        ctx.writeAndFlush(message);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("dead");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("server say : "+msg.toString());
    }
}

服务器端

/**
 * @author toms
 * 服务器端
 */
public class NettyServer {

    /**
     * 启动服务
     */
    public void init() {
        ServerBootstrap bootstrap = new ServerBootstrap();
        // 用来接收进来的连接
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        // 用来处理已经被接收的连接
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        bootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ServerChannelInit())
                //服务端将不能处理的客户端连接请求放在队列中等待处理,backlog参数指定了队列的大小
                .option(ChannelOption.SO_BACKLOG, 128)
                //是否启用心跳保活机制
                .childOption(ChannelOption.SO_KEEPALIVE,true);

        try {
            ChannelFuture future = bootstrap.bind(8080).sync();
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    /**
     *
     */
    class ServerChannelInit extends ChannelInitializer {
        /**
         * 最大長度
         */
        private int MAX_FRAME_LENGTH = 1024 * 1024;
        /**
         * Message类中的length的长度,int占4位
         */
        private int LENGTH_FIELD_LENGTH = 4;
        /**
         * 偏移多少位之后才是我们的消息体
         */
        private int LENGTH_FIELD_OFFSET = 4;
        /**
         * 数据帧的长度
         */
        private int LENGTH_ADJUSTMENT = 0;
        /**
         * 跳过数据帧中的字节数
         */
        private int INITIAL_BYTES_TO_STRIP = 0;

        @Override
        protected void initChannel(SocketChannel socketChannel) throws Exception {
            ChannelPipeline pipeline = socketChannel.pipeline();
            //解码与编码
            pipeline.addLast(new MessageDecoder(MAX_FRAME_LENGTH,LENGTH_FIELD_LENGTH,LENGTH_FIELD_OFFSET,LENGTH_ADJUSTMENT,INITIAL_BYTES_TO_STRIP));
            //自己的逻辑handle
            pipeline.addLast("handler", new ServerHandle());
        }
    }

    public static void main(String[] args) {
        new NettyServer().init();
    }
}

自定义解码器

/**
 * @author toms
 * 消息解碼
 * */
public class MessageDecoder extends LengthFieldBasedFrameDecoder {

    private static final int HEADER_SIZE = 8;
    /** 數據類型**/
    private byte type;
    /**消息長度*/
    private int len;
    /**要发送的数据*/
    private String data;
    /**
     *
     * @param maxFrameLength   网络字节序,默认为大端字节序
     * @param lengthFieldOffset 消息中长度字段偏移的字节数
     * @param lengthFieldLength 数据帧的最大长度
     * @param lengthAdjustment 该字段加长度字段等于数据帧的长度
     * @param initialBytesToStrip 从数据帧中跳过的字节数
     * @param failFast 如果为true,则表示读取到长度域,TA的值的超过maxFrameLength,就抛出一个 TooLongFrameException
     */
    public MessageDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip) {
        super(maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip);
    }

    @Override
    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        if(in == null){
            return null;
        }
        if(in.readableBytes()<=HEADER_SIZE){
            return null;
        }
        type = in.readByte();
        len = in.readByte();
        if(in.readableBytes()

自定义handle


/**
 * @author toms
 *   一个最常用的Handler。这个Handler的作用就是处理接收到数据时的事件,
 *  也就是说,我们的业务逻辑一般就是写在这个Handler里面的
 * */
public class ServerHandle extends ChannelInboundHandlerAdapter {

    private int count=1;
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if(msg instanceof Message){
            Message message = (Message) msg;
            System.out.println(message);
        }
    }


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }

}
netty.png

序列化与反序列化

编码(Encoder)也就是发生在发送消息的时候需要将消息编译成字节对象

序列化工具protobuf


    com.google.protobuf
    protobuf-java
    3.0.2

你可能感兴趣的:(基础总结-socket)