前言,
说明
引入netty的pom
<!-- netty --> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.6.Final</version> </dependency>
然后下面要写四个类,
SocketServer ---服务器启动类
SocketServerHandler ---服务器消息处理
SocketClientHandlerTest ---测试使用的的客户端消息处理
SocketClientTest ---测试使用的客户端启动类
看这几个类,handle是启动类中的一个小部件,这个是2者的关系,具体代码如下
SocketServer
/** * Copyright (C), 2015-2018 * FileName: SocketServer * Author: zhao * Date: 2018/6/10 21:26 * Description: netty服务器 * History: * <author> <time> <version> <desc> * 作者姓名 修改时间 版本号 描述 */ package com.lizhaoblog.net; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.DelimiterBasedFrameDecoder; import io.netty.handler.codec.Delimiters; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; /** * 〈一句话功能简述〉<br> * 〈netty服务器〉 * * @author zhao * @create 2018/6/10 * @since 1.0.0 */ public class SocketServer { private static final Logger logger = LoggerFactory.getLogger(SocketServer.class); private static final String IP = "127.0.0.1"; private static final int PORT = 8088; /** * 分配用于处理业务的线程组数量 */ private static final int BIS_GROUP_SIZE = Runtime.getRuntime().availableProcessors() * 2; /** * 每个线程组中线程的数量 */ private static final int WORK_GROUP_SIZE = 4; private static EventLoopGroup bossGroup = new NioEventLoopGroup(BIS_GROUP_SIZE); private static EventLoopGroup workerGroup = new NioEventLoopGroup(WORK_GROUP_SIZE); public void run() throws Exception { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workerGroup); bootstrap.channel(NioServerSocketChannel.class); bootstrap.childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); // 以("\n")为结尾分割的 解码器 pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); pipeline.addLast("decoder", new StringDecoder()); pipeline.addLast("encoder", new StringEncoder()); pipeline.addLast(new SocketServerHandler()); } }); bootstrap.bind(IP, PORT).sync(); logger.info("Socket服务器已启动完成"); } protected static void shutdown() { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } public static void main(String[] args) throws Exception { logger.info("开始启动Socket服务器..."); new SocketServer().run(); } }
SocketServerHandler
/** * Copyright (C), 2015-2018 * FileName: SocketServerHandler * Author: zhao * Date: 2018/6/10 21:27 * Description: SocketServer的消息处理类 * History: * <author> <time> <version> <desc> * 作者姓名 修改时间 版本号 描述 */ package com.lizhaoblog.net; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; /** * 〈一句话功能简述〉<br> * 〈SocketServer的消息处理类〉 * * @author zhao * @create 2018/6/10 * @since 1.0.0 */ public class SocketServerHandler extends SimpleChannelInboundHandler<String> { private static final Logger logger = LoggerFactory.getLogger(SocketServer.class); @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable throwable) { logger.debug("异常发生", throwable); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { super.channelRead(ctx, msg); } @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) { logger.info("数据内容:data=" + msg); String result = "小李,我是服务器,我收到你的信息了。"; //这行很重要,StringDecoder以这个作为消息分割, // 如果没有换行符的话,服务端就没办法接受到 result += "\r\n"; ctx.writeAndFlush(result); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { logger.info("建立连接"); super.channelActive(ctx); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { logger.info("连接断开"); super.channelInactive(ctx); } }SocketClientHandlerTest
/** * Copyright (C), 2015-2018 * FileName: SocketClientHandlerTest * Author: zhao * Date: 2018/6/10 21:36 * Description: SocketClient的测试类 * History: * <author> <time> <version> <desc> * 作者姓名 修改时间 版本号 描述 */ package com.lizhaoblog.net; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; /** * 〈一句话功能简述〉<br> * 〈SocketClient的测试类〉 * * @author zhao * @create 2018/6/10 * @since 1.0.0 */ public class SocketClientHandlerTest extends SimpleChannelInboundHandler<String> { private static final Logger logger = LoggerFactory.getLogger(SocketClientTest.class); @Override public void exceptionCaught(ChannelHandlerContext arg0, Throwable arg1) { logger.info("异常发生", arg1); } @Override public void channelRead(ChannelHandlerContext arg0, Object msg) throws Exception { super.channelRead(arg0, msg); } @Override protected void channelRead0(ChannelHandlerContext arg0, String data) { logger.info("数据内容:data=" + data); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { logger.info("客户端连接建立"); super.channelActive(ctx); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { logger.info("客户端连接断开"); super.channelInactive(ctx); } }SocketClientTest
/** * Copyright (C), 2015-2018 * FileName: SocketServerTest * Author: zhao * Date: 2018/6/10 21:35 * Description: SocketServer的测试类 * History: * <author> <time> <version> <desc> * 作者姓名 修改时间 版本号 描述 */ package com.lizhaoblog.net; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.DelimiterBasedFrameDecoder; import io.netty.handler.codec.Delimiters; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; /** * 〈一句话功能简述〉<br> * 〈SocketServer的测试类〉 * * @author zhao * @create 2018/6/10 * @since 1.0.0 */ public class SocketClientTest { private static final Logger logger = LoggerFactory.getLogger(SocketClientTest.class); private static final String IP = "127.0.0.1"; private static final int PORT = 8088; private static EventLoopGroup group = new NioEventLoopGroup(); @SuppressWarnings("rawtypes") protected static void run() throws Exception { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group); bootstrap.channel(NioSocketChannel.class); bootstrap.handler(new ChannelInitializer() { @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); /* * 这个地方的 必须和服务端对应上。否则无法正常解码和编码 * * 解码和编码 我将会在下一张为大家详细的讲解。再次暂时不做详细的描述 * * */ pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); pipeline.addLast("decoder", new StringDecoder()); pipeline.addLast("encoder", new StringEncoder()); pipeline.addLast(new SocketClientHandlerTest()); } }); // 连接服务端 ChannelFuture channelFuture = bootstrap.connect(IP, PORT).sync(); String msg = "小王,我是客户端"; //这行很重要,StringDecoder以这个作为消息分割, // 如果没有换行符的话,服务端就没办法接受到 msg += "\r\n"; channelFuture.channel().writeAndFlush(msg); logger.info("向Socket服务器发送数据:" + msg); channelFuture.channel().closeFuture().sync(); } public static void main(String[] args) { logger.info("开始连接Socket服务器..."); try { run(); } catch (Exception e) { e.printStackTrace(); } finally { group.shutdownGracefully(); } } }测试方法:先启动Main函数中的main或者SocketServer中的main函数,2者一样,然后启动SocketClientTest中的函数,能做到收发就ok了,像这样
服务器 2018-06-10 21:48:45.763 INFO com.lizhaoblog.net.SocketServer - Socket服务器已启动完成 2018-06-10 21:48:51.277 DEBUG io.netty.buffer.AbstractByteBuf - -Dio.netty.buffer.bytebuf.checkAccessible: true 2018-06-10 21:48:51.281 DEBUG io.netty.util.ResourceLeakDetectorFactory - Loaded default ResourceLeakDetector: io.netty.util.ResourceLeakDetector@4a38e446 2018-06-10 21:48:51.291 INFO com.lizhaoblog.net.SocketServer - 建立连接 2018-06-10 21:48:51.303 DEBUG io.netty.util.Recycler - -Dio.netty.recycler.maxCapacityPerThread: 32768 2018-06-10 21:48:51.303 DEBUG io.netty.util.Recycler - -Dio.netty.recycler.maxSharedCapacityFactor: 2 2018-06-10 21:48:51.303 DEBUG io.netty.util.Recycler - -Dio.netty.recycler.linkCapacity: 16 2018-06-10 21:48:51.303 DEBUG io.netty.util.Recycler - -Dio.netty.recycler.ratio: 8 2018-06-10 21:48:51.329 INFO com.lizhaoblog.net.SocketServer - 数据内容:data=小王,我是客户端 客户端 2018-06-10 21:48:51.246 INFO com.lizhaoblog.net.SocketClientTest - 客户端连接建立 2018-06-10 21:48:51.251 DEBUG io.netty.util.Recycler - -Dio.netty.recycler.maxCapacityPerThread: 32768 2018-06-10 21:48:51.251 DEBUG io.netty.util.Recycler - -Dio.netty.recycler.maxSharedCapacityFactor: 2 2018-06-10 21:48:51.251 DEBUG io.netty.util.Recycler - -Dio.netty.recycler.linkCapacity: 16 2018-06-10 21:48:51.251 DEBUG io.netty.util.Recycler - -Dio.netty.recycler.ratio: 8 2018-06-10 21:48:51.255 INFO com.lizhaoblog.net.SocketClientTest - 向Socket服务器发送数据:小王,我是客户端 2018-06-10 21:48:51.340 INFO com.lizhaoblog.net.SocketClientTest - 数据内容:data=小李,我是服务器,我收到你的信息了。 2018-06-10 21:49:02.674 INFO com.lizhaoblog.net.SocketClientTest - 异常发生
接下来就是改造下简单的实例了,spring框架在java体系中确实是至关重要的一个东西,所以下一篇应该是将netty和spring结合起来。
上面的代码在码云上 https://gitee.com/lizhaoandroid/JgServer
可以加qq群一起探讨Java游戏服务器开发的相关知识 676231524