Netty实现一个简单的http服务器

由于客户端是浏览器,所以只需要编写服务端

服务端:绑定10086端口

package com.jym.http;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * @program: NettyPro
 * @description: 服务端,简单实现http
 * @author: jym
 * @create: 2020/02/08
 */
public class TestSever {
    public static void main(String[] args) throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new TestSeverInitializer());

            ChannelFuture channelFuture = serverBootstrap.bind(10086).sync();

            channelFuture.addListener(new ChannelFutureListener() {
                public void operationComplete(ChannelFuture channelFuture)  {
                    if(channelFuture.isSuccess()){
                        System.out.println("监听端口 10086 成功");
                    } else {
                        System.out.println("监听端口失败");
                    }
                }
            });
            channelFuture.channel().closeFuture().sync();

        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }
}

自定义Initializer需要继承ChannelInitializer重写方法:

package com.jym.http;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;

/**
 * @program: NettyPro
 * @description:
 * @author: jym
 * @create: 2020/02/08
 */
public class TestSeverInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        // 向管道加入处理器
        //得到管道
        ChannelPipeline pipeline = socketChannel.pipeline();
        // 加入一个netty提供的httpServer codec
        // HttpServerCodec说明
        // 1.netty提供的一个http的编--解码器
        pipeline.addLast("JymHttpSeverCodec",new HttpServerCodec());
        // 增加自定义的处理器
        pipeline.addLast("JymHttpSeverHandler",new TestHttpSeverHandler());
    }

}

自定义handler,因为浏览器会发送两个请求,一个正常的请求,一个网页图标,要分开处理

package com.jym.http;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;

/**
 * @program: NettyPro
 * @description: 处理器,SimpleChannelInboundHandler说明:
 * 1.是ChannelInboundHandlerAdapter的子类
 * 2.HttpObject 客户端和服务器端相互通讯的数据被封装成 HttpObject
 * @author: jym
 * @create: 2020/02/08
 */
public class TestHttpSeverHandler extends SimpleChannelInboundHandler<HttpObject> {

    private static byte[] bytes;

    static {
        File file = new File("D:\\壁纸\\LOL\\1.jpg");
        FileInputStream fi = null;
        try {
            long fileSize = file.length();
            if (fileSize > Integer.MAX_VALUE) {
                throw new IOException(file.getName()+"file too big... ");
            }
            fi = new FileInputStream(file);
            bytes = new byte[(int) fileSize];
            int offset = 0;
            int numRead = 0;
            while (offset < bytes.length
                    && (numRead = fi.read(bytes, offset, bytes.length - offset)) >= 0) {
                offset += numRead;
            }
            // 确保所有数据均被读取
            if (offset != bytes.length) {
                throw new IOException("Could not completely read file "
                        + file.getName());
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fi!=null){
                try {
                    fi.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 读取客户端数据
     */
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, HttpObject httpObject) throws Exception {
        // 判断 httpObject 是不是一个HttpRequest请求
        if(httpObject instanceof HttpRequest){
            System.out.println("msg 类型:"+ httpObject.getClass());
            System.out.println("客户端地址:"+channelHandlerContext.channel().remoteAddress());

            HttpRequest request = (HttpRequest)httpObject;
            URI uri = new URI(request.uri());
            ByteBuf content = null;
            // 处理图标
            if("/favicon.ico".equals(uri.getPath())){
                content = Unpooled.copiedBuffer(bytes);
            } else {
                // 回复信息给浏览器
                content = Unpooled.copiedBuffer("hello,我是服务器", CharsetUtil.UTF_8);
            }
            // 构造一个 httpResponse
            DefaultFullHttpResponse response =
                    new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,content);
            // 设置响应头
            response.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/plain");
            response.headers().set(HttpHeaderNames.CONTENT_LENGTH,content.readableBytes());

            channelHandlerContext.writeAndFlush(response);
        }
    }

}

SimpleChannelInboundHandler说明:
1.是ChannelInboundHandlerAdapter的子类
2.HttpObject 客户端和服务器端相互通讯的数据被封装成 HttpObject

测试结果:

启动服务端后,访问10086端口:
在这里插入图片描述
图标与内容都可正常显示

学习年限不足,知识过浅,说的不对请见谅。

世界上有10种人,一种是懂二进制的,一种是不懂二进制的。

发布了71 篇原创文章 · 获赞 54 · 访问量 42万+

猜你喜欢

转载自blog.csdn.net/weixin_43326401/article/details/104229005