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();

	}
}

  

转载于:https://www.cnblogs.com/jiecaoge/p/11131974.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值