netty(四) --基于netty搭建简单的http服务器

简介

 HTTP是一个超文本传输协议,是一种建立在TCP传输协议之上的应用层协议。http是目前web的主流协议,本章讲述的是基于netty NIO开发的http服务端。本章项目源码地址:https://github.com/itwwj/netty-learn.git中的netty-day05-httpServer项目。

一、http协议简介

 HTTP是一个应用层的面向对象的协议,http协议的主要特点如下:

  • 支持Client/Server模式。
  • 简单, 客户端向服务端请求服务时,只需要指定URL和携带必要的参数或者请求体。
  • 灵活,HTTP允许传输任意类型的数据对象,传输的内容类型由HTTP消息头中的Content-Type加以标记。
  • 无状态,服务器不需要保存请求的先前信息所以应答较快,负载较轻。

1.1 HttpRequest

HttpRequest由三部分组成:

  • 请求行
  • 请求头
  • 请求体
     请求行以一个方法符开头,以空格分开,后面跟着请求的URI和协议的版本,格式为:Method Request-URI HTTP-Version CRLF。其中 Method表示请求方法,Request-URI是一个统一资源标识符,HTTP-Version表示请求的HTTP协议版本,CRLF表示回车和换行。请求方法:
请求方法介绍
GET请求获取Request-URI所标识的资源
POST在Request-URI所标识的资源后附加新的提交数据
HEAD请求获取由Request-URI所标识的资源的响应消息报头
PUT请求服务器储存一个资源,并用Request-URI作为其标识符
DELETE请求服务器删除Request-URI所标识的资源
TRACE请求服务器回送收到的请求消息,主要用于测试或诊断
CONNECT保留将来使用
OPTIONS请求查询服务器的性能,或者查询与资源相关的选项和需求

http部分请求头列表:

名称作用
Accept用于指定客户端接收哪些类型的消息。例如:Accept:image/gif
Accept-Charset用于指定客户端接收的字符集
Accept-Encoding用于指定客户端接受的内容编码
Accept-Language指定一种自然语言
Authorization主要用于证明客户端有权查看某个资源,当浏览器访问一个页面时,如果收到服务器的响代码为401,可以发送一个包含Authorization请求报头域的请求,要求服务器对其进行认证
Host发送请求时,该报头域是必需的,用于指定被请求资源的Internet主机和端口号,它通常是从HTTP URL中提取出来的
User-Agent允许客户端将它的操作系统、浏览器和其他属性告诉服务器
Content-length请求消息体的长度
Content-Type表示后面的文档属于什么MIME类型。servlet默认为text/plain,由于经常要设置Content-Type,因此HttpServletResPonse提供了一个专用的方法setContentType
Connection连接类型

netty中的HttpRequest类:
在这里插入图片描述

1.2 HttpResponse

HttpResponse也是由三个部分组成:

  • 状态行
  • 消息报头
  • 响应正文
     状态行的格式为:HTTP-Version Status-Code Reason-Phrase CRLF,其中HTTP-Version表示HTTP协议的版本,Status-Code 表示服务器返回的响应状态码。常见响应状态码:
状态码状态描述
200OK,客户端请求成功
400客户端请求语法错误,不能被服务器理解
401请求未经授权,这个状态码必须和WWW-Authenticate报头域一起使用
403服务器收到请求,但是拒绝提供服务
404请求资源不存在
500服务期发生不可预期的错误
503服务器当前不能处理客户端的请求,一段时间后可能恢复正常

netty 中的HttpResponse类:
在这里插入图片描述

二、服务端开发

启动类:

/**
 * netty服务端启动类
 *
 * @author jie
 */
public class NettyServer {
    /**
     * 设置端口号
     */
    private static int port = 1100;

    public static void main(String[] args) {
        //用于处理服务端接受客户端连接
        EventLoopGroup boosGroup = new NioEventLoopGroup();
        //用于SocketChannel的网络读写操作
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            //用于启动NIO服务端的辅助启动类,目的是降低服务端的开发复杂度
            ServerBootstrap b = new ServerBootstrap();
            b.group(boosGroup, workerGroup)
                    //对应JDK NIO类库中的ServerSocketChannel
                    .channel(NioServerSocketChannel.class)
                    //配置NioServerSocketChannel的TCP参数 指定处理客户端连接队列大小
                    //ChannelOption.SO_KEEPALIVE 一直保持连接活动状态
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    //绑定I/O的事件处理类
                    .childHandler(new MyServerChannelInitializer());
            //调用它的bind操作监听端口号,sync 等待异步操作执行完毕
            ChannelFuture f = b.bind(port).sync();
            //异步操作的通知回调
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //优雅的退出,释放线程池资源
            boosGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

事件处理类:

/**
 *
 * MyChannelInitializer的主要目的是为程序员提供了一个简单的工具,用于在某个Channel注册到EventLoop后,对这个Channel执行一些初始
 * 化操作。ChannelInitializer虽然会在一开始会被注册到Channel相关的pipeline里,但是在初始化完成之后,ChannelInitializer会将自己
 * 从pipeline中移除,不会影响后续的操作。
 * @author jie
 */
public class MyServerChannelInitializer extends ChannelInitializer<SocketChannel> {
    /**
     * 这个方法在Channel被注册到EventLoop的时候会被调用
     * @param socketChannel
     * @throws Exception
     */
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        // 数据解码操作
        socketChannel.pipeline().addLast(new HttpResponseEncoder());
        // 数据编码操作
        socketChannel.pipeline().addLast(new HttpRequestDecoder());

        socketChannel.pipeline().addLast(new MyServerHandler());
    }
}

事件操作类:

/**
 * 操作类
 *实现简单的http服务
 * @author jie
 */
public class MyServerHandler extends ChannelInboundHandlerAdapter {


    /**
     * 通道有消息触发
     *
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //请求参数
        //HttpRequest Http请求的第一个部分包含了HTTP的头部信息
        if (msg instanceof HttpRequest) {
            DefaultHttpRequest request = (DefaultHttpRequest) msg;
            System.out.println("URI:" + request.getUri());
            System.out.println(msg);
        }

        //HttpContent 包含了数据
        if (msg instanceof HttpContent) {
            LastHttpContent httpContent = (LastHttpContent) msg;
            ByteBuf byteData = httpContent.content();
            if (!(byteData instanceof EmptyByteBuf)) {
                //接收msg消息
                byte[] msgByte = new byte[byteData.readableBytes()];
                byteData.readBytes(msgByte);
                System.out.println(new String(msgByte, Charset.forName("UTF-8")));
            }
        }
        String sendMsg = "来自netty服务端的回复:";
        FullHttpResponse response = new DefaultFullHttpResponse(
                HttpVersion.HTTP_1_1,
                HttpResponseStatus.OK,
                Unpooled.wrappedBuffer(sendMsg.getBytes(Charset.forName("UTF-8"))));
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain;charset=UTF-8");
        response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
        response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
        ctx.write(response);
        ctx.flush();
    }

    /**
     * 当Channel上的一个读操作完成时被调用
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

    /**
     * 当连接发生异常时触发
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
        cause.printStackTrace();
    }
}

三、测试

在这里插入图片描述
至此使用netty开发一个简单的http接受和响应消息完成

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值