Netty在Android开发中的应用实战系列(五)——— 创建Web服务 | 作为HTTP服务器

阅读本文建议从第一篇开始往后看

本系列文章

不得不感叹Netty的强大除了可以处理Socket的需求,竟然也还可以创建Web服务让Android充当一个Web服务器处理GETPOST等等请求…

一、创建Http服务

public class HttpServer {
    private static final String TAG = "HttpServer";
    //服务开启在的端口
    public static final int PORT = 7020;

    public void startHttpServer() {
        try {
            EventLoopGroup bossGroup = new NioEventLoopGroup();
            EventLoopGroup workerGroup = new NioEventLoopGroup();
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            // http服务器端对request解码
                            pipeline.addLast(new HttpRequestDecoder());
                            // http服务器端对response编码
                            pipeline.addLast(new HttpResponseEncoder());
                            // 在处理POST消息体时需要加上
                            pipeline.addLast(new HttpObjectAggregator(65536));
                            // 处理发起的请求
                            pipeline.addLast(new HttpServerHandler());
                        }
                    });
            //绑定服务在7020端口上
            b.bind(new InetSocketAddress(PORT)).sync();
            Log.d(TAG, "HTTP服务启动成功 PORT=" + PORT);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  • 启动HTTP服务
new HttpServer().startHttpServer();

与我们之前写的连接Socket代码可以发现添加的DecoderEncoder不一致;这里是用的是Netty封装好的专门针对HTTP请求的解码器和编码器

  • 运行的效果
    在这里插入图片描述

二、在HttpServerHandler中处理收到的HTTP请求

public class HttpServerHandler extends ChannelInboundHandlerAdapter {
    private static final String TAG = "HttpServerHandler";

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (!(msg instanceof FullHttpRequest)) {
            Log.e(TAG, "未知请求:" + msg.toString());
            return;
        }
        //获取请求的信息
        FullHttpRequest httpRequest = (FullHttpRequest) msg;
        String path = httpRequest.uri();
        HttpMethod method = httpRequest.method();
        
        Log.d(TAG, "==================接收到了请求==================");
        Log.d(TAG, "route = " + route);
        Log.d(TAG, "method = " + method);
    }
}
  • 这里我是把项目跑在Android模拟器上的,所以需要在控制台输入如下命令做一个端口转发,这样在我们的电脑浏览器就可以使用localhost:7020就可以访问到HTTP服务了
//7020 就是你要转发的端口
adb forward tcp:7020 tcp:7020
  • 在浏览器输入localhost:7020/test来看下效果
    在这里插入图片描述
  • 上面我们只接收了请求但是没有对请求进行返回结果,这也就导致请求一直无法完成一直处与请求中状态
    在这里插入图片描述

三、响应HTTP请求

  • 只需要向连接中写入FullHttpResponse即可,如下代码
ByteBuf byteBuf = Unpooled.copiedBuffer(Result.ok("请求成功").getBytes());
response(ctx, "text/json;charset=UTF-8", byteBuf, HttpResponseStatus.OK);
/**
 * 响应请求结果
 *
 * @param ctx     	  返回
 * @param contentType 响应类型
 * @param content 	  消息
 * @param status      状态
 */
private void response(ChannelHandlerContext ctx, String contentType, ByteBuf content, HttpResponseStatus status) {
    FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, content);
    response.headers().set(HttpHeaderNames.CONTENT_TYPE, contentType);
    ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}

到这一个基本的HTTP服务就已经跑起来了,剩下的就是解析发起的参数和处理请求了

四、下面给出一些示例,展示如何获取请求的参数和响应json数据或者响应图片数据

  • HttpServerHandler类
public class HttpServerHandler extends ChannelInboundHandlerAdapter {
    private static final String TAG = "HttpServerHandler";

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (!(msg instanceof FullHttpRequest)) {
            Log.e(TAG, "未知请求:" + msg.toString());
            return;
        }
        FullHttpRequest httpRequest = (FullHttpRequest) msg;
        String path = httpRequest.uri();
        HttpMethod method = httpRequest.method();

        String route = parseRoute(path);
        Map<String, Object> params = new HashMap<>();
        if (method == HttpMethod.GET) {
            parseGetParams(params, path);
        } else if (method == HttpMethod.POST) {
            parsePostParams(params, httpRequest);
        } else {
            //错误的请求方式
            ByteBuf byteBuf = Unpooled.copiedBuffer(Result.error("不支持的请求方式").getBytes());
            response(ctx, "text/json;charset=UTF-8", byteBuf, HttpResponseStatus.BAD_REQUEST);
        }
        Log.d(TAG, "==================接收到了请求==================");
        Log.d(TAG, "route = " + route);
        Log.d(TAG, "method = " + method);
        Log.d(TAG, "params = " + params.toString());

        handlerRequest(ctx, route, params);
    }

    /**
     * 处理每个请求
     */
    private void handlerRequest(ChannelHandlerContext ctx, String route, Map<String, Object> params) throws Exception {
        switch (route) {
            case "login":
                ByteBuf login;
                if ("admin".equals(params.get("name")) && "123".equals(params.get("psd"))) {
                    login = Unpooled.copiedBuffer(Result.ok("登录成功").getBytes());
                } else {
                    login = Unpooled.copiedBuffer(Result.error("登录失败").getBytes());
                }
                response(ctx, "text/json;charset=UTF-8", login, HttpResponseStatus.OK);
                break;
            case "getImage":
                //返回一张图片
                ByteBuf image = getImage(new File("/storage/emulated/0/Android/data/com.azhon.nettyhttp/cache/test.jpg"));
                response(ctx, "image/jpg", image, HttpResponseStatus.OK);
                break;
            case "json":
    			ByteBuf json = Unpooled.copiedBuffer(Result.ok("测试post请求成功").getBytes());
    			response(ctx, "text/json;charset=UTF-8", json, HttpResponseStatus.OK);
    			break;
            default:
                ByteBuf error = Unpooled.copiedBuffer(Result.error("未实现的请求地址").getBytes());
                response(ctx, "text/json;charset=UTF-8", error, HttpResponseStatus.BAD_REQUEST);
                break;
        }
    }

    /**
     * 解析Get请求参数
     */
    private void parseGetParams(Map<String, Object> params, String path) {
        //拼接成全路径好取参数
        Uri uri = Uri.parse("http://127.0.0.1" + path);
        Set<String> names = uri.getQueryParameterNames();
        Iterator<String> iterator = names.iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            params.put(key, uri.getQueryParameter(key));
        }
    }

    /**
     * 解析Post请求参数,以提交的body为json为例
     */
    private void parsePostParams(Map<String, Object> params, FullHttpRequest httpRequest) throws JSONException {
        ByteBuf content = httpRequest.content();
        String body = content.toString(CharsetUtil.UTF_8);
        JSONObject object = new JSONObject(body);
        Iterator<String> keys = object.keys();
        while (keys.hasNext()) {
            String key = keys.next();
            params.put(key, object.opt(key));
        }
    }

    /**
     * 解析调用的接口(路由地址)
     */
    private String parseRoute(String path) {
        if (path.contains("?")) {
            String uri = path.split("\\?")[0];
            return uri.substring(1);
        } else {
            return path.substring(1);
        }
    }


    /**
     * 返回图片
     */
    private ByteBuf getImage(File file) throws Exception {
        ByteBuf byteBuf = Unpooled.buffer();
        FileInputStream stream = new FileInputStream(file);
        int len;
        byte[] buff = new byte[1024];
        while ((len = stream.read(buff)) != -1) {
            byteBuf.writeBytes(buff, 0, len);
        }
        return byteBuf;
    }

    /**
     * 响应请求结果
     *
     * @param ctx         返回
     * @param contentType 响应类型
     * @param content     消息
     * @param status      状态
     */
    private void response(ChannelHandlerContext ctx, String contentType, ByteBuf content, HttpResponseStatus status) {
        FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, content);
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, contentType);
        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    }
}
  • Result类,返回的json数据工具类
public class Result {
    /**
     * 响应请求c成功
     */
    public static String ok(String msg) {
        JSONObject object = new JSONObject();
        try {
            object.put("code", 100);
            object.put("msg", msg);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return object.toString();
    }

    /**
     * 响应请求失败
     */
    public static String error(String msg) {
        JSONObject object = new JSONObject();
        try {
            object.put("code", 101);
            object.put("msg", msg);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return object.toString();
    }
}

五、上面使用到的测试接口地址

GET		http://localhost:7020/login?name=admin&psd=123  登录
GET		http://localhost:7020/getImage					获取图片
POST	http://localhost:7020/json						提交json数据
  • POST方式这里使用postman来模拟的,如下:
    在这里插入图片描述

六、运行效果

在这里插入图片描述

总的来说使用Netty创建Http服务还是很easy的,有兴趣的同学可以尝试下;代码都已经贴在文章中了就不上传Demo了(上传Demo还要下载积分emmmm…)

Code-Porter CSDN认证博客专家 代码界的扛把子
我从事多年Android软件开发及物联网方面开发,熟悉Java,Android,前端、数据库开发;对技术要求苛刻、热爱分享、热爱新技术
©️2020 CSDN 皮肤主题: 像素格子 设计师:CSDN官方博客 返回首页
实付 5.20元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值