【netty】(2)---搭建一个简单服务器
netty(2)---搭建一个简单服务器
说明:本篇博客是基于学习慕课网有关视频教学。效果:当用户访问:localhost:8088 后 服务器返回 “hello netty”;
一、服务端线程模型
下面的做法是服务端监听线程和 IO 线程分离,类似于 Reactor 的多线程模型,它的工作原理图如下(盗的图):

这里netty版本是4.1.25
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.25.Final</version>
</dependency>
二、主程序类
/**
* @Description: 实现客户端发送一个请求,服务器会返回 hello netty
*/
public class HelloServer {
public static void main(String[] args) throws Exception {
// 定义一对线程组
// 主线程组, 用于接受客户端的连接,但是不做任何处理,跟老板一样,不做事
EventLoopGroup bossGroup = new NioEventLoopGroup();
// 从线程组, 老板线程组会把任务丢给他,让手下线程组去做任务
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// netty服务器的创建, 辅助工具类,用于服务器通道的一系列配置
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup) //绑定两个线程组
.channel(NioServerSocketChannel.class) //指定NIO的模式
.childHandler(new HelloServerInitializer()); // 子处理器,用于处理workerGroup
// 启动server,并且设置8088为启动的端口号,同时启动方式为同步
ChannelFuture channelFuture = serverBootstrap.bind(8088).sync();
// 监听关闭的channel,设置位同步方式
channelFuture.channel().closeFuture().sync();
} finally {
//退出线程组
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
上面这段代码展示了服务端的一个基本步骤:
(1)、 初始化用于Acceptor的主"线程池"以及用于I/O工作的从"线程池";
(2)、 初始化ServerBootstrap实例, 此实例是netty服务端应用开发的入口;
(3)、 通过ServerBootstrap的group方法,设置(1)中初始化的主从"线程池";
(4)、 指定通道channel的类型,由于是服务端,故而是NioServerSocketChannel;
(5)、 设置ServerSocketChannel的处理器
(6)、 设置子通道也就是SocketChannel的处理器, 其内部是实际业务开发的"主战场"
(8)、 配置子通道也就是SocketChannel的选项
(9)、 绑定并侦听某个端口
三、子处理器 HelloServerInitializer类
/**
* @Description: 初始化器,channel注册后,会执行里面的相应的初始化方法
*/
public class HelloServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
// 通过SocketChannel去获得对应的管道
ChannelPipeline pipeline = channel.pipeline();
// 通过管道,添加handler
// HttpServerCodec是由netty自己提供的助手类,可以理解为拦截器
// 当请求到服务端,我们需要做解码,响应到客户端做编码
pipeline.addLast("HttpServerCodec", new HttpServerCodec());
// 添加自定义的助手类,返回 "hello netty~"
pipeline.addLast("customHandler", new CustomHandler());
}
}
子处理器也可以通过内部方法来实现的。
b.group(group).channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//这里进行方法实现.......
socketChannel.pipeline().addLast(serverHandler);
}
});
四、自定义助手类 CustomHandler类
/**
* 创建自定义助手类
*/
// SimpleChannelInboundHandler: 对于请求来讲,其实相当于[入站,入境]
public class CustomHandler extends SimpleChannelInboundHandler<HttpObject> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg)
throws Exception {
// 获取channel
Channel channel = ctx.channel();
if (msg instanceof HttpRequest) {
// 显示客户端的远程地址
System.out.println(channel.remoteAddress());
// 定义发送的数据消息
ByteBuf content = Unpooled.copiedBuffer("Hello netty~", CharsetUtil.UTF_8);
// 构建一个http response
FullHttpResponse response =
new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
content);
// 为响应增加数据类型和长度
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());
// 把响应刷到客户端
ctx.writeAndFlush(response);
}
}
/**
* 上面的方法是必须重写的,因为是父类定义的抽象方法。
*
* 下面的方法是一些 助手类的执行顺序
*/
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("channel。。。注册");
super.channelRegistered(ctx);
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("channel。。。移除");
super.channelUnregistered(ctx);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channel。。。活跃");
super.channelActive(ctx);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channel。。。不活跃");
super.channelInactive(ctx);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("channeld读取完毕。。。");
super.channelReadComplete(ctx);
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
System.out.println("用户事件触发。。。");
super.userEventTriggered(ctx, evt);
}
@Override
public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
System.out.println("channel可写更改");
super.channelWritabilityChanged(ctx);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("补货到异常");
super.exceptionCaught(ctx, cause);
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
System.out.println("助手类添加");
super.handlerAdded(ctx);
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
System.out.println("助手类移除");
super.handlerRemoved(ctx);
}
}
##五、启动主类 进行测试

后台完整输出

六、客户端线程模型
相比于服务端,客户端的线程模型简单一些,它的工作原理如下:

代码如下
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group) // 注册线程池
.channel(NioSocketChannel.class) // 使用NioSocketChannel来作为连接用的channel类
.remoteAddress(new InetSocketAddress(this.host, this.port)) // 绑定连接端口和host信息
.handler(new ChannelInitializer<SocketChannel>() { // 绑定连接初始化器
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//这里放入自定义助手类
ch.pipeline().addLast(new EchoClientHandler());
}
});
ChannelFuture cf = b.connect().sync(); // 异步连接服务器
cf.channel().closeFuture().sync(); // 异步等待关闭连接channel
} finally {
group.shutdownGracefully().sync(); // 释放线程池资源
}
}
客户端的开发步骤和服务端都差不多:
(1)、 初始化用于连接及I/O工作的"线程池";
(2)、 初始化`Bootstrap`实例, 此实例是netty客户端应用开发的入口;
(3)、 通过Bootstrap的group方法,设置(1)中初始化的"线程池";
(4)、 指定通道channel的类型,由于是客户端,故而是`NioSocketChannel`;
(5)、 设置SocketChannel的选项;
(6)、 设置SocketChannel的处理器, 其内部是实际业务开发的"主战场";
(7)、 连接指定的服务地址;
相比于服务端很明显的区别在于
(1),客户端只需要创建一个 EventLoopGroup,因为它不需要独立的线程去监听客户端连接,也没必要通过一个单独的客户端线程去连接服务端。Netty 是异步事件驱动的 NIO 框架,它的连接和所有 IO 操作都是异步的,因此不需要创建单独的连接线程。
(2)服务端引导类ServerBootstrap,而客户端引导是Bootstrap进行开发的,不过它们都需要通过group属性指定EventLoopGroup, 因为是开发NIO程序,所以我们选择NioEventLoopGroup。
如果一个人充满快乐,正面的思想,那么好的人事物就会和他共鸣,而且被他吸引过来。同样,一个人老带悲伤,倒霉的事情也会跟过来。
——在自己心情低落的时候,告诫自己不要把负能量带给别人。(大校11)
【netty】(2)---搭建一个简单服务器的更多相关文章
- 用nodejs搭建一个简单的服务器
使用nodejs搭建一个简单的服务器 nodejs优点:性能高(读写文件) 数据操作能力强 官网:www.nodejs.org 验证是否安装成功:cmd命令行中输入node -v 如果显示版本号表示安 ...
- 初学Node(六)搭建一个简单的服务器
搭建一个简单的服务器 通过下面的代码可以搭建一个简单的服务器: var http = require("http"); http.createServer(function(req ...
- 使用gitblit搭建一个简单的局域网服务器
使用gitblit搭建一个简单的局域网服务器 1.使用背景 现在很多使用github管理代码,但是github需要互联网的支持,而且私有的git库需要收费.有一些项目的代码不能外泄,所以,搭建一个局域 ...
- Golang学习-第二篇 搭建一个简单的Go Web服务器
序言 由于本人一直从事Web服务器端的程序开发,所以在学习Golang也想从Web这里开始学起,如果对Golang还不太清楚怎么搭建环境的朋友们可以参考我的上一篇文章 Golang的简单介绍及Wind ...
- 利用 nodeJS 搭建一个简单的Web服务器(转)
下面的代码演示如何利用 nodeJS 搭建一个简单的Web服务器: 1. 文件 WebServer.js: //-------------------------------------------- ...
- Linux中搭建一个ftp服务器详解
来源:Linux社区 作者:luzhi1024 详解Linux中搭建一个ftp服务器. ftp工作是会启动两个通道:控制通道 , 数据通道在ftp协议中,控制连接均是由客户端发起的,而数据连接有两种 ...
- Prism for WPF 搭建一个简单的模块化开发框架(五)添加聊天、消息模块
原文:Prism for WPF 搭建一个简单的模块化开发框架(五)添加聊天.消息模块 中秋节假期没事继续搞了搞 做了各聊天的模块,需要继续优化 第一步画页面 页面参考https://github.c ...
- 用express搭建一个简单的博客系统
转自:https://blog.csdn.net/qq_29721837/article/details/62055603 Express 简介 Express 是一个简洁而灵活的 node.js W ...
- 【Head First Servlets and JSP】笔记6:什么是响应首部 & 快速搭建一个简单的测试环境
搭建简单的测试环境 什么是响应首部 最简单的响应首部——Content-Type 设置响应首部 请求重定向与响应首部 在浏览器中查看Response Headers 1.先快速搭建一个简单的测试环境, ...
随机推荐
- 洛谷 p2678 跳石头 题解
一道裸的二分答案 如果不会分治的去找dalao吧,本蒟蒻只会二分 不知道二分答案的看这里 这位dalao解释的很详细其实只是随便找了一个 那里面貌似也有这个题的题解,但我还是要写(才不是应付老师) 关 ...
- jedis keys和scan操作
关于redis的keys命令的性能问题 KEYS pattern 查找所有符合给定模式 pattern 的 key . KEYS * 匹配数据库中所有 key . KEYS h?llo 匹配 hell ...
- vue基础5-生命周期
1.vue实例的生命周期 1.1.什么是生命周期? --从Vue实例创建.运行.销毁期间,总是伴随着各式各样的事件,这些事件,统称为生命周期! 1.2.生命周期钩子:就是生命周期事件的别名而已: ...
- 使用Cordova打包Vue项目
因为公司项目要求, 原本的vue移动端项目, 现在要求能使用定位, 调用摄像头等功能, 并且开发成混合APP. 一个小白的孤军奋战史, 记录一下, 以备后用.... 第一步: 安装cordova 在命 ...
- FW/IDS/IPS/WAF等安全设备部署方式及优缺点
现在市场上的主流网络安全产品可以分为以下几个大类:1.基础防火墙FW/NGFW类 主要是可实现基本包过滤策略的防火墙,这类是有硬件处理.软件处理等,其主要功能实现是限制对IP:port的访问.基本上的 ...
- 基本排序算法(Java)
基本排序算法 (Java) 经过几次笔试,发现自己的Java基础有些薄弱,基本的排序算法掌握的还不够熟练,需要多加学习总结. 1. 选择排序 思想: 给定一个整数数组,例 int[] a ={38,6 ...
- 1.3 正则表达式和python语言-1.3.6匹配多个字符串
1.3.6 匹配多个字符串(2018-05-08) 我们在正则表达式 bat|bet|bit 中使用了择一匹配(|)符号.如下为在 Python中使用正则表达式的方法. import re #bat| ...
- 关于最小生成树,拓扑排序、强连通分量、割点、2-SAT的一点笔记
关于最小生成树,拓扑排序.强连通分量.割点.2-SAT的一点笔记 前言:近期在复习这些东西,就xjb写一点吧.当然以前也写过,但这次偏重不太一样 MST 最小瓶颈路:u到v最大权值最小的路径.在最小生 ...
- 记录一次大量CLOSE_WAIT的情况
近期的项目中,有一个特殊的需求,对于每个客户端程序有若干个机构,对于每个机构有不同的客户端证书,程序间隔一段时间向服务端进行请求,根据请求的成功与否更新各机构的状态(如正常,证书未配置,证书过期等). ...
- String.matches()的用法
https://blog.csdn.net/victoryckl/article/details/6930409