Netty通信

java Netty框架学习

JAVA IO模型

理解java io基本知识
1、同步和异步的主要区别:重点在于多个任务和事件发生时,一个事件的发生或者执行是否会导致整个流程的暂停。

2、阻塞和非阻塞:(阻塞)client发出请求,不满足就一直等待,直至条件满足;(非阻塞)发送一个请求,不满足就告知,不会一直等待。

3、阻塞io和非阻塞io:io有两个阶段,查看数据是否就绪;进行数据拷贝。阻塞和非阻塞的区别就是在第一阶段是否会一直等待,还是会发送消息

4、同步io和异步io:同步IO和异步IO的关键区别反映在数据拷贝阶段是由用户线程完成还是内核完成。所以说异步IO必须要有操作系统的底层支持。
在这里插入图片描述
5、理解多路复用IO模型
在多路复用IO模型中,Selector不断去轮询多个socket的状态,只有当socket真正有读写事件时,才真正调用实际的IO读写操作
在这里插入图片描述

NIO编程

  • 声明并得到通道管理器
  • 获得ServerSocket通道,并进行初始化
  • 将该通道设置为非阻塞
  • 将ip和port端口绑定到此通道
  • 将通道管理器和该通道绑定,并可以监听
	SelectionKey.OP_CONNECT;//当客户端的尝试连接到服务器
	SelectionKey.OP_ACCEPT;//当服务器接受来自客户端的请求
	SelectionKey.OP_READ;//当服务器可以从channel中读取数据
	SelectionKey.OP_WRITE;//当服务器可以向channel中写入数据
  • 轮询监听selector上是否有需要处理的事件,如果有则处理。
  • 返回有该键的相关操作集(应该是可读可写操作吧)所标识的一个操作准备就绪的集合
    处理信息
    在这里插入图片描述
    NioServer
package com.hoolai.osi;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;

public class TcpServer {
	public static void main(String[] args) {
		serverRun();
	}
	
	public static void serverRun() {
		
		int port = 4700;
		ServerSocket serverSocket = null;
		int recvMsgSize = 0;
		//接收缓冲数组
		byte[] recvBuf = new byte[32];
		
		try {
			serverSocket = new ServerSocket(port);
			System.out.println("服务器启动,请求建立连接");
			
			while(true) {
				//阻塞io一直等待客户端连接
				Socket clientSocket = serverSocket.accept();
				SocketAddress clientIp = clientSocket.getRemoteSocketAddress();
				System.out.println("客户端的ip为 :"+clientIp);
				
				InputStream in = clientSocket.getInputStream();
				OutputStream out = clientSocket.getOutputStream();
				
				while ((recvMsgSize = in.read(recvBuf))!=-1) {
					String recvData = new String(recvBuf);
					System.out.println(recvData);
					out.write(recvBuf, 0, recvMsgSize);
				}
				clientSocket.close();
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
}

NioClient


	package com.hoolai.osi;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpClient {
	public static void main(String[] args) {
		clientRun();
	}
	public static void clientRun() {
		
		int port = 4700;
		String host = "localhost"; 
		Socket socket = null;
		String msg = "begin game";
		
		try {
			socket = new Socket(host, port);
			System.out.println("连接服务器并发送数据");
			OutputStream out = socket.getOutputStream();
			InputStream in = socket.getInputStream();
			byte[] data = msg.getBytes();
			out.write(data);
			//接收数据
			//目前收到的总字节长度
			int totalRecvByte = 0;
			//最后一次读取的字节长度
			int lastRecvByte;
			
			while(totalRecvByte < data.length) {
				lastRecvByte = in.read(data, totalRecvByte, data.length-totalRecvByte);
				
				if (lastRecvByte == -1) {
					System.out.println("连接已断开");
				}
				
				totalRecvByte += lastRecvByte;
				System.out.println("接收的数据"+new String(data));
			}
				
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			try {
				if (socket != null) {
					socket.close();
				}
			} catch (Exception e2) {
				e2.printStackTrace();
			}
		}
	}
}

我对这个过程的理解:服务器得到serversocketchannel,绑定ip和端口。并注册到Selector中。客户端得到socketChannel,绑定ip和端口,发送connect指令,并注册进Selector(连接)事件,取出有消息的通道,判断是否连接(可读),如果连接成功,则发送消息给服务器,服务器端的Selector.select()检测到请求过来,返回值,并根据检测request(即每个request会封装一个channel,将所有的channel注册在一个Selector(接收连接)上,然后selector开始不断的轮询每个request的可读状态。)得到一个类似于key-serverchannel的集合,然后通过操作key得到ServerSocketChannel,并将此ServerSocketChannel与Socket,channel连接
理解有错误的地方,欢迎指出,因为自己也是个初学者。

Java Netty

Netty的零拷贝
Netty 的接收和发送 ByteBuffer 采用 DIRECT BUFFERS,使用堆外直接内存进行 Socket 读写,不需要进行字节缓冲区的二次拷贝。如果使用传统的堆内存(HEAP BUFFERS)进行 Socket 读写,JVM 会将堆内存 Buffer 拷贝一份到直接内存中,然后才写入 Socket 中。相比于堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝。

  1. 创建client连接服务器处理线程组和io处理线程组
  2. 定义Bootstrap,服务工具类,服务器通道的一系列配置参数
  3. bootstrap绑定两个线程组,和选择通道模式。
  4. 通过serverhandler中的channelReader方法读取object 和ChannelHandlerContext
  5. 响应消息给客户端并断开channel连接
    ServerNetty

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
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;

public class ServerNetty {
	private int port;
	
	public ServerNetty(int port) {
		this.port = port;
	}

	public static void main(String[] args) {
		new ServerNetty(4700)
			.run();
	}
	
	public void run(){
		EventLoopGroup bossGrop = new NioEventLoopGroup();//用于处理服务器接收客户端连接
		EventLoopGroup workerGrop = new NioEventLoopGroup();//用于进行网络通信(读写)
		
		try {
			ServerBootstrap bootstrap = new ServerBootstrap();//服务工具类,服务器通道的一系列参数配置
			bootstrap.group(bossGrop, workerGrop)//绑定两个线程池
				.channel(NioServerSocketChannel.class)//制定NIO的模式
				.childHandler(new ChannelInitializer<SocketChannel>() {

					@Override
					protected void initChannel(SocketChannel ch) throws Exception {//配置数据具体的处理方式
						ch.pipeline().addLast(new ServerlHandler());
					}
				})
				/**
				 * 
				 *  SO_BACKLOG的理解,服务器端有两个队列,分别为A,B
				 *	当客户端发出SYN连接,服务器返回ACK时,TCP内核把客户端加入A队列
				 *	当客户端对服务器端回复ACK时,TCP内核把客户端移动到B队列,
				 *	accept()方法返回,完成3次握手
				 *	A队列和B队列的和就是ChannelOption.SO_BACKLOG,当SO_BACKLOG小了
				 *	A和B的和就小了,可能会导致新的客户端无法连接。
				 */
				.option(ChannelOption.SO_BACKLOG, 128)//设置tcp的缓冲区大小
				.option(ChannelOption.SO_RCVBUF, 32*1024)//设置发送缓冲区大小
				.option(ChannelOption.SO_SNDBUF,32*1024)//设置接收缓冲区大小
				.childOption(ChannelOption.SO_KEEPALIVE, true);//保持连接
			ChannelFuture future = bootstrap.bind(port).sync();//打开通道直到完成
			future.channel().closeFuture().sync();
			
		}catch (Exception e) {
			e.printStackTrace();
		}finally {
			workerGrop.shutdownGracefully();
			bossGrop.shutdownGracefully();
		}
	}
}

ServerHandler

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

/**
 * 读取通道channel中的channelHandlerContext和object
 * 通过channelRead()方法解析get和post的数据,
 * 并发送给protomessage交给MsgHandler处理.
 * 注意:netty5之后的版本channelRead方法在ChannelHandlerAdapter中
 * @author lenovo
 *
 */
public class ServerlHandler extends ChannelInboundHandlerAdapter {

	@Override
	public void channelRead(ChannelHandlerContext ctx,Object msg) throws Exception{
		ByteBuf buf = (ByteBuf)msg;
		byte[] data = new byte[buf.readableBytes()];
		buf.readBytes(data);
		String req = new String(data,"utf-8");
		System.out.println("Server: "+req);
		//发送给客户端
		String resp = "发给你,客户端";
		ctx.writeAndFlush(Unpooled.copiedBuffer((resp+"6666666666666666666666666666666").getBytes()));
	}
	
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		cause.printStackTrace();
		ctx.close();
	}
}

ClientNetty


import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

/**
 * netty客户端
 * @author lenovo
 *
 */
public class ClientNetty {
	public static void main(String[] args) throws Exception{
		EventLoopGroup workerGroup = new NioEventLoopGroup();
		Bootstrap bootstrap = new Bootstrap(); 
		
		bootstrap.group(workerGroup)
			.channel(NioSocketChannel.class)
			.handler(new ChannelInitializer<SocketChannel>() {
				@Override
				protected void initChannel(SocketChannel sc) throws Exception {
					sc.pipeline().addLast(new ClientHandler());
				}
			});
		
		/**
		 *  ChannelFuture	Netty中的所有I / O操作都是异步的。 
		 *	这意味着任何I / O调用都将立即返回,不保证在呼叫结束时所请求的I / O操作已完成。 
		 *	相反,您将返回一个ChannelFuture实例,该实例向您提供有关I / O操作结果或状态的信息。 
		 */
		ChannelFuture future = bootstrap.connect("localhost",8000).sync();
		future.channel().writeAndFlush(Unpooled.copiedBuffer("66666666".getBytes()));
		future.channel().closeFuture().sync();
		workerGroup.shutdownGracefully();
	}
}

ClientHandler


import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;

public class ClientHandler extends ChannelInboundHandlerAdapter{
	 @Override
	    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
	        try {
	            ByteBuf buf = (ByteBuf) msg;
	            byte[] data = new byte[buf.readableBytes()];
	            buf.readBytes(data);
	            String info = new String(data,"utf-8");
	            System.out.println("Client:" + info);
	        } finally {
	            ReferenceCountUtil.release(msg);
	        }
	    }
	 
	    @Override
	    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
	        cause.printStackTrace();
	        ctx.close();
	    }
}

目前博主正在学习,上面只是学习过程中写的一点笔记。如有错误,欢迎指出

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值