Netty Base Http Client example Code





NettyHttpClient


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
 
public class NettyClient {
 
    ChannelFuture cf;
    EventLoopGroup group;
    
    public void connect(String host, int port) {
 
       group = new NioEventLoopGroup();
 
        try {
            Bootstrap b = new Bootstrap();
            b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true)
                    .handler(new NettyHttpChannelInit(group));
 
            cf = b.connect(host, port).sync();
//            cf.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
//post Request Encoder : attribute를 지정하고 싶다면, PostRquestEncoder를 통해 요청해야한다.
    public void createRequest(String host, int port, String url) throws Exception {
        
        
            HttpRequest request = null;
            HttpPostRequestEncoder postRequestEncoder = null;
            
            request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/create"
//                    ,Unpooled.copiedBuffer(url.getBytes(CharsetUtil.UTF_8))
            );
            request.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED);
            request.headers().set(HttpHeaderNames.HOST, host+":"+port);
            request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
//            request.headers().set(HttpHeaderNames.CONTENT_LENGTH, url.length());
            
            postRequestEncoder = new HttpPostRequestEncoder(request, false);
            postRequestEncoder.addBodyAttribute("url", url);
            request=postRequestEncoder.finalizeRequest();
            postRequestEncoder.close();
//            cf.channel().writeAndFlush(request).addListener(ChannelFutureListener.CLOSE);
            cf.channel().writeAndFlush(request);
//            System.out.println(request.toString());
    }
    
    public void close() {
        cf.channel().close();
        group.shutdownGracefully();
    }
    
    
}
 
 

cs



-> 가장 중요한 부분은 Request Format이다.  DefaultFullHttpRequest객체를 통해 http의 header, body를 직접 정의할 수 있다.

체크해야할 부분이 있다면, PostRequestEncoder. Post방식의 경우 요청 파라미터가 Url에 명시되지 않기 때문에, PostRequestEncoder를 통해 지정가능하다.








Channelinitializer 



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
 
public class NettyHttpChannelInit extends ChannelInitializer<SocketChannel>{
    
    private boolean ssl = false;
    private EventLoopGroup group;
    
     public NettyHttpChannelInit(EventLoopGroup group) {
         this.group = group;
    }
    @Override
    protected void initChannel(SocketChannel sc) throws Exception {
        // TODO Auto-generated method stub
        
        ChannelPipeline p = sc.pipeline();
        if(ssl) {
            SslContext sslCtx = null;
            try {
                sslCtx = SslContextBuilder.forClient()
                        .trustManager(InsecureTrustManagerFactory.INSTANCE).build();
                 p.addLast(sslCtx.newHandler(sc.alloc()));
            } catch (SSLException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
        }
    
        /**
         *
         * Message Decoder, FastLz...
        */
    
    //    p.addLast(new MessageDecoder());
 
        
        //chunked 된 응답을 집계하는 코덱
        p.addLast("chunked",new HttpObjectAggregator(1048576));
        p.addLast("codec",new HttpClientCodec());
        p.addLast(new NettyHttpHandler(group, sc));
    }
}
cs



-> 채널을 초기화하는 객체에서는 서버에서 데이터를  받기 위해 여러 핸들러를 정의했다. 

샘플 코드에서는 4개의 핸들러를 추가었다.



1) sslCtx.newHandler : SSL,TLS 성립된 서버와의 통신을 위해 추가된 코덱 (https에 해당한다.)


2)HttpObjectAggregator : chunked된 응답을 집계하는 코덱이다.



* Chunked

  - Chunk Response, "덩어리 응답" 전체 페이지를 가공하지 않는다.

  - 서버측에서 html 전부 생성한 후에 클라이언트에게 보내는 것이 아니라 html 덩어리(chunk) 단위로 쪼개서 보낼  있다.

  - 브라우저(클라이언트)에게 전체 컨텐츠 크기가 얼마나 큰지 알려주지 않아도된다는 특징이 있다. 

  - 따라서동적인 크기의 컨텐츠  스트리밍에 적합하고 Chunked transfer encoding 사용해야하며 netty의 경우 httpObjectAggregator 코덱에 해당된다.

 

3) httpClientCodec


송신시에는 HttpClientCodec을 사용해야 하며. HttpResponseEncoder로 대신 사용할 수 있다.

pipeline.addLast("encoder", new HttpClientCodec());

pipeline.addLast("encoder", new HttpResponseEncoder());


수신시에는 HttpServerCodec 사용해야 하며아래와 같이 HttpRequestDecoder대신 사용할 수 있다.

pipeline.addLast("decoder", new HttpServerCodec());

pipeline.addLast("decoder", new HttpRequestDecoder());

 


4) NettyHttpHandler : 사용자 정의 핸들러, SimpleChannelInboundHandler를 상속받은 사용자 정의 객체 입니다. 실제 메시지를 확인하기 위해 만들었다.





NettyClientHandler


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
 
 
public class NettyHttpHandler extends SimpleChannelInboundHandler<HttpObject> {
 
    private EventLoopGroup group;
    private SocketChannel sc;
    private int count=0;
    
 
    public NettyHttpHandler(EventLoopGroup group, SocketChannel sc) {
        this.group = group;
        this.sc =sc;
    }
 
 
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // TODO Auto-generated method stub
        super.channelActive(ctx);
//        System.out.println("connect:"+ctx.channel().isActive());
    }
    
 
 
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
        // TODO Auto-generated method stub
//        System.err.println(msg);
        
        if (msg instanceof HttpResponse) {
            HttpResponse response = (HttpResponse) msg;
            
            
            System.err.println("STATUS: " + response.status());
            System.err.println("VERSION: " + response.protocolVersion());
            
 
            if (!response.headers().isEmpty()) {
                for (CharSequence name : response.headers().names()) {
                    for (CharSequence value : response.headers().getAll(name)) {
                        System.err.println("HEADER: " + name + " = " + value);
                    }
                }
                System.err.println();
            }
 
            if (HttpUtil.isTransferEncodingChunked(response)) {
                System.err.println("CHUNKED CONTENT {");
            } else {
                System.err.println("CONTENT {");
            }
        }
        if (msg instanceof HttpContent) {
            count++;
            HttpContent content = (HttpContent) msg;
            System.err.println(count+". create url = "+content.content().toString(CharsetUtil.UTF_8));
            System.err.flush();
 
            if (content instanceof LastHttpContent) {
                System.err.println("} END OF CONTENT");
            }
        }
    }
 
        
 
 
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
        cause.printStackTrace();
        ctx.close();
        sc.close();
        group.shutdownGracefully();
    }
cs




+ Recent posts