核心就是ChannelInitializer的实现使用http 消息解码器

package com.coremain.handler;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec; /**
* @description: Netty服务端回调处理 HTTP
* @author: GuoTong
* @createTime: 2023-05-16 22:05
* @since JDK 1.8 OR 11
**/
public class NettyServerHTTPHandler extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) {
socketChannel.pipeline()
// http 消息解码器
.addLast(new HttpServerCodec())
// http 消息合并(2048 为消息最大长度)
.addLast(new HttpObjectAggregator(2048))
// 自定义消息处理业务类
.addLast(new HttpMessageHandler());
// 此处还可以进行添加其他处理(例如ssl)等
}
}

核心2就是ChannelInboundHandlerAdapter的实现

package com.coremain.handler;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.coremain.netty.init.ServiceInit;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
import io.netty.handler.codec.http.multipart.InterfaceHttpData;
import io.netty.handler.codec.http.multipart.MemoryAttribute;
import io.netty.util.CharsetUtil; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; /**
* @description: HTTP消息处理器: 通道入站处理适配器。
* 整个的IO处理操作环节包括:
* 从通道读数据包、数据包解码、业务处理、目标数据编码、把数据包写到通道,然后由通道发送到对端,
* 入站处理,触发的方向为:自底向上,Netty的内部(如通道)到 ChannelInboundHandler入站处理器。
* 出站处理,触发的方向为:自顶向下,从ChannelOutboundHandler出站处理器 到Netty的内部(如通道)。
* 按照这种方向来分,前面数据包解码、业务处理两个环节——属于入站处理器 的工作;
* 后面目标数据编码、把数据包写到通道中两个环节——属于出站处理器的 工作
* @author: GuoTong
* @createTime: 2023-05-16 22:06
* @since JDK 1.8 OR 11
**/
@SuppressWarnings("all")
public class HttpMessageHandler extends ChannelInboundHandlerAdapter { private static final String CONTENT_TYPE = "Content-Type";
private static final String CONTENT_LENGTH = "Content-Length";
private static final String CONNECTION = "Connection";
private static final String KEEP_ALIVE = "keep-alive"; private static final String FORM_CONTENT_TYPE = "application/x-www-form-urlencoded";
private static final String JSON_CONTENT_TYPE = "application/json";
private static final String MULTIPART_CONTENT_TYPE = "multipart/form-data";
private static final String TEXT_CONTENT_TYPE = "text/xml"; /**
* Description: 当通道注册完成后,Netty会调用fireChannelRegistered, 触发通道注册事件。
* 通道会启动该入站操作的流水线处理,在通道注册过的入站处理器Handler的channelRegistered方法,会被调用到
*
* @param ctx
* @author: GuoTong
* @date: 2023-05-17 19:53:18
* @return:void
*/
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception { super.channelRegistered(ctx);
} /**
* Description: 通道未注册
*
* @param ctx
* @author: GuoTong
* @date: 2023-05-17 19:55:48
* @return:void
*/
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
super.channelUnregistered(ctx);
} /**
* Description: 当通道激活完成后,Netty会调用fireChannelActive, 触发通道激活事件。通道会启动该入站操作的流水线处理,
* 在通道注册过的入站处理器Handler的channelActive方法,会被调用到。
*
* @param ctx
* @author: GuoTong
* @date: 2023-05-17 19:56:37
* @return:void
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
} /**
* Description: 当连接被断开或者不可用,Netty会调用fireChannelInactive, 触发连接不可用事 件。通道会启动对应的流水线处理,
* 在通道注册过的入站处理器Handler的channelInactive方法,会被调用到。
*
* @param ctx
* @author: GuoTong
* @date: 2023-05-17 19:57:06
* @return:void
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception { super.channelInactive(ctx);
} /**
* Description: 当通道缓冲区读完,Netty会调用fireChannelReadComplete, 触发通道读完事 件。通道会启动该入站操作的流水线处理
* ,在通道注册过的入站处理器Handler的channelReadComplete方法,会被调用到。
*
* @param ctx
* @author: GuoTong
* @date: 2023-05-17 19:57:31
* @return:void
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush();
} /**
* Description: 当通道处理过程发生异常时,Netty会调用fireExceptionCaught,触发异常捕获事件。通道会启动异常捕获的流水线处理,
* 在通道注册过的处理器Handler的 exceptionCaught方法,会被调用到。
* 注意,这个方法是在通道处理器中ChannelHandler定义的方法,入站处理器、出站处理器接口都继承到了该方法。
*
* @param ctx
* @param cause
* @author: GuoTong
* @date: 2023-05-17 19:58:31
* @return:void
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace();
ctx.close();
} /**
* Description: 当通道缓冲区可读,Netty会调用fireChannelRead, 触发通道可读事件。通道会启动该入站操作的流水线处理,
* 在通道注册过的入站处理器Handler的channelRead方法,会被调用到。
*
* @param ctx
* @param msg
* @author: GuoTong
* @date: 2023-05-17 19:58:09
* @return:void
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// 仅处理http请求
if (msg instanceof FullHttpRequest) {
//客户端的请求对象
FullHttpRequest req = (FullHttpRequest) msg;
//新建一个返回消息的Json对象
String responseJson = "";
// 获取请求方式
HttpMethod method = req.method();
// 判断请求方式是GET还是POST
boolean isPost = method.equals(HttpMethod.POST);
//把客户端的请求数据格式化为Json对象
JSONObject requestJson = null;
try {
if (isPost) {
requestJson = getPostRequestParams(req);
} else {
requestJson = getUrlRequestParams(req);
}
} catch (Exception e) {
ResponseJson(ctx, req, "参数解析失败:" + e.getMessage());
return;
}
// 此处不进行请求方式区分,分局请求url 进行区分(url格式:/类标识/方法标识)
// 维护两个map, a: Map<类标识, Class(类对应的class)> b: Map<方法名, Class(方法参数对应的class)>
//获取客户端的URL
String uri = req.getUri();
String[] uris;
if (uri.contains("?")) {
uris = uri.substring(1, uri.indexOf("?")).split("/");
} else {
uris = uri.substring(1).split("/");
}
if (uris.length == 3) {
try {
Object result = dealService(requestJson, uris, isPost);
if (result instanceof String) {
responseJson = (String) result;
} else {
responseJson = JSON.toJSONString(result);
}
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
ResponseJson(ctx, req, "业务调用异常" + e.getMessage());
} catch (Exception e) {
ResponseJson(ctx, req, "业务调用异常" + e.getMessage());
}
} else {
ResponseJson(ctx, req, "访问路径不合法");
}
//向客户端发送结果
ResponseJson(ctx, req, responseJson);
}
} /**
* 处理真正业务
*
* @param requestJson 请求参数
* @param uris ([项目标识,类标识,方法标识])
* @return
*/
private Object dealService(JSONObject requestJson, String[] uris, boolean isPost) throws Exception {
// 获取项目名
String project_identity = uris[0];
// 获取类标识
String class_identity = uris[1];
// 获取方法标识
String method_identity = uris[2];
// 获取类class 以及 方法参数class
Map<Class, Map<String, Class>> classClassMap = ServiceInit.CLASS_MAPPING.get(class_identity).get(method_identity);
List<Map.Entry<Class, Map<String, Class>>> collect = classClassMap.entrySet().stream().collect(Collectors.toList());
Map.Entry<Class, Map<String, Class>> classEntry = collect.get(0);
// 业务类class
Class serviceClass = classEntry.getKey();
// 参数class
Map<String, Class> paramClasses = classEntry.getValue();
// 封装参数的值
Object[] params = new Object[paramClasses.size()];
// 反射
Class[] classes = new Class[params.length];
if (isPost && paramClasses.size() == 1) {
Class c = paramClasses.entrySet().iterator().next().getValue();
params[0] = JSON.parseObject(requestJson.toJSONString(), c);
classes[0] = c;
} else {
Object[] webSendParams = requestJson.values().toArray();
if (paramClasses.size() != webSendParams.length) {
throw new Exception("参数不匹配");
}
// 获取参数类型,解析拼装参数的值
int i = 0;
for (Map.Entry<String, Class> entry : paramClasses.entrySet()) {
String key = entry.getKey();
Class pClass = entry.getValue();
// 以此拼接传入参数
Object value = webSendParams[i];
// 判断参数类型,强转
if (pClass.equals(String.class)) {
params[i] = (String) value;
} else {
// 默认不是String就是用Integer接收
params[i] = Integer.parseInt(value.toString());
}
classes[i] = pClass;
i++;
}
}
// 业务类对象
Object service = serviceClass.getDeclaredConstructor().newInstance();
// 根据方法标识反射调用该方法
Method method = serviceClass.getDeclaredMethod(method_identity, classes);
// 这样设置完之后,在外部也是可以访问private的。
method.setAccessible(true);
Object result = method.invoke(service, params);
// 返回结果
return result;
} /**
* 响应HTTP的请求
*
* @param ctx
* @param req
* @param jsonStr
*/
private void ResponseJson(ChannelHandlerContext ctx, FullHttpRequest req, String jsonStr) {
byte[] jsonByteByte = (jsonStr + "\r\n").getBytes();
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(jsonByteByte));
String contentType = req.headers().get(CONTENT_TYPE);
switch (contentType) {
case FORM_CONTENT_TYPE:
// 最常见的 POST 提交数据的方式了。浏览器的原生 表单
response.headers().set(CONTENT_TYPE, FORM_CONTENT_TYPE);
break;
case JSON_CONTENT_TYPE:
// 告诉服务端消息主体是序列化后的 JSON 字符串
response.headers().set(CONTENT_TYPE, JSON_CONTENT_TYPE);
break;
case MULTIPART_CONTENT_TYPE:
// 常见的 POST 数据提交的方式。我们使用表单上传文件时,必须让 表单的 enctype 等于 multipart/form-data。
response.headers().set(CONTENT_TYPE, MULTIPART_CONTENT_TYPE);
break;
case TEXT_CONTENT_TYPE:
// 它是一种使用 HTTP 作为传输协议,XML 作为编码方式的远程调用规范
response.headers().set(CONTENT_TYPE, TEXT_CONTENT_TYPE);
break;
default:
response.headers().set(CONTENT_TYPE, "text/json");
}
response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes());
response.headers().set(CONNECTION, KEEP_ALIVE);
ctx.write(response);
ctx.flush();
ctx.close();
} /**
* 获取post请求的内容
*
* @param request
* @return
*/
private JSONObject getPostRequestParams(FullHttpRequest request) {
// 获取请求的 Content-Type 类型
String contentType = request.headers().get(CONTENT_TYPE);
// 获取报文信息
ByteBuf jsonBuf = request.content();
String jsonStr = jsonBuf.toString(CharsetUtil.UTF_8);
JSONObject json = JSONObject.of();
switch (contentType) {
case FORM_CONTENT_TYPE:
// 最常见的 POST 提交数据的方式了。浏览器的原生 表单
String[] keyvalues = jsonStr.split("&");
for (int i = 0; i < keyvalues.length; i++) {
// 放入键值对
json.put(keyvalues[i], keyvalues[i + 1]);
// 指针前进一格
i++;
}
break;
case JSON_CONTENT_TYPE:
// 告诉服务端消息主体是序列化后的 JSON 字符串
json = JSON.parseObject(jsonStr);
break;
case MULTIPART_CONTENT_TYPE:
// 常见的 POST 数据提交的方式。我们使用表单上传文件时,必须让 表单的 enctype 等于 multipart/form-data。
Map<String, String> form = getFormRequestParams(request);
json = (JSONObject) JSON.toJSON(form);
break;
case TEXT_CONTENT_TYPE:
// 它是一种使用 HTTP 作为传输协议,XML 作为编码方式的远程调用规范
break;
}
return json;
} /**
* 获取url参数
*
* @param request
* @return
*/
private JSONObject getUrlRequestParams(FullHttpRequest request) {
QueryStringDecoder decoder = new QueryStringDecoder(request.getUri());
Map<String, List<String>> parameters = decoder.parameters();
JSONObject jsonObject = new JSONObject();
Iterator<Map.Entry<String, List<String>>> iterator = parameters.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, List<String>> next = iterator.next();
List<String> value = next.getValue();
jsonObject.put(next.getKey(), value.size() > 1 ? value : next.getValue().get(0));
}
return jsonObject;
} /**
* 获取表单参数
*
* @param request
* @return
*/
private Map<String, String> getFormRequestParams(FullHttpRequest request) {
HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(new DefaultHttpDataFactory(false), request);
List<InterfaceHttpData> httpPostData = decoder.getBodyHttpDatas();
Map<String, String> params = new HashMap<>();
for (InterfaceHttpData data : httpPostData) {
if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.Attribute) {
MemoryAttribute attribute = (MemoryAttribute) data;
params.put(attribute.getName(), attribute.getValue());
}
}
return params;
} }

Netty集成HTTP的GET和POST通讯的更多相关文章

  1. netty 集成 wss 安全链接

    netty集成ssl完整参考指南(含完整源码) 虽然我们在内部rpc通信中使用的是基于认证和报文头加密的方式实现安全性,但是有些时候仍然需要使用SSL加密,可能是因为对接的三方系统需要,也可能是由于o ...

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

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

  3. 即时通信系统中实现全局系统通知,并与Web后台集成【附C#开源即时通讯系统(支持广域网)——QQ高仿版IM最新源码】

    像QQ这样的即时通信软件,时不时就会从桌面的右下角弹出一个小窗口,或是显示一个广告.或是一个新闻.或是一个公告等.在这里,我们将其统称为“全局系统通知”.很多使用C#开源即时通讯系统——GGTalk的 ...

  4. netty集成ssl完整参考指南(含完整源码)

    虽然我们在内部rpc通信中使用的是基于认证和报文头加密的方式实现安全性,但是有些时候仍然需要使用SSL加密,可能是因为对接的三方系统需要,也可能是由于open的考虑.中午特地测了下netty下集成ss ...

  5. netty集成springboot

    一 前言 springboot 如何集成netty实现mapper调用不为null的问题让好多读者都头疼过,知识追寻者发了一点时间做了个基本入门集成应用给读者们指明条正确的集成方式,我相信,只要你有n ...

  6. 3、netty第二个例子,使用netty建立客户端,与服务端通讯

    第一个例子中,建立了http的服务器端,可以直接使用curl命令,或者浏览器直接访问. 在第二个例子中,建立一个netty的客户端来主动发送请求,模拟浏览器发送请求. 这里先启动服务端,再启动客户端, ...

  7. Netty集成Protobuf

    一.创建Personproto.proto 创建Personproto.proto文件 syntax = "proto2"; package com.example.protobu ...

  8. NetCore Netty 框架 BT.Netty.RPC 系列随讲 二 WHO AM I 之 NETTY/NETTY 与 网络通讯 IO 模型之关系?

    一:NETTY 是什么? Netty 是什么?  这个问题其实百度上一搜一堆. 这是官方话的描述:Netty 是一个基于NIO的客户.服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个 ...

  9. 《连载 | 物联网框架ServerSuperIO教程》1.4种通讯模式机制。附小文:招.NET开发,结果他转JAVA了,一切都是为了生活

    参考文章: 1.SuperIO通讯框架介绍,含通信本质 2.C#跨平台物联网通讯框架ServerSuperIO(SSIO) 一.感慨 上大学的时候,没有学过C#,花了5块钱在地坛书市买了一本教程,也就 ...

  10. 通俗地讲,Netty 能做什么?

    https://www.zhihu.com/question/24322387/answer/78947405 作者:郭无心链接:https://www.zhihu.com/question/2432 ...

随机推荐

  1. 【tvm解析】PACKFUNC机制

    为实现多种语言支持,需要满足以下几点: 部署:编译结果可以从python/javascript/c++调用. Debug: 在python中定义一个函数,在编译函数中调用. 链接:编写驱动程序以调用设 ...

  2. 使用部分写时复制提升Lakehouse的 ACID Upserts性能

    使用部分写时复制提升Lakehouse的 ACID Upserts性能 译自:Fast Copy-On-Write within Apache Parquet for Data Lakehouse A ...

  3. Linux 一些常用命令

    Linux命令的分类 1.帮助命令(help) 2.常用系统工作命令 3.常用状态检测命令 4.工作目录切换命令 linux命令在线中文手册 Linux常用命令 echo命令 作用:在终端输出字符串或 ...

  4. .net 6 winform启动器:调用CMD命令行执行dotnet命令启动.net core程序并将控制台输出日志输出到winform textbox实现实时日志显示

    背景 历史遗留问题,生产车间运行的一个.net core signalr程序使用命令行程序启动,经常由于生产人员误操作将光标停留在控制台上导致程序假死,丢失部分测试数据,车间随便找了一台win10系统 ...

  5. 使用Locust进行性能测试

    当涉及到评估应用程序或服务的性能时,Locust是一个功能强大且易于使用的开源工具.本文将介绍Locust的基本概念和使用方法. 什么是Locust? Locust是一个用于编写.运行和分析负载测试的 ...

  6. C#解析匿名JSON数据

    C#解析匿名JSON数据 使用工具:Newtonsoft.Json 使用方式: //模拟数据 var jsonData = JsonConvert.SerializeObject(new { Name ...

  7. KVM "shutting down, reason=crashed" 问题处理

    打开debug日志抓取信息 2022-10-12 07:42:43.698+0000: 63115: debug : processMonitorEOFEvent:4814 : Monitor con ...

  8. 论文解读(APCA)《Adaptive prototype and consistency alignment for semi-supervised domain adaptation》

    [ Wechat:Y466551 | 付费咨询,非诚勿扰 ] 论文信息 论文标题:Adaptive prototype and consistency alignment for semi-super ...

  9. Blazor前后端框架Known-V1.2.10

    V1.2.10 Known是基于C#和Blazor开发的前后端分离快速开发框架,开箱即用,跨平台,一处代码,多处运行. Gitee: https://gitee.com/known/Known Git ...

  10. 利用pytorch准备数据集、构建与训练、保存与加载CNN模型

    本文的主要内容是利用pytorch框架与torchvision工具箱,进行准备数据集.构建CNN网络模型.训练模型.保存和加载自定义模型等工作.本文若有疏漏.需更正.改进的地方,望读者予以指正,如果本 ...