002 hello netty程序编写

1 Netty简介

        Netty是由JBOSS提供的一个java开源框架,Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序;也就是说,Netty 是一个基于NIO的客户、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户、服务端应用;Netty简化和流线化了网络应用的编程开发过程,例如:基于TCP和UDP的socket服务开发;

        选择Netty,原因无他,简单!编程模型固定化,再也不必去编写复杂的代码逻辑去实现通信,也不需要去考虑性能问题,不需要考虑编解码问题,半包读写问题等,这些强大的Netty已经帮我们实现好了,只需要使用即可。

        Netty目前是最流行的NIO框架,其健壮性、功能、性能、可定制性和可扩展性在同类框架都是首屈一指的。它已经得到成百上千的商业/商用项目验证,如Hadoop的RPC框架Avro、以及JMS框架,强大的RocketMQ、还有主流的分布式通信框架Dubbox等等。

        Netty是一个NIO client-server(客户端服务器)框架,使用Netty可以快速开发网络应用,例如服务器和客户端协议。Netty提供了一种新的方式来使开发网络应用程序,这种新的方式使得它很容易使用和有很强的扩展性。Netty的内部实现是很复杂的,但是Netty提供了简单易用的api从网络处理代码中解耦业务逻辑。Netty是完全基于NIO实现的,所以整个Netty都是异步的。

2. Netty架构图

 3. Netty特性

4. hello netty

下载地址:http://netty.io/
 Netty实现通信的步骤:
 -1 创建两个的NIO线程组,一个专门用于网络事件处理(接受客户端的连接),另一个则进行网络通信读写
 -2 创建一个ServerBootstrap对象,配置Netty的一系列参数,例如接受传出数据的缓存大小等等。
 -3 创建一个实际处理数据的类ChannelInitializer,进行初始化的准备工作,比如设置接受传出数据的字符集、格式、已经实际处理数据的接口。
 -4 绑定端口,执行同步阻塞方法等待服务器端启动即可。

step1:新建springboot项目、引入依赖包

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.5.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.cc</groupId>
	<artifactId>netty-001</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>netty-001</name>
	<description>netty-001</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!-- netty -->
		<dependency>
			<groupId>io.netty</groupId>
			<artifactId>netty-all</artifactId>
			<version>4.1.12.Final</version>
		</dependency>
		<!-- netty jboss序列化框架marshalling-->
		<dependency>
			<groupId>org.jboss.marshalling</groupId>
			<artifactId>jboss-marshalling</artifactId>
			<version>1.3.0.CR9</version>
		</dependency>
		<dependency>
			<groupId>org.jboss.marshalling</groupId>
			<artifactId>jboss-marshalling-serial</artifactId>
			<version>1.3.0.CR9</version>
		</dependency>
		<!-- netty google序列化框架protobuf(推荐)-->
		<dependency>
			<groupId>com.google.protobuf</groupId>
			<artifactId>protobuf-java</artifactId>
			<version>2.5.0</version>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

step2:创建Server及ServerHandler

package com.cc.netty.quickstart;

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 Server {
	
	public static void main(String[] args) throws InterruptedException {
		
		/* 1、创建两个线程组:一个用于进行网络连接的接收另一个用于数据处理(网络通信的读写)*/
		EventLoopGroup bossGroup = new NioEventLoopGroup();
		EventLoopGroup workGroup = new NioEventLoopGroup();
		
		/* 2、通过辅助类去构造server/client*/
		ServerBootstrap serverBootstrap = new ServerBootstrap();
		
		serverBootstrap.group(bossGroup, workGroup) //传入两个线程组
		/*option设置server参数,childOption设置socket(套接字)对象参数*/
		.channel(NioServerSocketChannel.class)	//创建通道(传入类底层通过clazz字节码new传入类对象【反射】)
		.option(ChannelOption.CONNECT_TIMEOUT_MILLIS , 3000) //3秒超时
		.option(ChannelOption.SO_BACKLOG, 1024) //sync+acept=backlog 指定队列缓存为1024
		//.option(ChannelOption.SO_KEEPALIVE, false) //(是否为长连接)如果在两个小时内没有数据通信,TCP自动发送一个活动探测数据报文
		//.option(ChannelOption.SO_REUSEADDR, true) //允许重复使用本地地址和端口
		.childOption(ChannelOption.SO_RCVBUF, 1024 * 32) //接收区缓存为3K
		.childOption(ChannelOption.SO_SNDBUF, 1024 * 32) //发送区缓存为3K
		.childOption(ChannelOption.TCP_NODELAY, true) //禁用了Nagle算法,允许小数据即时传输(默认为false)
		
		.childHandler(new ChannelInitializer<SocketChannel>() {

			@Override
			protected void initChannel(SocketChannel ch) throws Exception {
				ch.pipeline().addLast(new ServerHandler());
				/*采用责任链模式,添加多个Handler,对数据进行层层过滤
				ch.pipeline().addLast(new ServerHandler()); //1 解码数据(二进制转换为java对象)
				ch.pipeline().addLast(new ServerHandler()); //2校验(检查参数是否正确,正确向下执行,错误直接返回)
				ch.pipeline().addLast(new ServerHandler()); //3处理业务逻辑(处理实际业务逻辑)
				*/
			}
		});
		/*服务器端绑定端口启动服务,启动同步阻塞(启动Server,并进入同步等待,直到有链接请求,数据最终进入ServerHandler处理)
		 * 注意:此处阻塞为应用程序阻塞,实际工作中此方式不会同步阻塞,而且有概率应用服务会停止
		 */
		ChannelFuture channelFuture = serverBootstrap.bind(8765).sync();
		//使用channel级别的监听close端口(调用channel().closeFuture().sync()实现连接通道阻塞)
		channelFuture.channel().closeFuture().sync();
		
		//关闭资源
		bossGroup.shutdownGracefully();
		workGroup.shutdownGracefully();
		
		
	}

}

 

package com.cc.netty.quickstart;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
/*ServerHandler默认继承ChannelHandler,推荐实现ChannelInboundHandlerAdapter
public class ServerHandler implements ChannelHandler {
*/
public class ServerHandler extends ChannelInboundHandlerAdapter {
	
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		System.out.println("server channel active...");
	}
	
	/**
	 * 真正的数据最终会走到这个方法进行处理
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        
    	//读取客户端的数据
    	ByteBuf buf = (ByteBuf) msg;
    	//使用一个byte[]数组存储客户端数据,数组大小设置为buf可读大小
        byte[] request = new byte[buf.readableBytes()];
        //从buf中将数据读取到request数组中
        buf.readBytes(request);
        
        String requestBody = new String(request, "utf-8");
        System.err.println("Server: " + requestBody);
        
        //返回响应数据
        String responseBody = "返回响应数据" + requestBody;
        //writeAndFlush内通过递归的方式不断从通道读数据,读完后关闭通道自动释放msg资源
        ctx.writeAndFlush(Unpooled.copiedBuffer(responseBody.getBytes()));
        
    }

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

step3:创建Client及ClientHandler

package com.cc.netty.quickstart;

import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.Unpooled;
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;
import io.netty.channel.socket.nio.NioSocketChannel;


public class Client {

public static void main(String[] args) throws InterruptedException {
		
		//1. 只需要一个线程组用于我们的实际处理(网络通信的读写)
		EventLoopGroup workGroup = new NioEventLoopGroup();
		
		//2 通过辅助类去构造client
		Bootstrap b = new Bootstrap();
		b.group(workGroup)
		 .channel(NioSocketChannel.class)
		 .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
		 .option(ChannelOption.SO_RCVBUF, 1024 * 32)
		 .option(ChannelOption.SO_SNDBUF, 1024 * 32)
		 .handler(new ChannelInitializer<SocketChannel>() {
			@Override
			protected void initChannel(SocketChannel ch) throws Exception {
				ch.pipeline().addLast(new ClientHandler());	//1
			}
		});
		//客户端连接服务器端地址
		ChannelFuture cf = b.connect("127.0.0.1", 8765).syncUninterruptibly();
		
		//使用writeAndFlush将数据写入缓冲并冲刷入通道进行数据传输,应用Unpooled.copiedBuffer将字节数组转为buffer
		cf.channel().writeAndFlush(Unpooled.copiedBuffer("hello netty!".getBytes()));
		
		Thread.sleep(1000);
		cf.channel().writeAndFlush(Unpooled.copiedBuffer("hello netty!".getBytes()));

		//使用channel级别的监听close端口 阻塞的方式
		cf.channel().closeFuture().sync();
		workGroup.shutdownGracefully();
		
	}
}
package com.cc.netty.quickstart;


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

public class ClientHandler extends ChannelInboundHandlerAdapter {

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		System.out.println("client channel active...");
	}
	
	/**
	 * client处理响应数据
     */
	@Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        try {
        	//读取服务器端返回数据
        	ByteBuf buf = (ByteBuf) msg;
        	 byte[] request = new byte[buf.readableBytes()];
            buf.readBytes(request);
            
            String requestBody = new String(request, "utf-8");
            System.err.println("Clent: " + requestBody);
            
            //读取返回数据后可以继续回写,此处做只读模拟
		} finally {
			//只读必须关闭msg
			ReferenceCountUtil.release(msg);
		}
    }

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

}

step4:启动Server和Client

https://netty.io/wiki/user-guide-for-4.x.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值