本篇主要介绍Netty基于HTTP协议的开发应用
一,概念介绍
HTTP协议是建立在TCP传输协议之上的应用层的面向对象的协议。
主要特点如下:
1,支持Client/Server模式;
2,简单。客户端向服务端请求服务时,只需指定服务URL,携带必要的请求参数或者消息体;
3,灵活。HTTP允许传输任意类型的数据对象,传输的内容类型由HTTP消息头中的Content-Type加以标记;
4,无状态。指协议对于事务处理没有记忆能力。
HTTP URL的格式如下:
http://host[":"port][abs_path]
http表示要通过HTTP协议来定位网络资源;host表示合法的Internet主机域名或者IP地址;port指定一个端口号,为空则使用默认端口80;abs_path指定请求资源的URI。
HTTP请求消息HttpRequest:
HTTP请求由三部分组成请求行,消息头,请求正文。
请求行格式:Method Request-URI HTTP-Version CRLF
Method表示请求方法,Request-URI表示统一资源标识符,HTTP-Version表示请求的HTTP协议版本,CRLF表示回车和换行。
请求方法:
消息头:
HTTP响应消息HttpResponse
处理完HTTP客户端的请求之后,HTTP服务端返回响应消息给客户端,HTTP响应也是由三个部分组成,分别是:状态行,消息报头,响应正文。
状态行格式:HTTP-Version Status-Code Reason-Phrase CRLF
Status-Code表示服务器返回的响应状态代码,Reason-Phrase表示服务器返回的错误信息。
状态代码由三个数字组成,有如下5中可能取值。
(1)1xx:指示信息。表示请求已接收,继续处理。
(2)2xx:成功。表示请求已被成功接收,理解,接受。
(3)3xx:重定向。要完成请求必须进行更进一步的操作。
(4)4xx:客户端错误。请求有语法错误或请求无法实现。
(5)5xx:服务端错误。服务器未能处理请求。
常用的响应报头:
Location:响应报头域用于重定向接收者到一个新的位置,Location响应报头域常用语更换域名的时候。
Server:响应报头域包含了服务器用来处理请求的软件信息,与User-Agent请求报头域是相对应的。
WWW-Authenticate:必须被包含在401未授权响应消息中,客户端收到401响应消息,并发送Authorization报头域请求服务器对其进行验证时,服务端响应报头就包含该报头域。
二,实例代码
由于Netty天生是异步事件驱动的框架,因此基于NIO TCP协议栈开发的HTTP协议栈也是异步非阻塞的。
Netty的HTTP协议栈无论在性能上还是可靠性上,都表现优异,非常适合在非Web容器的场景下应用。
实例场景如下:
文件服务器使用HTTP协议对外提供服务,当客户端通过浏览器访问文件服务器时,对访问路径进行检查,检查失败时返回HTTP 403错误,该页无法访问;如果校验通过,以链接的方式打开当前文件目录,每个目录或者文件都是个超链接,可以递归访问。
HTTP服务端开发:
package com.huawei.netty.http; import io.netty.bootstrap.ServerBootstrap; 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.NioServerSocketChannel; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpRequestDecoder; import io.netty.handler.codec.http.HttpResponseEncoder; import io.netty.handler.stream.ChunkedWriteHandler; /** * Created by liuzhengqiu on 2017/11/4. */ public class HttpFileServer { private static final String DEAFAULT_URL = "/src/main/java/com/huawei/netty"; public void run(final int port,final String url) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup,workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline() .addLast("http-decoder",new HttpRequestDecoder()) .addLast("http-aggregator",new HttpObjectAggregator(65536)) .addLast("http-encoder",new HttpResponseEncoder()) .addLast("http-chunked",new ChunkedWriteHandler()) .addLast("fileServerHanlder",new HttpFileServerHandler(url)); } }); ChannelFuture future = bootstrap.bind("127.0.0.1",port).sync(); System.out.println("HTTP文件目录服务器启动,网址是:http://127.0.0.1:"+port+url); future.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } public static void main(String[] args) throws Exception { new HttpFileServer().run(8080,DEAFAULT_URL); } }
向ChannelPipeline中添加了HTTP请求消息的解码器,随后又添加了HttpObjectAggregator解码器,它的作用是将多个消息转换为单一的FullHttpRequest或者FullHttpResponse,原因是HTTP解码器在每个HTTP消息中会生成多个消息对象。
ChunkedHandler的主要作用是支持异步发送大的码流,但不占用过多的内存,防止发生Java内存溢出错误。
package com.huawei.netty.http; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.*; import io.netty.handler.codec.http.*; import io.netty.handler.stream.ChunkedFile; import io.netty.util.CharsetUtil; import javax.activation.MimetypesFileTypeMap; import java.io.File; import java.io.FileNotFoundException; import java.io.RandomAccessFile; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.regex.Pattern; import static io.netty.handler.codec.http.HttpHeaders.Names.CONNECTION; import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE; import static io.netty.handler.codec.http.HttpHeaders.Names.LOCATION; import static io.netty.handler.codec.http.HttpHeaders.isKeepAlive; import static io.netty.handler.codec.http.HttpHeaders.setContentLength; import static io.netty.handler.codec.http.HttpResponseStatus.*; /** * Created by liuzhengqiu on 2017/11/4. */ public class HttpFileServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> { private final String url; private static final Pattern INSECURE_URI = Pattern.compile(".*[<>&\"].*"); private String sanitizeUri(String uri) { try { uri = URLDecoder.decode(uri,"UTF-8"); } catch (UnsupportedEncodingException e) { throw new Error(); } if (!uri.startsWith(url)) { return null; } if (!uri.startsWith("/")) { return null; } uri = uri.replace('/', File.separatorChar); if (uri.contains(File.separator + '.') || uri.contains('.'+File.separator)||uri.startsWith(".") || uri.endsWith(".") || INSECURE_URI.matcher(uri).matches()) { return null; } return System.getProperty("user.dir") + File.separator + uri; } public HttpFileServerHandler(String url) { this.url = url; } private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) { FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,status, Unpooled.copiedBuffer("Failure:"+status.toString()+"\r\n", CharsetUtil.UTF_8)); response.headers().set(CONTENT_TYPE,"text/plain;charset=UTF-8"); ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); } @Override protected void channelRead0(ChannelHandlerContext channelHandlerContext, FullHttpRequest fullHttpRequest) throws Exception { if (!fullHttpRequest.getDecoderResult().isSuccess()) { sendError(channelHandlerContext,BAD_REQUEST); return ; } if (fullHttpRequest.getMethod() != HttpMethod.GET) { sendError(channelHandlerContext,METHOD_NOT_ALLOWED); return ; } final String uri = fullHttpRequest.getUri(); final String path = sanitizeUri(uri); if (path == null) { sendError(channelHandlerContext,FORBIDDEN); return ; } File file = new File(path); if (file.isHidden() || !file.exists()) { sendError(channelHandlerContext,NOT_FOUND); return; } if (file.isDirectory()) { if(uri.endsWith("/")) { sendListing(channelHandlerContext,file); } else { sendRedirect(channelHandlerContext,uri + '/'); } return; } if (!file.isFile()) { sendError(channelHandlerContext,FORBIDDEN); return; } RandomAccessFile randomAccessFile = null; try { randomAccessFile = new RandomAccessFile(file,"r"); }catch (FileNotFoundException fnfe) { sendError(channelHandlerContext,NOT_FOUND); return; } long fileLength = randomAccessFile.length(); HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1,OK); setContentLength(response,fileLength); setContentTypeHeader(response,file); if (isKeepAlive(fullHttpRequest)) { response.headers().set(CONNECTION,HttpHeaders.Values.KEEP_ALIVE); } channelHandlerContext.write(response); ChannelFuture sendFileFuture; sendFileFuture = channelHandlerContext.write(new ChunkedFile(randomAccessFile,0,fileLength,8192), channelHandlerContext.newProgressivePromise()); sendFileFuture.addListener(new ChannelProgressiveFutureListener() { @Override public void operationProgressed(ChannelProgressiveFuture channelProgressiveFuture, long progress, long total) throws Exception { if (total < 0) { System.err.println("Transfer progress: "+progress); } else { System.err.println("Transfer progress:"+progress+"/"+total); } } @Override public void operationComplete(ChannelProgressiveFuture channelProgressiveFuture) throws Exception { System.out.println("Transfer complete."); } }); ChannelFuture lastContentFuture = channelHandlerContext.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); if (!isKeepAlive(fullHttpRequest)) { lastContentFuture.addListener(ChannelFutureListener.CLOSE); } } private static void setContentTypeHeader(HttpResponse response,File file) { MimetypesFileTypeMap mimetypesFileTypeMap = new MimetypesFileTypeMap(); response.headers().set(CONTENT_TYPE,mimetypesFileTypeMap.getContentType(file.getPath())); } private static final Pattern ALLOW_FILE_NAME = Pattern.compile("[A-Za-z0-9][-_A-Za-z0-9\\.]*"); private static void sendListing(ChannelHandlerContext ctx,File dir) { FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,OK); response.headers().set(CONTENT_TYPE,"text/html;charset=UTF-8"); StringBuilder buf = new StringBuilder(); String dirPath = dir.getPath(); buf.append("<!DOCTYPE html>\r\n"); buf.append("<html><head><title>"); buf.append(dirPath); buf.append("目录:"); buf.append("</title></head><body>\r\n"); buf.append("<h3>"); buf.append(dirPath).append(" 目录: "); buf.append("</h3>\r\n"); buf.append("<ul>"); buf.append("<li>链接:<a href=\"../\">..</a></li>\r\n"); for (File file : dir.listFiles()) { if (file.isHidden() || !file.canRead()) { continue; } String name = file.getName(); if (!ALLOW_FILE_NAME.matcher(name).matches()) { continue; } buf.append("<li>链接:<a href=\""); buf.append(name); buf.append("\">"); buf.append(name); buf.append("</a></li>\r\n"); } buf.append("</ul></body></html>\r\n"); ByteBuf buffer = Unpooled.copiedBuffer(buf,CharsetUtil.UTF_8); response.content().writeBytes(buffer); buffer.release(); ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); } private static void sendRedirect(ChannelHandlerContext ctx,String newUri) { FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,FOUND); response.headers().set(LOCATION,newUri); ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); } }
启动HTTP文件服务器,运行结果如下:
三,总结
主要介绍了Netty HTTP协议栈的入门级应用。目前最流行的是HTTP+XML开发或者Restful+JSON的形式。后续将进一步介绍
相关推荐
"基于Comsol的采空区阴燃现象研究:速度、氧气浓度、瓦斯浓度与温度分布的二维模型分析",comsol采空区阴燃。 速度,氧气浓度,瓦斯浓度及温度分布。 二维模型。 ,comsol; 采空区; 阴燃; 速度; 氧气浓度; 瓦斯浓度; 温度分布; 二维模型;,"COMSOL模拟采空区阴燃:速度、浓度与温度分布的二维模型研究"
安全驱动的边云数据协同策略研究.pdf
MATLAB代码实现电-气-热综合能源系统耦合优化调度模型:精细电网、气网与热网协同优化,保姆级注释参考文档详可查阅。,MATLAB代码:电-气-热综合能源系统耦合优化调度 关键词:综合能源系统 优化调度 电气热耦合 参考文档:自编文档,非常细致详细,可联系我查阅 仿真平台:MATLAB YALMIP+cplex gurobi 主要内容:代码主要做的是一个考虑电网、热网以及气网耦合调度的综合能源系统优化调度模型,考虑了电网与气网,电网与热网的耦合,算例系统中,电网部分为10机39节点的综合能源系统,气网部分为比利时20节点的配气网络,潮流部分电网是用了直流潮流,气网部分也进行了线性化的操作处理,代码质量非常高,保姆级的注释以及人性化的模块子程序,所有数据均有可靠来源 ,关键词:MATLAB代码; 电-气-热综合能源系统; 耦合优化调度; 电网; 热网; 气网; 潮流; 直流潮流; 线性化处理; 保姆级注释; 人性化模块子程序; 可靠数据来源。,MATLAB代码:电-气-热综合能源系统耦合优化调度模型(保姆级注释,数据来源可靠)
内容概要:本文详细探讨了人工智能(AI)对就业市场的深远影响及其发展趋势。首先介绍了到2027年,44%的工人核心技能将受技术变革尤其是AI影响的事实,并提及自动化可能取代部分工作的现象。其次指出虽然某些职位面临风险,但也带来了全新的职业机遇与现有角色改进的可能性,关键在于人类要学会借助AI释放自身潜力并培养软实力,以适应快速发展的科技需求。再者,强调终身学习理念下企业和教育培训须革新教学手段与评估机制,以便紧跟AI进化速率,为个体和社会持续注入新动力。最后提到了教育机构应当加快调整步伐以匹配技术变革的速度,并利用AI实现个性化的教育,进而提升学习者的适应能力和解决问题的能力。 适用人群:政策制定者、企业管理层、在职人员及教育工作者,还有广大学生群体均能从中获得启示。 使用场景及目标:面向关注未来职场动向及教育发展方向的专业人士,提供前瞻性思考角度,助力各界积极规划职业生涯路径或调整教育资源分配策略。 其他说明:本文综合多位行业领袖的观点展开讨论,旨在唤起社会各界共同思考AI带来的变革及对策,而非单方面渲染危机感。
2025最新空调与制冷作业考试题及答案.doc
2025最新初级电工证考试题及答案.docx
飞剪PLC控制系统——采用西门子S7-200SMART和触摸屏实现智能化操控及图纸详述,飞锯追剪程序,PLC和触摸屏采用西门子200smart,包含图纸,触摸屏程序和PLC程序。 ,核心关键词:飞锯追剪程序; 西门子200smart; PLC程序; 触摸屏程序; 图纸; 控制系统。,"西门子200smart飞锯追剪系统程序包:含图纸、PLC与触摸屏程序"
使用PyQt6制作的Python应用程序。
三相桥式整流电路双闭环控制策略:电压外环与电流内环协同优化研究,三相桥式整流电路双闭环控制 电流内环 电压外环(也有开环控制) 采用电压电流双闭环控制,在电压、电流控制电路中,电压单环控制易于设计和分析,但是响应速度慢,无限流功能。 而电流环能增强电路稳定性、响应速度快。 三相桥式全控整流电路由整流变压器、阴极相连接的晶闸管(VT1, VT3, VT5)、阳极相连接的晶闸管(VT4, VT6, VT2)、负载、触发器和同步环节组成(如图1),6个晶闸管依次相隔60°触发,将电源交流电整流为直流电。 matlab仿真模型(开闭环都有)控制效果良好,可写报告。 ,三相桥式整流电路;双闭环控制;电流内环;电压外环;开环控制;MATLAB仿真模型。,基于双闭环控制的电压电流三相整流技术分析与Matlab仿真实现
MATLAB四旋翼仿真PID控制:从入门到精通的手把手教学,含QAV方法、模型代码、Simulink布局思路及详细图文说明,MATLAB四旋翼仿真 PID控制,有完全对应的说明文档,专门为初级学习者提供。 不用问在不在,直接拿即可。 亮点: 拥有和模型完全对应的讲解文档,相当于手把手教学。 内容包括: 1.QAV详细方法 2.模型及代码 3.模型2(提供simulink排版布局思路) 4.相关图片 5.使用备注 ,核心关键词:MATLAB四旋翼仿真; PID控制; 完全对应说明文档; 初级学习者; QAV详细方法; 模型及代码; simulink排版布局思路; 相关图片; 使用备注。,"MATLAB四旋翼仿真教程:PID控制详解与手把手教学"
定子磁链控制下的直接转矩控制系统MATLAB仿真研究及结果分析报告,基于定子磁链控制的直接转矩控制系统 MATLAB SIMULINK仿真模型(2018b)及说明报告,仿真结果良好。 报告第一部分讨论异步电动机的理论基础和数学模型,第二部分介绍直接转矩控制的具体原理,第三部分对调速系统中所用到的脉宽调制技术CFPWM、SVPWM进行了介绍,第四部分介绍了MATLAB仿真模型的搭建过程,第五部分对仿真结果进行了展示及讨论。 ,关键词:定子磁链控制;直接转矩控制系统;MATLAB SIMULINK仿真模型;异步电动机理论基础;数学模型;直接转矩控制原理;脉宽调制技术CFPWM;SVPWM;仿真结果。,基于MATLAB的异步电机直接转矩控制仿真研究报告
2025中小学教师编制考试教育理论基础知识必刷题库及答案.pptx
Python游戏编程源码-糖果消消消.zip
三相PWM整流器双闭环控制:电压外环电流内环的SVPWM调制策略及其代码编写详解——动态稳态特性优越的技术参考。,三相PWM整流器双闭环控制,电压外环,电流内环,PLL。 采用SVPWM调制,代码编写。 动态和稳态特性较好,可提供参考资料 ,三相PWM整流器;双闭环控制;电压外环;电流内环;PLL调制;SVPWM调制;动态特性;稳态特性;参考资料,三相PWM整流器双闭环SVPWM调制策略:稳态与动态特性优化参考指南
永磁同步电机滑膜观测器参数识别与仿真研究:转动惯量、阻尼系数及负载转矩的Matlab Simulink仿真分析文章及文档说明,永磁同步电机 滑膜观测器参数识别Matlab simulink仿真 包括转动惯量 阻尼系数 负载转矩 波形很好 跟踪很稳 包含仿真文件说明文档以及文章 ,关键词:永磁同步电机;滑膜观测器;参数识别;Matlab simulink仿真;转动惯量;阻尼系数;负载转矩;波形质量;跟踪稳定性;仿真文件;说明文档;文章。,基于Matlab Simulink仿真的永磁同步电机滑膜观测器参数识别及性能分析
基于永磁涡流的电梯缓冲结构设计.pdf
Python自动化办公源码-28 Python爬虫爬取网站的指定文章
MATLAB下的安全强化学习:利用Constraint Enforcement块训练代理实现目标接近任务,MATLAB代码:安全 强化学习 关键词:safe RL 仿真平台:MATLAB 主要内容:此代码展示了如何使用 Constraint Enforcement 块来训练强化学习 (RL) 代理。 此块计算最接近受约束和动作边界的代理输出的动作的修改控制动作。 训练强化学习代理需要 Reinforcement Learning Toolbox 。 在此示例中,代理的目标是使绿球尽可能靠近红球不断变化的目标位置。 具体步骤为创建用于收集数据的环境和代理,学习约束函数,使用约束强制训练代理,在没有约束执行的情况下训练代理。 ,核心关键词:safe RL; MATLAB代码; Constraint Enforcement 块; 强化学习代理; 绿球; 红球目标位置; 数据收集环境; 约束函数; 约束强制训练; 无约束执行训练。,MATLAB中安全强化学习训练的约束强化代理实现
基于EtherCAT总线网络的锂电池激光制片机控制系统,融合欧姆龙NX系列与威伦通触摸屏的智能制造方案。,锂电池激光模切机 欧姆龙NX1P2-1140DT,威伦通触摸屏,搭载从机扩展机架控制,I输入输出IO模块模拟量模块读取控制卷径计算 汇川IS620N总线伺服驱动器7轴控制,总线纠偏器控制 全自动锂电池激光制片机,整机采用EtherCAT总线网络节点控制, 伺服凸轮同步运动,主轴虚轴控制应用,卷径计算,速度计算,放卷张力控制。 触摸屏设计伺服驱动器报警代码,MC总线报警代码,欧姆龙伺服报警代码 张力摆臂控制,PID控制,等等 触摸屏产量统计,触摸屏故障统计,触摸屏与PLC对接信息交互,触摸屏多账户使用,多产品配方程序,优秀的触摸屏模板。 NX在收放卷控制的设计 欧姆龙NX系列实际项目程序+威纶触摸屏程序+新能源锂电设备 涵盖威纶通人机,故障记录功能,st+梯形图+FB块,注释齐全。 ,"新能源锂电池激光模切机:欧姆龙NX与威纶通触摸屏的智能控制与信息交互系统"
2025装载机理论考试试题库(含答案).pptx