Netty Http协议栈开发(客户端&服务端)

协议栈 是指网络中各层协议的总和, 反应了一个网络中文件传输的过程: 由上层协议到底层协议,再由底层协议到上层协议.

一.Netty Http+Json 协议栈开发

1. 构造请求信息,编码为http+json
    Netty的http协议栈提供了构造http请求消息的相关接口
2. 客户端发起http请求
3. 服务端对客户端的请求进行解码成Javabean
4. 服务端构造应答消息并编码,通过http+json返回给客户端
5. 客户端对响应的消息解码成Javabean

HttpJsonRequestEncoder: 请求消息编码类
1.首先调用父类的encode(),将HttpJsonRequest通过序列化成xml字符串,然后封装成netty的byteBuf
2.判断,如果业务侧自定义和定制了消息头,则使用业务侧设置的http消息头,如果没有,则构造新的http消息头
3.构造和设置默认的http消息头
4.由于请求消息消息体不为空,也没有使用chunk方式,所以在http消息头中设置消息体的长度,然后加入out中
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;
	}
}
HttpJsonRequest: 请求消息
包含两个成员变量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;
    }

}
 
    
AbstractHttpJsonDecoder: 父类
从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;
	}
}
HttpJsonResponse: 响应消息
包含两个成员变量 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;
	}
}
HttpJsonResponseEncoder: 响应消息编码类
判断,如果已经构造了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);
    }

}
 
    
HttpJsonResponseDecoder: 响应消息编码类
通过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编码器用于响应消息的编码
HttpJsonServerHandler:
通过channelRead()的方法接收HttpJsonRequest(已经解码后的消息),获取请求消息对象,然后进行业务处理,最后发送响应消息,发送完成之后主动关闭http连接















你可能感兴趣的:(Android,#,Basic)