java 使用netty搭建tcp服务器(hello world)

public class Program {
public static void main(String[] args) throws Exception {

    EchoServer echoServer = new EchoServer("127.0.0.1", 8888); // 启动
    echoServer.start();
}

}

class EchoServer {
private final int port;
private final String ip;

public EchoServer(String ip, int port) {
    this.ip = ip;
    this.port = port;
}

public void start() throws Exception {
    // 创建用于监听accept的线程池
    EventLoopGroup bossGroup = new NioEventLoopGroup();
    // 创建用于处理队列和数据的线程池
    EventLoopGroup group = new NioEventLoopGroup();
    try {
        ServerBootstrap bootstrap = new ServerBootstrap();

        // BACKLOG用于构造服务端套接字ServerSocket对象,标识当服务器请求处理线程全满时
        // ,用于临时存放已完成三次握手的请求的队列的最大长度。如果未设置或所设置的值小于1,Java将使用默认值50。
        bootstrap.option(ChannelOption.SO_BACKLOG, 1024);

        // SO_REUSEADDR允许启动一个监听服务器并捆绑其众所周知端口,
        // 即使以前建立的将此端口用做他们的本地端口的连接仍存在。
        // 这通常是重启监听服务器时出现,若不设置此选项,则bind时将出错。
        // SO_REUSEADDR允许在同一端口上启动同一服务器的多个实例,
        // 只要每个实例捆绑一个不同的本地IP地址即可。对于TCP,
        // 我们根本不可能启动捆绑相同IP地址和相同端口号的多个服务器。
        // SO_REUSEADDR允许单个进程捆绑同一端口到多个套接口上,
        // 只要每个捆绑指定不同的本地IP地址即可。这一般不用于TCP服务器。
        // SO_REUSEADDR允许完全重复的捆绑:当一个IP地址和端口绑定到某个套接口上时,
        // 还允许此IP地址和端口捆绑到另一个套接口上。一般来说,这个特性仅在支持多播的系统上才有,
        // 而且只对UDP套接口而言(TCP不支持多播)
        bootstrap.option(ChannelOption.SO_REUSEADDR, true);

        // Netty4使用对象池,重用缓冲区
        bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
        bootstrap.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);

        // 绑定线程池
        bootstrap.group(group, bossGroup);
        // 指定使用的channel
        bootstrap.channel(NioServerSocketChannel.class);
        // 绑定监听端口
        ;
        // 绑定客户端连接时候触发操作
        bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                System.out.println("报告");
                System.out.println("信息:有一客户端链接到本服务端");
                System.out.println("IP:" + ch.localAddress().getHostName());
                System.out.println("Port:" + ch.localAddress().getPort());
                System.out.println("报告完毕");

                ch.pipeline().addLast(new StringEncoder(Charset.forName("GBK")));
                System.out.println("客户端触发操作");
                ch.pipeline().addLast(new EchoServerHandler()); // 客户端触发操作
                ch.pipeline().addLast(new ByteArrayEncoder());
            }
        });
        ChannelFuture cf = bootstrap.bind(this.port).sync(); // 服务器异步创建绑定
        System.out.println(EchoServer.class + " 启动正在监听: " + cf.channel().localAddress());
        cf.channel().closeFuture().sync(); // 关闭服务器通道
    } finally {
        // 释放线程池资源
        group.shutdownGracefully().sync();
        bossGroup.shutdownGracefully().sync();
    }
}

}

class EchoServerHandler extends ChannelInboundHandlerAdapter {

/**
 * channelAction channel 通道 action 活跃的
 * 当客户端主动链接服务端的链接后,这个通道就是活跃的了。也就是客户端与服务端建立了通信通道并且可以传输数据
 */
public void channelActive(ChannelHandlerContext ctx) throws Exception {
    System.out.println(ctx.channel().localAddress().toString() + " 通道已激活!");
}

/**
 * channelInactive channel 通道 Inactive 不活跃的
 * 当客户端主动断开服务端的链接后,这个通道就是不活跃的。也就是说客户端与服务端的关闭了通信通道并且不可以传输数据
 */
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
    System.out.println(ctx.channel().localAddress().toString() + " 通道不活跃!并且关闭。");
    // 关闭流
    ctx.close();
}

/**
 * 功能:读取服务器发送过来的信息
 */
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    // 第一种:接收字符串时的处理

    ByteBuf buf = (ByteBuf) msg;
    byte[] buffer = new byte[buf.readableBytes()];
    buf.readBytes(buffer, 0, buffer.length);
    String rev = new String(buffer);
    System.out.println("客户端收到服务器数据:" + rev);

    ctx.writeAndFlush(rev);
}

/**
 * 功能:读取完毕客户端发送过来的数据之后的操作
 */
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
    System.out.println("服务端接收数据完毕..");
    // 第一种方法:写一个空的buf,并刷新写出区域。完成后关闭sock channel连接。
    // ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
    // ctx.flush();
    // ctx.flush(); //
    // 第二种方法:在client端关闭channel连接,这样的话,会触发两次channelReadComplete方法。
    // ctx.flush().close().sync(); // 第三种:改成这种写法也可以,但是这中写法,没有第一种方法的好。
}

/**
 * 功能:服务端发生异常的操作
 */
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    ctx.close();
    System.out.println("异常信息:\r\n");
    cause.printStackTrace();

}

}

猜你喜欢

转载自www.cnblogs.com/jiecaoge/p/11131569.html