netty 学习 (4)混合使用coder和handler

摘要 该例子模拟一个Server和Client,两者之间通过http协议进行通讯,在Server内部通过一个自定义的StringDecoder把httprequest转换成String。Server端处理完成后,通过StringEncoder把String转换成httpresponse,发送给客户端。 转自:http://blog.csdn.net/u013252773/article/details/21564301

netty coder handler

该例子模拟一个Server和Client,两者之间通过http协议进行通讯,在Server内部通过一个自定义的StringDecoder把httprequest转换成String。Server端处理完成后,通过StringEncoder把String转换成httpresponse,发送给客户端。具体的处理流程如图所示:

其中红色框中的Decoder、Encoder及request都是Netty框架自带的,灰色框中的三个类是我自己实现的。

引入jar包:

用到的log4j.xml:

?

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

<?xmlversion="1.0"?>

<!DOCTYPElog4j:configuration SYSTEM "log4j.dtd">

<log4j:configurationxmlns:log4j="http://jakarta.apache.org/log4j/">

    <appendername="CONSOLE"class="org.apache.log4j.ConsoleAppender">

        <layoutclass="org.apache.log4j.PatternLayout">

            <paramname="ConversionPattern"value="[%-5p] [%d] [%t] [%c] %m%n"/>

        </layout>

    </appender>

     

    <appendername="FILE"class="org.apache.log4j.DailyRollingFileAppender">

        <paramname="File"value="./log/netty.log"/>

        <layoutclass="org.apache.log4j.PatternLayout">

            <paramname="ConversionPattern"value="[%-5p] [%d] [%t] [%c] %m%n"/>

        </layout>

    </appender>

     

    <appendername="FILE_ERR"class="org.apache.log4j.DailyRollingFileAppender">

        <paramname="File"value="./log/netty_err.log"/>

        <paramname="Threshold"value="ERROR"/>

        <layoutclass="org.apache.log4j.PatternLayout">

            <paramname="ConversionPattern"value="[%-5p] [%d] [%t] [%c] %m%n"/>

        </layout>

    </appender>      

     

    <loggername="io.netty"additivity="false">

        <levelvalue="INFO,DEBUG"/>

        <appender-refref="FILE"/>

        <appender-refref="FILE_ERR"/>

        <appender-refref="CONSOLE"/>

    </logger>

    <loggername="com.yao"additivity="false">

        <levelvalue="INFO,DEBUG"/>

        <appender-refref="FILE"/>

        <appender-refref="FILE_ERR"/>

        <appender-refref="CONSOLE"/>

    </logger>

     

    <root>

         

      <levelvalue="debug"/>

        <appender-refref="FILE"/>

        <appender-refref="CONSOLE"/>

        <appender-refref="FILE_ERR"/>

    </root>

</log4j:configuration>

Server端的类有:Server StringDecoder BusinessHandler StringEncoder四个类。

1、Server 启动netty服务,并注册handler、coder,注意注册的顺序:

?

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

packagecom.yao.codehandler;

importio.netty.bootstrap.ServerBootstrap;

importio.netty.channel.ChannelFuture;

importio.netty.channel.ChannelInitializer;

importio.netty.channel.ChannelOption;

importio.netty.channel.EventLoopGroup;

importio.netty.channel.nio.NioEventLoopGroup;

importio.netty.channel.socket.SocketChannel;

importio.netty.channel.socket.nio.NioServerSocketChannel;

importio.netty.handler.codec.http.HttpRequestDecoder;

importio.netty.handler.codec.http.HttpResponseEncoder;

// 测试coder 和 handler 的混合使用

publicclass Server {

    publicvoid start(intport) throwsException {

        EventLoopGroup bossGroup = newNioEventLoopGroup();

        EventLoopGroup workerGroup = newNioEventLoopGroup();

        try{

            ServerBootstrap b = newServerBootstrap();

            b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)

                    .childHandler(newChannelInitializer<SocketChannel>() {

                                @Override

                                publicvoid initChannel(SocketChannel ch) throwsException {

                                    // 都属于ChannelOutboundHandler,逆序执行

                                    ch.pipeline().addLast(newHttpResponseEncoder());

                                    ch.pipeline().addLast(newStringEncoder());

                                     

                                    // 都属于ChannelIntboundHandler,按照顺序执行

                                    ch.pipeline().addLast(newHttpRequestDecoder());

                                    ch.pipeline().addLast(newStringDecoder());

                                    ch.pipeline().addLast(newBusinessHandler());

                                }

                            }).option(ChannelOption.SO_BACKLOG,128)

                    .childOption(ChannelOption.SO_KEEPALIVE,true);

            ChannelFuture f = b.bind(port).sync();

            f.channel().closeFuture().sync();

        }finally{

            workerGroup.shutdownGracefully();

            bossGroup.shutdownGracefully();

        }

    }

    publicstatic void main(String[] args) throwsException {

        Server server = newServer();

        server.start(8000);

    }

}

2、StringDecoder 把httpRequest转换成String,其中ByteBufToBytes是一个工具类,负责对ByteBuf中的数据进行读取

?

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

packagecom.yao.codehandler;

importio.netty.channel.ChannelHandlerContext;

importio.netty.channel.ChannelInboundHandlerAdapter;

importio.netty.handler.codec.http.HttpContent;

importio.netty.handler.codec.http.HttpHeaders;

importio.netty.handler.codec.http.HttpRequest;

importorg.apache.commons.logging.Log;

importorg.apache.commons.logging.LogFactory;

publicclass StringDecoder extendsChannelInboundHandlerAdapter {

    privatestatic Log  logger  = LogFactory.getLog(StringDecoder.class);

    privateByteBufToBytes  reader;

    @Override

    publicvoid channelRead(ChannelHandlerContext ctx, Object msg) throwsException {

        logger.info("StringDecoder : msg's type is " + msg.getClass());

        if(msg instanceofHttpRequest) {

            HttpRequest request = (HttpRequest) msg;

            reader = newByteBufToBytes((int) HttpHeaders.getContentLength(request));

        }

        if(msg instanceofHttpContent) {

            HttpContent content = (HttpContent) msg;

            reader.reading(content.content());

            if(reader.isEnd()) {

                byte[] clientMsg = reader.readFull();

                logger.info("StringDecoder : change httpcontent to string ");

                ctx.fireChannelRead(newString(clientMsg));

            }

        }

    }

}

3、BusinessHandler 具体处理业务的类,把客户端的请求打印出来,并向客户端发送信息

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

packagecom.yao.codehandler;

importio.netty.channel.ChannelHandlerContext;

importio.netty.channel.ChannelInboundHandlerAdapter;

importorg.apache.commons.logging.Log;

importorg.apache.commons.logging.LogFactory;

publicclass BusinessHandler extendsChannelInboundHandlerAdapter {

    privateLog logger  = LogFactory.getLog(BusinessHandler.class);

    @Override

    publicvoid channelRead(ChannelHandlerContext ctx, Object msg) throwsException {

        String clientMsg = "client said : " + (String) msg;

        logger.info("BusinessHandler read msg from client :" + clientMsg);

        ctx.write("I am very OK!");

    }

    @Override

    publicvoid channelReadComplete(ChannelHandlerContext ctx) throwsException {

        ctx.flush();

    }

}

4、StringEncoder 把字符串转换成HttpResponse

?

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

packagecom.yao.codehandler;

importstatic io.netty.handler.codec.http.HttpHeaders.Names.CONNECTION;

importstatic io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;

importstatic io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;

importstatic io.netty.handler.codec.http.HttpResponseStatus.OK;

importstatic io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

importio.netty.buffer.Unpooled;

importio.netty.channel.ChannelHandlerContext;

importio.netty.channel.ChannelOutboundHandlerAdapter;

importio.netty.channel.ChannelPromise;

importio.netty.handler.codec.http.DefaultFullHttpResponse;

importio.netty.handler.codec.http.FullHttpResponse;

importio.netty.handler.codec.http.HttpHeaders.Values;

importorg.apache.commons.logging.Log;

importorg.apache.commons.logging.LogFactory;

// 把String转换成httpResponse

publicclass StringEncoder extendsChannelOutboundHandlerAdapter {

    privateLog logger  = LogFactory.getLog(StringEncoder.class);

    @Override

    publicvoid write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throwsException {

        logger.info("StringEncoder response to client.");

        String serverMsg = (String) msg;

        FullHttpResponse response = newDefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(serverMsg

                .getBytes()));

        response.headers().set(CONTENT_TYPE,"text/plain");

        response.headers().set(CONTENT_LENGTH, response.content().readableBytes());

        response.headers().set(CONNECTION, Values.KEEP_ALIVE);

        ctx.write(response);

        ctx.flush();

    }

}

Client端有两个类:Client ClientInitHandler 
1、Client 与Server端建立连接,并向Server端发送HttpRequest请求。

?

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

packagecom.yao.codehandler;

importio.netty.bootstrap.Bootstrap;

importio.netty.buffer.Unpooled;

importio.netty.channel.ChannelFuture;

importio.netty.channel.ChannelInitializer;

importio.netty.channel.ChannelOption;

importio.netty.channel.EventLoopGroup;

importio.netty.channel.nio.NioEventLoopGroup;

importio.netty.channel.socket.SocketChannel;

importio.netty.channel.socket.nio.NioSocketChannel;

importio.netty.handler.codec.http.DefaultFullHttpRequest;

importio.netty.handler.codec.http.HttpHeaders;

importio.netty.handler.codec.http.HttpMethod;

importio.netty.handler.codec.http.HttpRequestEncoder;

importio.netty.handler.codec.http.HttpResponseDecoder;

importio.netty.handler.codec.http.HttpVersion;

importjava.net.URI;

publicclass Client {

    publicvoid connect(String host, intport) throwsException {

        EventLoopGroup workerGroup = newNioEventLoopGroup();

        try{

            Bootstrap b = newBootstrap();

            b.group(workerGroup);

            b.channel(NioSocketChannel.class);

            b.option(ChannelOption.SO_KEEPALIVE,true);

            b.handler(newChannelInitializer<SocketChannel>() {

                @Override

                publicvoid initChannel(SocketChannel ch) throwsException {

                    ch.pipeline().addLast(newHttpResponseDecoder());

                    ch.pipeline().addLast(newHttpRequestEncoder());

                    ch.pipeline().addLast(newClientInitHandler());

                }

            });

            // Start the client.

            ChannelFuture f = b.connect(host, port).sync();

            URI uri = newURI("http://127.0.0.1:8000");

            String msg = "Are you ok?";

            DefaultFullHttpRequest request = newDefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST,

                    uri.toASCIIString(), Unpooled.wrappedBuffer(msg.getBytes()));

            request.headers().set(HttpHeaders.Names.HOST, host);

            request.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE);

            request.headers().set(HttpHeaders.Names.CONTENT_LENGTH, request.content().readableBytes());

            f.channel().write(request);

            f.channel().flush();

            f.channel().closeFuture().sync();

        }finally{

            workerGroup.shutdownGracefully();

        }

    }

    publicstatic void main(String[] args) throwsException {

        Client client = newClient();

        client.connect("127.0.0.1",8000);

    }

}

2、ClientInitHandler 从Server端读取响应信息

?

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

packagecom.yao.codehandler;

importio.netty.buffer.ByteBuf;

importio.netty.channel.ChannelHandlerContext;

importio.netty.channel.ChannelInboundHandlerAdapter;

importio.netty.handler.codec.http.HttpContent;

importio.netty.handler.codec.http.HttpHeaders;

importio.netty.handler.codec.http.HttpResponse;

publicclass ClientInitHandler extendsChannelInboundHandlerAdapter {

    privateByteBufToBytes  reader;

    @Override

    publicvoid channelRead(ChannelHandlerContext ctx, Object msg) throwsException {

        if(msg instanceofHttpResponse) {

            HttpResponse response = (HttpResponse) msg;

            if(HttpHeaders.isContentLengthSet(response)) {

                reader = newByteBufToBytes((int) HttpHeaders.getContentLength(response));

            }

        }

        if(msg instanceofHttpContent) {

            HttpContent httpContent = (HttpContent) msg;

            ByteBuf content = httpContent.content();

            reader.reading(content);

            content.release();

            if(reader.isEnd()) {

                String resultStr = newString(reader.readFull());

                System.out.println("Server said:" + resultStr);

            }

        }

    }

    @Override

    publicvoid channelReadComplete(ChannelHandlerContext ctx) throwsException {

        ctx.close();

    }

}

工具类:ByteBufToBytes 对ByteBuf的数据进行读取,支持流式读取(reading 和 readFull方法结合使用) 
ByteBufToBytes 

?

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

packagecom.yao.codehandler;

importio.netty.buffer.ByteBuf;

importio.netty.buffer.Unpooled;

publicclass ByteBufToBytes {

    privateByteBuf temp;

    privateboolean end = true;

    publicByteBufToBytes() {}

    publicByteBufToBytes(intlength) {

        temp = Unpooled.buffer(length);

    }

    publicvoid reading(ByteBuf datas) {

        datas.readBytes(temp, datas.readableBytes());

        if(this.temp.writableBytes() != 0) {

            end = false;

        }else{

            end = true;

        }

    }

    publicboolean isEnd() {

        returnend;

    }

    publicbyte[] readFull() {

        if(end) {

            byte[] contentByte = newbyte[this.temp.readableBytes()];

            this.temp.readBytes(contentByte);

            this.temp.release();

            returncontentByte;

        }else{

            returnnull;

        }

    }

    publicbyte[] read(ByteBuf datas) {

        byte[] bytes = newbyte[datas.readableBytes()];

        datas.readBytes(bytes);

        returnbytes;

    }

}

通过该实例证明,Encoder、Decoder的本质也是Handler,它们的执行顺序、使用方法与Handler保持一致。

执行顺序是:Encoder 先注册的后执行,与OutboundHandler一致;Decoder是先注册的先执行,与InboundHandler一致。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值