前言,
说明
引入netty的pom
io.netty
netty-all
4.1.6.Final
然后下面要写四个类, SocketServer ---服务器启动类 SocketServerHandler ---服务器消息处理 SocketClientHandlerTest ---测试使用的的客户端消息处理 SocketClientTest ---测试使用的客户端启动类
看这几个类,handle是启动类中的一个小部件,这个是2者的关系,具体代码如下 SocketServer
/**
* Copyright (C), 2015-2018
* FileName: SocketServer
* Author: zhao
* Date: 2018/6/10 21:26
* Description: netty服务器
* History:
*
* 作者姓名 修改时间 版本号 描述
*/
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;
/**
* 〈一句话功能简述〉
* 〈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() {
@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:
*
* 作者姓名 修改时间 版本号 描述
*/
package com.lizhaoblog.net;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/**
* 〈一句话功能简述〉
* 〈SocketServer的消息处理类〉
*
* @author zhao
* @create 2018/6/10
* @since 1.0.0
*/
public class SocketServerHandler extends SimpleChannelInboundHandler {
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:
*
* 作者姓名 修改时间 版本号 描述
*/
package com.lizhaoblog.net;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/**
* 〈一句话功能简述〉
* 〈SocketClient的测试类〉
*
* @author zhao
* @create 2018/6/10
* @since 1.0.0
*/
public class SocketClientHandlerTest extends SimpleChannelInboundHandler {
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:
*
* 作者姓名 修改时间 版本号 描述
*/
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;
/**
* 〈一句话功能简述〉
* 〈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游戏服务器开发的相关知识 676231564