netty-时间服务器

编解码器

public class ClientInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4));
        pipeline.addLast(new LengthFieldPrepender(4));
        pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
        pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
        pipeline.addLast(new ClientHandler());
    }
}

因为主体还是字符串传输,所以再复述一遍。

StringEncodeStringDecode都是可以指定编码的,默认也是utf8

客户端和服务端的编解码要对应,所以一样的就不多说了。

主体逻辑

server

public class ServerHandler extends SimpleChannelInboundHandler<String> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        System.out.println(ctx.channel().remoteAddress() + "请求");
        ctx.writeAndFlush(LocalDateTime.now().toString());
    }
     @Override
     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

简单逻辑就是如此,返回当前时间即可。

更多操作后面添加。

我们写入的时候是toString过后的,也就是字符串对象。

当不进行toString的时候,你会发现客户端根本就不能够进行接收。

同样的,客户端writeAndFlush写入的不是String的话,服务器的方法也没有触发。

encode
decode
decode
encode
source
binary
target

所以前面说编码解码要一一对应,编码当然简单,基本没有失败一说。

但是如果解码失败,这时候解码的handler就真的是拦截器了,这个信息并不会向后传播。

所以也就不会触发后续的业务逻辑。

当业务逻辑比较简单的时候,编解码的handler就会是服务器的主体代码。

规范一点,异常处理也要覆盖到,否则你会看到这个提示

It usually means the last handler in the pipeline did not handle the exception.

不陌生吧,这些地方要注意,业务逻辑和异常处理,也就是channelRead0exceptionCaugth缺一不可。

client

public class ClientHandler extends SimpleChannelInboundHandler<String> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        System.out.println("time from server:" + msg);
    }
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush("ask for time");
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

嗯,额外增加了一个部分,从名称上看来是链接激活时执行的。

之前的代码其实已经准备就绪,只是看不到效果而已。

状态大致类比于此:

A:你先动手我一定打死你

B:有种你动手,我保证打不死你

叫嚣了半天,没人动手,虽然准备打架了,但是就是没有打起来。

这个时候我们channelActive悄悄的挑拨一下,然后就开始动手了,也就是去触发

因为channelRead0是接收到数据才能够进行处理的。

额外功能

代码有有一个最大的漏洞,那就是身份问题。

可以看到,不论是哪个服务器,也不论是谁,都能够调用我们的服务。

我们当然能够去做一些限定条件。

身份校验

protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        if (!"godme-godme".equals(msg)) {
            ctx.writeAndFlush("你没有权限访问该服务:username-password");
            return;
        }
        System.out.println(ctx.channel().remoteAddress() + "发起请求");
        ctx.writeAndFlush(LocalDateTime.now().toString());
    }

ip限定

protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress();
        if (!"127.0.0.1".equals(address.getHostName())){
            ctx.close();
            return;
        }
        if (!"godme-godme".equals(msg)) {
            ctx.writeAndFlush("你没有权限访问该服务:username-password");
            return;
        }
        System.out.println(address.getHostName() + "发起请求");
        ctx.writeAndFlush(LocalDateTime.now().toString());
    }

不是规定的ip,直接关闭连接。

小结

这算得上是一个完整的客户端-服务端代码了。

麻雀虽小五脏俱全,基本流程就是如此了。

至于校验之类的,可以自己随便定义。

垃圾代码在此;

你可能感兴趣的:(netty)