Netty简介

Netty是由JBOSS提供的一个java开源框架,现为 Github上的独立项目。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
也就是说,Netty 是一个基于NIO的客户、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户、服务端应用。Netty相当于简化和流线化了网络应用的编程开发过程,例如:基于TCP和UDP的socket服务开发。
“快速”和“简单”并不用产生维护性或性能上的问题。Netty 是一个吸收了多种协议(包括FTP、SMTP、HTTP等各种二进制文本协议)的实现经验,并经过相当精心设计的项目。最终,Netty 成功的找到了一种方式,在保证易于开发的同时还保证了其应用的性能,稳定性和伸缩性。

说明:本文只介绍netty框架最基本的应用,而且是每次客户端请求完毕会关闭连接,后续会写一篇客户端先与服务端建立连接,然后一条条发送数据,发送完毕主动关闭连接的博客。

Netty搭建WebSocket服务端

Netty服务端

1.引入依赖

<?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 http://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>1.5.9.RELEASE</version> <!-- 我这里用的1.5.9 -->
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.blaze</groupId>
<artifactId>netty-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>netty-demo</name>
<description>Demo project for Spring Boot</description> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency> <dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency> <!--fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.50</version>
</dependency> <!--netty依赖-->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.43.Final</version>
</dependency> </dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.blaze.nettydemo.server.NettyServer</mainClass>
</configuration>
</plugin>
</plugins>
<finalName>netty-demo</finalName>
</build> </project>

2.Netty服务端

NettyServer

package com.blaze.nettydemo.server;

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;
import org.springframework.stereotype.Component; /**
* create by zy 2019/11/15 9:14
* TODO
*/
@Component
public class NettyServer {
public static void main(String[] args) {
int port = 9898;
new NettyServer().bind(port);
} public void bind(int port) {
/**
* interface EventLoopGroup extends EventExecutorGroup extends ScheduledExecutorService extends ExecutorService
* 配置服务端的 NIO 线程池,用于网络事件处理,实质上他们就是 Reactor 线程组
* bossGroup 用于服务端接受客户端连接,workerGroup 用于进行 SocketChannel 网络读写
*/
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
/**
* ServerBootstrap 是 Netty 用于启动 NIO 服务端的辅助启动类,用于降低开发难度
*/
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChildChannelHandler()); /**服务器启动辅助类配置完成后,调用 bind 方法绑定监听端口,调用 sync 方法同步等待绑定操作完成*/
ChannelFuture f = b.bind(port).sync(); System.out.println(Thread.currentThread().getName() + ",服务器开始监听端口,等待客户端连接........."); /**下面会进行阻塞,等待服务器连接关闭之后 main 方法退出,程序结束*/
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
/**优雅退出,释放线程池资源*/
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
} /**
* 初始化连接
*/
@Component
private class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
@Override
public void initChannel(SocketChannel socketChannel) throws Exception {
/**
* 设置 netty 服务端的 handler
*/
socketChannel.pipeline().addLast(new NettyServerHandler()); /**
* 如果使用 netty 搭建 http 服务端,则用下面三个设置代替上面一个设置
*/
//socketChannel.pipeline().addLast(new HttpServerCodec());// http 编解码
//socketChannel.pipeline().addLast("httpAggregator", new HttpObjectAggregator(512 * 1024)); // http 消息聚合器
//socketChannel.pipeline().addLast(new HttpServerHandler());
}
}
}

NettyServerHandler

package com.blaze.nettydemo.server;

import com.alibaba.fastjson.JSON;
import com.blaze.nettydemo.model.RequestModel;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.springframework.stereotype.Component; /**
* create by zy 2019/11/15 10:06
* TODO
*/
@Component
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
/**
* 收到客户端消息,自动触发
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
/**
* 将 msg 转为 Netty 的 ByteBuf 对象,类似 JDK 中的 java.nio.ByteBuffer,不过 ButeBuf 功能更强,更灵活
*/
ByteBuf buf = (ByteBuf) msg;
/**
* readableBytes:获取缓冲区可读字节数,然后创建字节数组
* 从而避免了像 java.nio.ByteBuffer 时,只能盲目的创建特定大小的字节数组,比如 1024
*/
byte[] reg = new byte[buf.readableBytes()];
/**
* readBytes:将缓冲区字节数组复制到新建的 byte 数组中
* 然后将字节数组转为字符串
*/
buf.readBytes(reg);
String body = new String(reg, "UTF-8");
System.out.println(Thread.currentThread().getName() + ",The server receive order : " + body); String respMsg = "I am Server, success!"; /**
* 业务处理代码 此处省略
* ......
*/ /**
* 回复消息
* copiedBuffer:创建一个新的缓冲区,内容为里面的参数
* 通过 ChannelHandlerContext 的 write 方法将消息异步发送给客户端
*/
ByteBuf respByteBuf = Unpooled.copiedBuffer(respMsg.getBytes());
ctx.write(respByteBuf);
} @Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
/**
* flush:将消息发送队列中的消息写入到 SocketChannel 中发送给对方,为了频繁的唤醒 Selector 进行消息发送
* Netty 的 write 方法并不直接将消息写如 SocketChannel 中,调用 write 只是把待发送的消息放到发送缓存数组中,再通过调用 flush
* 方法,将发送缓冲区的消息全部写入到 SocketChannel 中
*/
ctx.flush();
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
/**当发生异常时,关闭 ChannelHandlerContext,释放和它相关联的句柄等资源 */
ctx.close(); }
}

3.Netty客户端

NettyClient

package com.blaze.nettydemo.client;

import io.netty.bootstrap.Bootstrap;
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.NioSocketChannel;
import org.springframework.stereotype.Component; /**
* create by zy 2019/11/15 10:08
* TODO
*/
@Component
public class NettyClient {
/**
* 使用 3 个线程模拟三个客户端
*/
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
new Thread(new MyThread()).start();
}
} static class MyThread implements Runnable {
/**服务端 ip 及端口*/
@Override
public void run() {
connect("193.168.19.25", 9898);
} public void connect(String host, int port) {
/**配置客户端 NIO 线程组/池*/
EventLoopGroup group = new NioEventLoopGroup();
try {
/**
* Bootstrap 与 ServerBootstrap 都继承(extends)于 AbstractBootstrap
* 创建客户端辅助启动类,并对其配置,与服务器稍微不同,这里的 Channel 设置为 NioSocketChannel
* 然后为其添加 Handler,这里直接使用匿名内部类,实现 initChannel 方法
* 作用是当创建 NioSocketChannel 成功后,在进行初始化时,将它的ChannelHandler设置到ChannelPipeline中,用于处理网络I/O事件
*/
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new NettyClientHandler());
}
}); /**connect:发起异步连接操作,调用同步方法 sync 等待连接成功*/
ChannelFuture channelFuture = b.connect(host, port).sync();
System.out.println(Thread.currentThread().getName() + ",客户端发起异步连接.........."); /**等待客户端链路关闭*/
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
/**优雅退出,释放NIO线程组*/
group.shutdownGracefully();
}
} }
}

NettyClientHandler

package com.blaze.nettydemo.client;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.springframework.stereotype.Component; import java.util.logging.Logger; /**
* create by zy 2019/11/15 10:09
* TODO
*/
@Component
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
private static final Logger logger = Logger.getLogger(NettyClientHandler.class.getName());
/**
* 当客户端和服务端 TCP 链路建立成功之后,Netty 的 NIO 线程会调用 channelActive 方法
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
String reqMsg = "客户端请求服务端发送的数据"; byte[] reqMsgByte = reqMsg.getBytes("UTF-8");
ByteBuf reqByteBuf = Unpooled.buffer(reqMsgByte.length);
/**
* writeBytes:将指定的源数组的数据传输到缓冲区
* 调用 ChannelHandlerContext 的 writeAndFlush 方法将消息发送给服务器
*/
reqByteBuf.writeBytes(reqMsgByte);
ctx.writeAndFlush(reqByteBuf);
} /**
* 当服务端返回应答消息时,channelRead 方法被调用,从 Netty 的 ByteBuf 中读取并打印应答消息
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
String body = new String(req, "UTF-8");
System.out.println(Thread.currentThread().getName() + ",Server return Message:" + body);
ctx.close();
} /**
* 当发生异常时,打印异常 日志,释放客户端资源
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
/**释放资源*/
logger.warning("Unexpected exception from downstream : " + cause.getMessage());
ctx.close(); }
}

4.Netty Http服务端

NettyServer

package com.blaze.nettydemo.server;

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;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import org.springframework.stereotype.Component; /**
* create by zy 2019/11/15 9:14
* TODO
*/
@Component
public class NettyServer {
public static void main(String[] args) {
int port = 9898;
new NettyServer().bind(port);
} public void bind(int port) {
/**
* interface EventLoopGroup extends EventExecutorGroup extends ScheduledExecutorService extends ExecutorService
* 配置服务端的 NIO 线程池,用于网络事件处理,实质上他们就是 Reactor 线程组
* bossGroup 用于服务端接受客户端连接,workerGroup 用于进行 SocketChannel 网络读写
*/
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
/**
* ServerBootstrap 是 Netty 用于启动 NIO 服务端的辅助启动类,用于降低开发难度
*/
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChildChannelHandler()); /**服务器启动辅助类配置完成后,调用 bind 方法绑定监听端口,调用 sync 方法同步等待绑定操作完成*/
ChannelFuture f = b.bind(port).sync(); System.out.println(Thread.currentThread().getName() + ",服务器开始监听端口,等待客户端连接........."); /**下面会进行阻塞,等待服务器连接关闭之后 main 方法退出,程序结束*/
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
/**优雅退出,释放线程池资源*/
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
} /**
* 初始化连接
*/
@Component
private class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
@Override
public void initChannel(SocketChannel socketChannel) throws Exception {
/**
* 设置 netty 服务端的 handler
*/
//socketChannel.pipeline().addLast(new NettyServerHandler()); /**
* 如果使用 netty 搭建 http 服务端,则用下面三个设置代替上面一个设置
*/
socketChannel.pipeline().addLast(new HttpServerCodec());// http 编解码
socketChannel.pipeline().addLast("httpAggregator", new HttpObjectAggregator(512 * 1024)); // http 消息聚合器
socketChannel.pipeline().addLast(new HttpServerHandler());
}
}
}

HttpServerHandler

package com.blaze.nettydemo.server;

import com.alibaba.fastjson.JSONObject;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import org.springframework.stereotype.Component; /**
* create by zy 2019/11/19 9:21
* TODO
*/
@Component
public class HttpServerHandler extends ChannelInboundHandlerAdapter { @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof FullHttpRequest) {
FullHttpRequest req = (FullHttpRequest) msg; try {
// 1.获取URI
String uri = req.uri();
System.out.println("uri:" + uri);
// 2.获取请求体
ByteBuf buf = req.content();
String content = buf.toString(CharsetUtil.UTF_8); // 3.根据请求的方法uri不同处理不同的逻辑
Object rc = new Object();
switch (uri) {
case "/test1":
// ......
break;
case "/ltest2":
// ......
break;
default:
break;
}
// 4.返回结果
response(ctx, rc);
} finally {
req.release();
}
}
} private void response(ChannelHandlerContext ctx, Object c) { // 1.设置响应
FullHttpResponse resp = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
Unpooled.copiedBuffer(JSONObject.toJSONString(c), CharsetUtil.UTF_8));
resp.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=UTF-8");
// 2.发送
// 注意必须在使用完之后,close channel
ctx.writeAndFlush(resp).addListener(ChannelFutureListener.CLOSE);
}
}

Http客户端,使用postman请求服务端进行测试即可。

本文参考:https://blog.csdn.net/wangmx1993328/article/details/83036285

Netty搭建服务端的简单应用的更多相关文章

  1. Netty入门系列(1) --使用Netty搭建服务端和客户端

    引言 前面我们介绍了网络一些基本的概念,虽然说这些很难吧,但是至少要做到理解吧.有了之前的基础,我们来正式揭开Netty这神秘的面纱就会简单很多. 服务端 public class PrintServ ...

  2. CAS 5.1.x 的搭建和使用(二)—— 通过Overlay搭建服务端-其它配置说明

    CAS单点登录系列: CAS 5.1.x 的搭建和使用(一)—— 通过Overlay搭建服务端 CAS5.1.x 的搭建和使用(二)—— 通过Overlay搭建服务端-其它配置说明 CAS5.1.x ...

  3. CAS 5.1.x 的搭建和使用(一)—— 通过Overlay搭建服务端

    CAS单点登录系列: CAS 5.1.x 的搭建和使用(一)—— 通过Overlay搭建服务端 CAS5.1.x 的搭建和使用(二)—— 通过Overlay搭建服务端-其它配置说明 CAS5.1.x ...

  4. 原理剖析-Netty之服务端启动工作原理分析(下)

    一.大致介绍 1.由于篇幅过长难以发布,所以本章节接着上一节来的,上一章节为[原理剖析(第 010 篇)Netty之服务端启动工作原理分析(上)]: 2.那么本章节就继续分析Netty的服务端启动,分 ...

  5. 原理剖析-Netty之服务端启动工作原理分析(上)

    一.大致介绍 1.Netty这个词,对于熟悉并发的童鞋一点都不陌生,它是一个异步事件驱动型的网络通信框架: 2.使用Netty不需要我们关注过多NIO的API操作,简简单单的使用即可,非常方便,开发门 ...

  6. Netty实现服务端客户端长连接通讯及心跳检测

    通过netty实现服务端与客户端的长连接通讯,及心跳检测.        基本思路:netty服务端通过一个Map保存所有连接上来的客户端SocketChannel,客户端的Id作为Map的key.每 ...

  7. 使用Apache MINA框架搭建服务端

    使用MINA框架搭建服务端步骤: 1.定义一个启动服务的类MinaServer,并实现接口ServletContextListener 2.定义一个处理业务逻辑的类MinaServerHandler, ...

  8. Netty实现客户端和服务端通信简单例子

    Netty是建立在NIO基础之上,Netty在NIO之上又提供了更高层次的抽象. 在Netty里面,Accept连接可以使用单独的线程池去处理,读写操作又是另外的线程池来处理. Accept连接和读写 ...

  9. 【转】C# client 与java netty 服务端的简单通信,客户端采用Unity

    http://blog.csdn.net/wilsonke/article/details/24721057 近日根据官方提供的通信例子自己写了一个关于Unity(C#)和后台通信的类,拿出来和大家分 ...

随机推荐

  1. go timer定时器 WaitGroup用法

    参考文章: timer定时器: https://www.cnblogs.com/oxspirt/p/7107312.html WaitGroup用法: https://studygolang.com/ ...

  2. 49.Django起步学习

    django起步 django安装 pip install django==2.0.4(版本号) pip install django 默认安装最新版本 创建项目 django-admin start ...

  3. layui监听radio点击事件

    layui.form.on('radio(layui_dataType)', function(data){ console.log(data.elem); //得到radio原始DOM对象 cons ...

  4. SpringBoot(三)手写starter pom自动配置

    思想:主要是EnableAutoConfiguration在启动的时候会扫描spring.factories并加载 1在resource下面新建META-INF/spring.factories 2在 ...

  5. oracle经典查询语句

    1. select * from emp;   2. select empno, ename, job from emp;   3. select empno 编号, ename 姓名, job 工作 ...

  6. array_chunk — 将一个数组分割成多个

    说明 array_chunk ( array $array , int $size [, bool $preserve_keys = false ] ) : array 将一个数组分割成多个数组,其中 ...

  7. (十四)SpringBoot之事务处理

    一.简介 ssh ssm都有事务管理service层通过applicationContext.xml配置,所有service方法都加上事务操作: 用来保证一致性,即service方法里的多个dao操作 ...

  8. android 自动化测试案例之 MonkeyRunner

    #-*- coding: UTF-8 -*- #上面第一行是设置文件编码,windows下第一行必须是这个#文件名 MonkeyRunner.py#功能: 使用monkey runner测试app,此 ...

  9. SQL优化中的重要概念:锁定

    原文:SQL优化中的重要概念:锁定 上篇文章讲的是事务,这篇就引出另一个重要概念,就是锁定. 当一个用户要读取另一个用户正在修改的数据,或者一个用户正在修改另一个用户正在读取的数据,或者一个用户要修改 ...

  10. 在ASP.NET Core中实现自动注入、批量注入

    我们在使用AddScoped.AddTransient.AddSingleton这类方法的时候很是麻烦.我们每增加一个接口以及其实现的时候,是不是需要在这里硬编码注册一行代码呢?项目小还好,但当我们的 ...