package com.tomorrow_p.netty5_http_file_p.LAN.http;
import android.util.Log;
import java.net.InetAddress;
import java.util.List;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpVersion;
public class HttpJsonRequestEncoder extends AbsHttpJsonEncoder {
@Override
protected void encode(ChannelHandlerContext ctx, HttpJsonRequest message, List out) throws Exception {
Log.d("Encode", "HttpJsonRequestEncoder");
ByteBuf body = encode(ctx, message.getBody());
FullHttpRequest request = message.getRequest();
if (request == null) {
request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/do", body);
HttpHeaders headers = request.headers();
headers.set(HttpHeaders.Names.HOST, InetAddress.getLocalHost().getHostAddress());
headers.set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
headers.set(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP.toString() + "," + HttpHeaders.Values.DEFLATE.toString());
}
HttpHeaders.setContentLength(request, body.readableBytes());
out.add(request);
}
}
AbstractHttpJsonEncoder: 父类
完成http请求消息的编码
package com.tomorrow_p.netty5_http_file_p.LAN.http;
import android.util.Log;
import com.google.gson.Gson;
import java.nio.charset.Charset;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
public abstract class AbsHttpJsonEncoder extends MessageToMessageEncoder {
private final static String CHARSET_NAME = "UTF-8";
private static Charset UTF_8 = Charset.forName(CHARSET_NAME);
protected ByteBuf encode(ChannelHandlerContext ctx, Object body) throws Exception {
String jsonStr = new Gson().toJson(body);
ByteBuf encodeBuf = Unpooled.copiedBuffer(jsonStr, UTF_8);
Log.d("Encode", jsonStr);
return encodeBuf;
}
}
包含两个成员变量FullHttpRequest和Object,用于实现和协议栈之间的解耦
package com.tomorrow_p.netty5_http_file_p.LAN.http;
import io.netty.handler.codec.http.FullHttpRequest;
public class HttpJsonRequest {
private FullHttpRequest request;
private Object body;
public HttpJsonRequest(FullHttpRequest request, Object body) {
super();
this.request = request;
this.body = body;
}
public FullHttpRequest getRequest() {
return request;
}
public void setRequest(FullHttpRequest request) {
this.request = request;
}
public Object getBody() {
return body;
}
public void setBody(Object body) {
this.body = body;
}
}
HttpJsonRequestDecoder: 请求消息解码类
收到请求消息后从http消息体重获取请求码流,然后反序列化,获得对象,对结果进行封装
package com.tomorrow_p.netty5_http_file_p.LAN.http;
import android.util.Log;
import com.tomorrow_p.netty5_http_file_p.LAN.bean.JsonRequest;
import com.tomorrow_p.netty5_http_file_p.LAN.utils.UrlDecode;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
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.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.CharsetUtil;
public class HttpJsonRequestDecoder extends AbsHttpJsonDecoder {
private static final String TAG = "HRDecoder";
public HttpJsonRequestDecoder(Class> clazz) {
super(clazz);
}
@Override
protected void decode(ChannelHandlerContext ctx, FullHttpRequest request, List out) throws Exception {
HttpJsonRequest req = null;
if (!request.getDecoderResult().isSuccess()) {
sendError(ctx, HttpResponseStatus.BAD_REQUEST);
return;
}
try {
req = new HttpJsonRequest(request, FullHttpRequestDecode(request));
} catch (Exception e) {
if (e.getMessage() != null) {
Log.e(TAG, "uriDecode" + e.getMessage());
req = new HttpJsonRequest(request, e.getMessage());
} else {
req = new HttpJsonRequest(request, e.getCause());
}
}
out.add(req);
}
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(HttpHeaders.Names.CONTENT_TYPE, "text/plain; charset=UTF-8");
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
private Object FullHttpRequestDecode(FullHttpRequest request) {
Object jsonMsg = null;
try {
String operation = "";
String classification = "";
Map map = new HashMap();
String root = null;
String uri = request.getUri();
if (uri.indexOf("?") < 0) {
root = uri;
} else {
root = uri.split("[?]")[0];
}
String[] roots = root.split("[/]");
if (roots.length > 1) {
operation = roots[1].replaceAll("[/]", "");
}
if (roots.length > 2) {
classification = roots[2].replaceAll("[/]", "");
}
if (request.getMethod() == HttpMethod.GET) {
UrlDecode.parseParameters(map, uri, "UTF-8");
}
if (request.getMethod() == HttpMethod.POST) {
UrlDecode.parseParameters(request, map);
}
if (roots.length > 1) {
jsonMsg = new JsonRequest();
((JsonRequest) jsonMsg).setOperation(operation);
((JsonRequest) jsonMsg).setClassification(classification);
((JsonRequest) jsonMsg).setType(map.get("type"));
((JsonRequest) jsonMsg).setUuid(map.get("uuid"));
((JsonRequest) jsonMsg).setVersion(map.get("version"));
if (map.get("data") != null) {
if (request.getMethod() == HttpMethod.GET) {
((JsonRequest) jsonMsg).setData(URLDecoder.decode(map.get("data"), "utf-8"));
} else {
((JsonRequest) jsonMsg).setData(map.get("data"));
}
}
} else {
jsonMsg = new String(map.get("data"));
}
} catch (Exception e) {
jsonMsg = new String(e.getCause().toString());
}
return jsonMsg;
}
}
AbstractHttpJson Decoder: 父类
从http消息体重获取请求码流,然后将json转换成javabean;根据码流开关决定是否打印消息体码流(增加开关往往是为了方便问题定位)
如果解码异常,如果没有关闭StringReader,则关闭输入流并通知jvm垃圾回收
package com.tomorrow_p.netty5_http_file_p.LAN.http;
import android.util.Log;
import com.google.gson.Gson;
import java.nio.charset.Charset;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
public abstract class AbsHttpJsonDecoder extends MessageToMessageDecoder {
private final static String CHARSET_NAME = "UTF-8";
private static Charset UTF_8 = Charset.forName(CHARSET_NAME);
private Class> clazz;
protected AbsHttpJsonDecoder(Class> clazz) {
this.clazz = clazz;
}
protected Object decode(ChannelHandlerContext cxt, ByteBuf body) throws Exception {
String content = body.toString(UTF_8);
Log.e("content: ", content);
Object result = null;
try {
result = new Gson().fromJson(content, clazz);
Log.d("Decode", content);
} catch (Exception e) {
Log.e("Decode", e.getMessage());
throw e;
}
return result;
}
}
包含两个成员变量
FullHttpRequest和Object,object是需要发送应答的对象
package com.tomorrow_p.netty5_http_file_p.LAN.http;
import io.netty.handler.codec.http.FullHttpResponse;
public class HttpJsonResponse {
private FullHttpResponse httpResponse;
private Object result;
public HttpJsonResponse(FullHttpResponse httpResponse, Object result) {
super();
this.httpResponse = httpResponse;
this.result = result;
}
public FullHttpResponse getHttpResponse() {
return httpResponse;
}
public void setHttpResponse(FullHttpResponse httpResponse) {
this.httpResponse = httpResponse;
}
public Object getResult() {
return result;
}
public void setResult(Object result) {
this.result = result;
}
}
HttpJson ResponseEncoder: 响应消息编码类
判断,如果已经构造了http响应消息,则利用已有应答消息重新复制一个新的http响应消息(不能重用自定响应消息的主要原因是netty的 DefaultFullHttpResponse没有提供动态设置消息体content的接口,所以有在第一次构造的时候设置内容 )
package com.tomorrow_p.netty5_http_file_p.LAN.http;
import java.util.List;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
public class HttpJsonResponseEncoder extends AbsHttpJsonEncoder {
@Override
protected void encode(ChannelHandlerContext ctx, HttpJsonResponse message,
List out) throws Exception {
ByteBuf body = encode(ctx, message.getResult());
FullHttpResponse response = message.getHttpResponse();
if (response == null) {
response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, body);
} else {
response = new DefaultFullHttpResponse(message.getHttpResponse().getProtocolVersion(), message.getHttpResponse().getStatus(), body);
}
response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/html;charset=UTF-8");
HttpHeaders.setContentLength(response, body.readableBytes());
out.add(response);
}
}
HttpJson ResponseDecoder: 响应消息编码类
通过DefaultFullHttpResponse和http响应消息反序列化后的对象构造 HttpJsonResponse,并添加到解码结果列表中
package com.tomorrow_p.netty5_http_file_p.LAN.http;
import java.util.List;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.FullHttpResponse;
public class HttpJsonResponseDecoder extends AbsHttpJsonDecoder {
protected HttpJsonResponseDecoder(Class> clazz) {
super(clazz);
}
@Override
protected void decode(ChannelHandlerContext ctx, FullHttpResponse response, List out) throws Exception {
HttpJsonResponse res = new HttpJsonResponse(response, super.decode(ctx, response.content()));
out.add(res);
}
}
客户端开发:
1. 发起http连接请求
2. 构造请求消息,发送给服务端
3. 接收http服务端的响应消息,将json响应消息反序列化成Javabean
4.关闭http连接
服务端开发:
1. 接收http客户端的连接
2. 接收http客户端的请求消息,并解码成javaBean
3. 对业务对象进行处理,构造响应消息返回
4. 通过http+json格式返回响应消息
5. 主动关闭http连接
HttpJsonServer:
ch.pipeline().addLast("http-decoder" , new HttpRequestDecoder()); ch.pipeline().addLast("http-aggregator" , new HttpObjectAggregator(65536 ));
用于绑定http请求消息解码器
ch.pipeline().addLast("json-decoder" , new HttpJsonRequestDecoder(JsonRequest.class ));
自定义HttpJsonRequestDecoder添加到http解码器之后
ch .pipeline().addLast("http-encoder" , new HttpResponseEncoder());
添加自定义的HttpResponseEncoder编码器用于响应消息的编码
通过channelRead()的方法接收 HttpJsonRequest(已经解码后的消息), 获取请求消息对象,然后进行业务处理,最后发送响应消息,发送完成之后主动关闭http连接