Netty实现HTTP客户端

本文详细介绍了使用 Netty 框架实现 HTTP 客户端的方法,包括 GET 和 POST 请求的发送,以及如何处理响应。通过实例代码展示了如何配置连接,发送请求并解析响应。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Netty实现HTTP客户端

这是用netty写的http客户端,没什么好说的,细节直接看代码

Http客户端

package http2;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
import io.netty.handler.codec.http.multipart.HttpDataFactory;
import io.netty.handler.codec.http.multipart.HttpPostRequestEncoder;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * Created with IntelliJ IDEA.
 * User: ASUS
 * Date: 14-6-25
 * Time: 下午2:44
 * To change this template use File | Settings | File Templates.
 */
public class Client {

    public static HttpRequest getRequestMethod(Map<String, String> parameter, String url, String method) throws HttpPostRequestEncoder.ErrorDataEncoderException {
        URI uri;
        try {
            uri = new URI(url);
        } catch (URISyntaxException e) {
            e.printStackTrace();
            return null;
        }

        String path = uri.getRawPath();
        String host = uri.getHost();

        HttpRequest request = null;
        if ("post".equalsIgnoreCase(method)) {
            request = new DefaultFullHttpRequest(
                    HttpVersion.HTTP_1_1, HttpMethod.POST, path);

            HttpDataFactory factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE);
            // This encoder will help to encode Request for a FORM as POST.
            HttpPostRequestEncoder bodyRequestEncoder = new HttpPostRequestEncoder(factory, request, false);
            // add Form attribute
            if (parameter != null) {
                Set<Map.Entry<String, String>> entrySet = parameter.entrySet();
                for (Map.Entry<String, String> e : entrySet) {
                    String key = e.getKey();
                    String value = e.getValue();
                    bodyRequestEncoder.addBodyAttribute(key, value);
                }
                try {
                    request = bodyRequestEncoder.finalizeRequest();
                } catch (HttpPostRequestEncoder.ErrorDataEncoderException e) {
                    // if an encoding error occurs
                    e.printStackTrace();
                }
            }

            request.headers().set(HttpHeaders.Names.HOST, host);
            request.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE);
            request.headers().set(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP);
            request.headers().set(HttpHeaders.Names.COOKIE, ClientCookieEncoder.encode(
                    new DefaultCookie("my-cookie", "foo"),
                    new DefaultCookie("another-cookie", "bar")));
        } else if ("get".equalsIgnoreCase(method)) {
            //uri.toString()没有查询参数的uri
            QueryStringEncoder encoder = new QueryStringEncoder(uri.toString());
            if (parameter != null) {
                Set<Map.Entry<String, String>> entrySet = parameter.entrySet();
                for (Map.Entry<String, String> e : entrySet) {
                    String key = e.getKey();
                    String value = e.getValue();
                    encoder.addParam(key, value);
                }
            }
            //encoder.toString()有查询参数的uri
            request = new DefaultFullHttpRequest(
                    HttpVersion.HTTP_1_1, HttpMethod.GET, encoder.toString());
            HttpHeaders headers = request.headers();
            headers.set(HttpHeaders.Names.HOST, host);
            headers.set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE);
            headers.set(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP.toString() + ','
                    + HttpHeaders.Values.DEFLATE.toString());

            headers.set(HttpHeaders.Names.ACCEPT_CHARSET, "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
            headers.set(HttpHeaders.Names.ACCEPT_LANGUAGE, "fr");
            headers.set(HttpHeaders.Names.USER_AGENT, "Netty Simple Http Client side");
            headers.set(HttpHeaders.Names.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");

            headers.set(HttpHeaders.Names.COOKIE, ClientCookieEncoder.encode(
                    new DefaultCookie("my-cookie", "foo"),
                    new DefaultCookie("another-cookie", "bar"))
            );
        } else {
            System.err.println("this method is not support!");
        }
        return request;
    }

    public void run(String url, HttpRequest request) throws HttpPostRequestEncoder.ErrorDataEncoderException, InterruptedException {
        URI uri;
        try {
            uri = new URI(url);
        } catch (URISyntaxException e) {
            e.printStackTrace();
            return;
        }

        String scheme = uri.getScheme() == null ? "http" : uri.getScheme();
        String host = uri.getHost() == null ? "localhost" : uri.getHost();
        int port = uri.getPort();
        if (port == -1) {
            if ("http".equalsIgnoreCase(scheme)) {
                port = 80;
            } else if ("https".equalsIgnoreCase(scheme)) {
                port = 443;
            }
        }

        if (!"http".equalsIgnoreCase(scheme) && !"https".equalsIgnoreCase(scheme)) {
            System.err.println("Only HTTP(S) is supported.");
        }

        boolean ssl = "https".equalsIgnoreCase(scheme);

        // Configure the client.
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new HttpClientInitializer(ssl));

            // Make the connection attempt.
            Channel ch = b.connect(host, port).sync().channel();
            // send request
            ch.writeAndFlush(request).sync();
            ch.closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }


    public static void main(String args[]) throws HttpPostRequestEncoder.ErrorDataEncoderException, InterruptedException {
        String url = "http://www.cnivi.com.cn/curriculum/search.html";
        Map<String, String> getData = new HashMap<String, String>();
        getData.put("tags", "806:938356;");
        getData.put("sort", "_p");

        HttpRequest get = getRequestMethod(getData, url, "get");
        new Client().run(url, get);
    }
}
package http2;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpContentDecompressor;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestEncoder;
import io.netty.handler.codec.http.HttpResponseDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslHandler;

import javax.net.ssl.SSLEngine;

public class HttpClientInitializer extends ChannelInitializer<SocketChannel> {

    private final boolean ssl;

    public HttpClientInitializer(boolean ssl) {
        this.ssl = ssl;
    }


    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline p = ch.pipeline();

        p.addLast("log", new LoggingHandler(LogLevel.INFO));
        if (ssl) {
            SSLEngine engine = SslContextFactory.getClientContext().createSSLEngine();
            engine.setUseClientMode(true);
            p.addLast("ssl", new SslHandler(engine));
        }
        p.addLast("request-encoder", new HttpRequestEncoder());

        p.addLast("response-decoder", new HttpResponseDecoder());

        // Remove the following line if you don't want automatic content decompression.
        p.addLast("inflater", new HttpContentDecompressor());

        //HttpObjectAggregator会把多个消息转换为 一个单一的FullHttpRequest或是FullHttpResponse
        //p.addLast("aggregator", new HttpObjectAggregator(1048576));
        p.addLast("handler", new HttpClientHandler());
    }
}
package http2;

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

/**
 * Created with IntelliJ IDEA.
 * User: ASUS
 * Date: 14-6-25
 * Time: 下午2:49
 * To change this template use File | Settings | File Templates.
 */
public class HttpClientHandler extends SimpleChannelInboundHandler<HttpObject> {

    public void messageReceived(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        if (msg instanceof HttpResponse) {
            HttpResponse response = (HttpResponse) msg;

            System.out.println("STATUS: " + response.getStatus());
            System.out.println("VERSION: " + response.getProtocolVersion());
            System.out.println();

            if (!response.headers().isEmpty()) {
                for (String name : response.headers().names()) {
                    for (String value : response.headers().getAll(name)) {
                        System.out.println("HEADER: " + name + " = " + value);
                    }
                }
                System.out.println();
            }

            if (HttpHeaders.isTransferEncodingChunked(response)) {
                System.out.println("CHUNKED CONTENT {");
            } else {
                System.out.println("CONTENT {");
            }
        }
        if (msg instanceof HttpContent) {
            HttpContent content = (HttpContent) msg;

            System.out.print(content.content().toString(CharsetUtil.UTF_8));
            System.out.flush();

            if (content instanceof LastHttpContent) {
                System.out.println("} END OF CONTENT");
            }
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
        messageReceived(ctx, msg);
    }
}

这是完整的netty实现的http客户端的代码。


补充:

//HttpObjectAggregator会把多个消息转换为 一个单一的FullHttpRequest或是FullHttpResponse

//p.addLast("aggregator", new HttpObjectAggregator(1048576));

这句代码对handler的处理方式有很大不同。如果注释掉这行代码,则对应的handler应该这样处理

package http3;

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

import java.nio.charset.Charset;

/**
 * Created with IntelliJ IDEA.
 * User: ASUS
 * Date: 14-6-25
 * Time: 下午2:49
 * To change this template use File | Settings | File Templates.
 */
public class HttpClientHandler extends SimpleChannelInboundHandler<HttpObject> {

    public void messageReceived(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        DefaultFullHttpResponse fullHttpResponse = (DefaultFullHttpResponse) msg;

        System.out.println("STATUS: " + fullHttpResponse.getStatus());
        System.out.println("VERSION: " + fullHttpResponse.getProtocolVersion());
        System.out.println();

        if (!fullHttpResponse.headers().isEmpty()) {
            for (String name : fullHttpResponse.headers().names()) {
                for (String value : fullHttpResponse.headers().getAll(name)) {
                    System.out.println("HEADER: " + name + " = " + value);
                }
            }
            System.out.println();
        }

        if (HttpHeaders.isTransferEncodingChunked(fullHttpResponse)) {
            System.out.println("CHUNKED CONTENT {");
        } else {
            System.out.println("CONTENT {");
            ByteBuf content = fullHttpResponse.content();
            System.out.println(content.toString(CharsetUtil.UTF_8));
            System.out.println("} END OF CONTENT");
        }
    }


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
        messageReceived(ctx, msg);
    }
}

======END======

转载于:https://my.oschina.net/xinxingegeya/blog/284103

### 使用 Netty 实现 WebSocket 客户端 为了创建一个基于 Netty 的 WebSocket 客户端,需要配置并初始化客户端引导程序 (Bootstrap),设置事件循环组 (EventLoopGroup),指定通道类型 (NioSocketChannel),并通过管道 (pipeline) 添加必要的处理器来处理连接和消息。 #### 创建 Bootstrap 并启动客户端 ```java import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; public class WebSocketClient { private final String host; private final int port; public WebSocketClient(String host, int port) { this.host = host; this.port = port; } public void start() throws Exception { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group) .channel(NioSocketChannel.class) .handler(new WebSocketClientInitializer()); ChannelFuture f = b.connect(host, port).sync(); f.channel().closeFuture().sync(); } finally { group.shutdownGracefully(); } } } ``` 此代码片段展示了如何通过 `Bootstrap` 设置一个新的客户端实例,并指定了用于网络 I/O 操作的线程池 `EventLoopGroup` 和要使用的通道类型 `NioSocketChannel`[^1]。 #### 配置 Pipeline 处理器 为了让客户端能够发送和接收 WebSocket 帧数据包,在建立 TCP 连接之后还需要进一步配置 `Pipeline` 来添加特定于 WebSocket 协议的消息编解码器和其他逻辑处理器: ```java import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.http.HttpClientCodec; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.stream.ChunkedWriteHandler; import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler; import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler; public class WebSocketClientInitializer extends ChannelInitializer<SocketChannel> { private static final String WEBSOCKET_PATH = "/ws"; @Override protected void initChannel(SocketChannel ch) throws Exception { SslContext sslCtx = SslContextBuilder.forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE).build(); ChannelPipeline pipeline = ch.pipeline(); // SSL/TLS 加密支持 if (sslCtx != null && !sslCtx.isClosed()) { pipeline.addLast(sslCtx.newHandler(ch.alloc(), "localhost", 8443)); } // HTTP 编解码器 pipeline.addLast(new HttpClientCodec()); // 支持分块写入 pipeline.addLast(new ChunkedWriteHandler()); // 聚合HTTP响应对象 pipeline.addLast(new HttpObjectAggregator(8192)); // 启用压缩扩展 pipeline.addLast(new WebSocketClientCompressionHandler()); // WebSocket协议处理器 pipeline.addLast(new WebSocketClientProtocolHandler( WEBSOCKET_PATH, null, true)); // 自定义业务逻辑处理器 pipeline.addLast(new WebSocketClientHandler()); } } ``` 这段代码说明了如何向 `Pipeline` 中加入各种处理器以确保正确地解析来自服务器的数据流以及准备向外传输的信息格式。特别是加入了 `HttpClientCodec`、`HttpObjectAggregator` 及专门针对 WebSockets 的编码/解码组件 `WebSocketClientProtocolHandler`[^4]。 #### 发送与接收消息 最后一步是在自定义的 `SimpleChannelInboundHandler<String>` 子类中实现具体的读取和写出行为: ```java import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.handler.timeout.IdleStateEvent; public class WebSocketClientHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> { @Override protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { System.out.println("Received message from server: " + msg.text()); } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { super.userEventTriggered(ctx, evt); if (evt instanceof IdleStateEvent) { // Handle heartbeat or reconnection logic here. } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { cause.printStackTrace(); ctx.close(); } public void sendMessage(String message){ ctx.writeAndFlush(new TextWebSocketFrame(message)); } } ``` 上述实现了基本的消息监听机制,每当收到新的文本帧时就会触发相应的回调函数;同时也提供了简单的方法来进行主动的消息推送操作[^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值