Netty处理HTTP请求和响应

概述

主要讲述Netty处理HTTP请求和响应和需要注意的事项

Neey作为HTTP服务器

1. 编写服务器的启动程序
/**
 * 服务端的启动代码,重点在HttpServerInitializer这个类中,这里指定了pipeline的处理器
 */
public class HttpServer {
    private int port ;

    public HttpServer(int port){
        this.port = port;
    }

    public void start() throws Exception{
        ServerBootstrap bootstrap = new ServerBootstrap();
        EventLoopGroup boss = new NioEventLoopGroup();
        EventLoopGroup work = new NioEventLoopGroup();
        bootstrap.group(boss,work)
                .handler(new LoggingHandler(LogLevel.DEBUG))
                .channel(NioServerSocketChannel.class)
                .childHandler(new HttpServerInitializer());
        ChannelFuture f = bootstrap.bind(new InetSocketAddress(port)).sync();
        System.out.println(" server start up on port : " + port);
        f.channel().closeFuture().sync();
    }
}
2. 配置服务端的pipeline处理器
public class HttpServerInitializer extends ChannelInitializer<SocketChannel>{
    @Override
    protected void initChannel(SocketChannel channel) throws Exception {
        ChannelPipeline pipeline = channel.pipeline();
        pipeline.addLast(new HttpServerCodec());// http 编解码
        pipeline.addLast("httpAggregator",new HttpObjectAggregator(512*1024)); // http 消息聚合器512*1024为接收的最大contentlength
        pipeline.addLast(new HttpRequestHandler());// 请求处理器

    }
}
3. 编写请求处理器的业务
public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
        if (is100ContinueExpected(req)) {
            // 检测 100 Continue,是否同意接收将要发送过来的实体
            ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,HttpResponseStatus.CONTINUE));
        }
        // 因为经过HttpServerCodec处理器的处理后消息被封装为FullHttpRequest对象
		// 获取请求的uri
        JsonObject json = new JsonObject();
        json.put("method", req.method().name()); // 获取请求方法
        json.put("uri", req.uri()); // 获取请求地址
        // 创建完整的响应对象
        FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,HttpResponseStatus.OK,Unpooled.copiedBuffer(json.toJsonString(), CharsetUtil.UTF_8));
       // 设置头信息
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json; charset=UTF-8");
       // 响应写回给客户端,并在协会后断开这个连接
        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    }
}
4. 启动服务器
public class Application {
    public static void main(String[] args) throws Exception{
        HttpServer server = new HttpServer(8081);// 8081为启动端口
        server.start();
    }
}

Netty作为客户端

1. 编写http客户端对象
// 客户端对象,这里处理服务的启动配置和处理器配置
public class HttpClient {
	public static void start(String host,int port){
	    // 配置一个事件循环组和一个启动器
		EventLoopGroup group = new NioEventLoopGroup();
		Bootstrap bootstrap = new Bootstrap();
		try {
			bootstrap.group(group)
					 .channel(NioSocketChannel.class)
					 .option(ChannelOption.SO_KEEPALIVE, true)
					 .handler(new ChannelInitializer<Channel>() {
						@Override
						protected void initChannel(Channel channel) throws Exception {
							channel.pipeline().addLast(new HttpClientCodec()); // http客户端编解码器
							channel.pipeline().addLast(new HttpObjectAggregator(65536)); // http消息聚合器
							channel.pipeline().addLast(new HttpContentDecompressor()); // 数据解压处理器
							channel.pipeline().addLast(new HttpClientHandler()); // 业务逻辑处理器
						}
					});
			// 异步等待连接上远程http服务器
			ChannelFuture future = bootstrap.connect(host, port).sync();
			// 等待与远程http服务器断开连接
			future.channel().closeFuture().sync();
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			group.shutdownGracefully();
		}
	}
	
	public static void main(String[] args) {
		start("127.0.0.1", 8081);
	}
}
2. 编写业务逻辑处理器
public class HttpClientHandler extends ChannelInboundHandlerAdapter {
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
	    // 通断被激活时我们发送请求到指定路径
		URI uri = new URI("/user/get");
		FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.GET, uri.toASCIIString());
		// FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri.toASCIIString()); // 有时候1.1发送的请求会报400错误
		// 添加请求头,保持长连接
		request.headers().add(HttpHeaderNames.CONNECTION,HttpHeaderValues.KEEP_ALIVE);
		// 添加请求头,确定请求体的长度
		request.headers().add(HttpHeaderNames.CONTENT_LENGTH,request.content().readableBytes());
		// 将请求实体写入出站处理器
		ctx.writeAndFlush(request);
	}
	
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		if(msg instanceof FullHttpResponse){
		    // 收到了http服务器的响应
			FullHttpResponse response = (FullHttpResponse)msg;
			ByteBuf buf = response.content();
			String result = buf.toString(CharsetUtil.UTF_8);
			System.out.println("response -> "+ result);
		}
	}
}
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
可以使用Netty处理在线播放mp4请求,下面是大致的实现思路: 1. 创建一个Netty服务器,监听指定的端口,等待客户端连接。 2. 当有客户端连接时,创建一个ChannelPipeline,添加一系列的ChannelHandler,用于处理请求响应。 3. 当收到客户端的请求时,解析请求中的URL,获取要播放的mp4文件的路径。 4. 使用Java的NIO API读取mp4文件,将数据写入响应Channel中,实现流式传输。 5. 客户端收到响应后,使用HTML5的video标签播放mp4文件,实现在线播放。 示例代码如下: ```java public class Mp4StreamingServer { private final int port; public Mp4StreamingServer(int port) { this.port = port; } public void run() throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new HttpServerCodec()); pipeline.addLast(new HttpObjectAggregator(65536)); pipeline.addLast(new ChunkedWriteHandler()); pipeline.addLast(new Mp4StreamingServerHandler()); } }); ChannelFuture f = b.bind(port).sync(); f.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } public static void main(String[] args) throws Exception { int port = Integer.parseInt(args[0]); new Mp4StreamingServer(port).run(); } } ``` ```java public class Mp4StreamingServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> { private static final String CONTENT_TYPE = "Content-Type"; private static final String CONTENT_LENGTH = "Content-Length"; private static final String KEEP_ALIVE = "keep-alive"; @Override protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception { if (request.method() != HttpMethod.GET) { sendError(ctx, HttpResponseStatus.METHOD_NOT_ALLOWED); return; } String uri = request.uri(); String path = sanitizeUri(uri); if (path == null) { sendError(ctx, HttpResponseStatus.FORBIDDEN); return; } File file = new File(path); if (!file.exists() || file.isHidden() || !file.isFile()) { sendError(ctx, HttpResponseStatus.NOT_FOUND); return; } if (!file.canRead()) { sendError(ctx, HttpResponseStatus.FORBIDDEN); return; } RandomAccessFile raf = new RandomAccessFile(file, "r"); long fileLength = raf.length(); HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); HttpUtil.setContentLength(response, fileLength); setContentTypeHeader(response, file); if (HttpUtil.isKeepAlive(request)) { response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); } ctx.write(response); ChannelFuture sendFileFuture = ctx.write(new ChunkedFile(raf, 0, fileLength, 8192), ctx.newProgressivePromise()); sendFileFuture.addListener(new ChannelProgressiveFutureListener() { @Override public void operationProgressed(ChannelProgressiveFuture future, long progress, long total) throws Exception { if (total < 0) { System.err.println("Transfer progress: " + progress); } else { System.err.println("Transfer progress: " + progress + " / " + total); } } @Override public void operationComplete(ChannelProgressiveFuture future) throws Exception { System.err.println("Transfer complete."); } }); ChannelFuture lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); if (!HttpUtil.isKeepAlive(request)) { lastContentFuture.addListener(ChannelFutureListener.CLOSE); } } private static final Pattern INSECURE_URI = Pattern.compile(".*[<>&\"].*"); private String sanitizeUri(String uri) { try { uri = URLDecoder.decode(uri, "UTF-8"); } catch (UnsupportedEncodingException e) { try { uri = URLDecoder.decode(uri, "ISO-8859-1"); } catch (UnsupportedEncodingException e1) { throw new Error(); } } if (!uri.startsWith("/")) { return null; } uri = uri.replace('/', File.separatorChar); if (uri.contains(File.separator + '.') || uri.contains('.' + File.separator) || uri.startsWith(".") || uri.endsWith(".") || INSECURE_URI.matcher(uri).matches()) { return null; } return System.getProperty("user.dir") + File.separator + uri; } 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(CONTENT_TYPE, "text/plain; charset=UTF-8"); ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); } private static void setContentTypeHeader(HttpResponse response, File file) { MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap(); response.headers().set(CONTENT_TYPE, mimeTypesMap.getContentType(file.getPath())); } } ``` 在上面的示例代码中,Mp4StreamingServer类是启动Netty服务器的入口,它创建了一个ServerBootstrap实例,配置了NioEventLoopGroup、NioServerSocketChannelChannelInitializer和Mp4StreamingServerHandler等组件,并监听指定的端口,等待客户端连接。 Mp4StreamingServerHandler类是处理请求响应的核心,它继承了SimpleChannelInboundHandler<FullHttpRequest>,重写了channelRead0方法,用于读取客户端的请求,解析请求中的URL,获取要播放的mp4文件的路径,读取mp4文件并将数据写入响应Channel中,实现流式传输。同时,如果客户端请求中包含keep-alive头,则在响应中添加该头,以保持连接,避免浪费资源。最后,通过调用ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT)方法,通知客户端数据传输完成,释放资源。 总之,使用Netty处理在线播放mp4请求,可以提高服务器的并发处理能力和吞吐量,同时实现实时流媒体传输,提升用户体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值