IM开发核心之构建TCP网关(上)

Untitled

TCP网关位于接入层的部分,客户端可以通过socket与接入层交互数据

接下来,使用socket监听我们的端口

编写SocketServer

@Configuration
public class SocketServer {

    @Autowired
    NettyServerHandler NETTY_SERVER_HANDLER;

    @Value("${socket.tcpSocket.port}")
    String tcpPort;

    @PostConstruct
    public void run() throws InterruptedException {
        EventLoopGroup boss = new NioEventLoopGroup();
        EventLoopGroup work = new NioEventLoopGroup();
        new ServerBootstrap()
            .group(boss, work)
            .channel(NioServerSocketChannel.class)
            .option(ChannelOption.SO_BACKLOG, 10240) // 服务端可连接队列大小
            .option(ChannelOption.SO_REUSEADDR, true) // 表示允许重复使用本地地址和端口
            .childOption(ChannelOption.TCP_NODELAY, true) // 禁用Nagla算法, 不能批量发送数据
            .childOption(ChannelOption.SO_KEEPALIVE, true) // 2h 没有数据,会发送心跳包
            .childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel socketChannel) throws Exception {
                    ChannelPipeline pipeline = socketChannel.pipeline();
                    pipeline.addLast(new MessageDecoder());
                    pipeline.addLast(new IdleStateHandler(0, 0, 10));
                    pipeline.addLast(NETTY_SERVER_HANDLER);
                }
            }).bind(Integer.parseInt(tcpPort)).sync();
    }
}

**SO_REUSEADDR:**端口复用最常用的用途应该是防止服务器重启时之前绑定的端口还未释放或者程序突然退出而系统没有释放端口。这种情况下如果设定了端口复用,则新启动的服务器进程可以直接绑定端口。如果没有设定端口复用,绑定会失败,提示ADDR已经在使用中——那只好等等再重试了,麻烦!

Nagla算法:

Nagle算法主要是避免发送小的数据包,要求TCP连接上最多只能有一个未被确认的小分组,在该分组的确认到达之前不能发送其他的小分组。相反,TCP收集这些少量的小分组,并在确认到来时以一个分组的方式发出去。

编写WebSocketServer

@Configuration
public class WebSocketServer {

    @Value("${socket.webSocket.port}")
    String webPort;

    @PostConstruct
    public void run() throws InterruptedException {
        EventLoopGroup boss = new NioEventLoopGroup();
        EventLoopGroup work = new NioEventLoopGroup();
        new ServerBootstrap()
            .group(boss, work)
            .channel(NioServerSocketChannel.class)
            .option(ChannelOption.SO_BACKLOG, 10240) // 服务端可连接队列大小
            .option(ChannelOption.SO_REUSEADDR, true) // 表示允许重复使用本地地址和端口
            .childOption(ChannelOption.TCP_NODELAY, true) // 禁用Nagla算法, 不能批量发送数据
            .childOption(ChannelOption.SO_KEEPALIVE, true) // 2h 没有数据,会发送心跳包
            .childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel socketChannel) throws Exception {
                    ChannelPipeline pipeline = socketChannel.pipeline();
                    // websocket 基于http协议,所以要有http编解码器
                    pipeline.addLast("http-codec", new HttpServerCodec());
                    // 对写大数据流的支持
                    pipeline.addLast("http-chunked", new ChunkedWriteHandler());
                    // 几乎在netty中的编程,都会使用到此hanler
                    pipeline.addLast("aggregator", new HttpObjectAggregator(65535));
                    /**
                     * websocket 服务器处理的协议,用于指定给客户端连接访问的路由 : /ws
                     * 本handler会帮你处理一些繁重的复杂的事
                     * 会帮你处理握手动作: handshaking(close, ping, pong) ping + pong = 心跳
                     * 对于websocket来讲,都是以frames进行传输的,不同的数据类型对应的frames也不同
                     */
                    pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
                }
            }).bind(Integer.parseInt(webPort)).sync();
    }
}

详解主流通讯协议

  1. 文本协议

2. 二进制协议

  1. xml协议