《从零开始搭建游戏服务器》Netty导入创建Socket服务器

        对于普通Web应用后端的开发,常常会借助一些成熟的框架,例如SpringMVC,游戏的服务器区别于此的特点是:游戏服务器需要有比较大的吞吐量,而且通常为Socket长连接,所以需要一个分布式架构。当前比较流行的网络通信框架都为NIO(非阻塞网络通信)框架,比较有代表性的有:NettyMina

      

一、优势对比:

        Netty是由JBoss提供的Java开发框架,适用于开发高性能、高稳定性的客户端或者服务端网络通信框架,相较于Mina有如下几点优点:

1.支持多种编解码方式,当然也包括当前游戏开发中应用最广的Google提供的Protobuff编解码方式;

2.典型的NIO架构,非阻塞异步的事件驱动网络通信架构,吞吐量高,支持高并发;


二、资源下载:

1.netty-all-4.0.42.Final.jar:在Netty官网下载所需版本的jar包,不同版本的Netty差别比较大,我选择的是Netty4.0.42 Final版本。

2.Log4j或common-logging:这是用于打印和记录日志的工具,非常实用,详细的使用方法可以参考:最详细的Log4j使用教程,当然也有更简单方便的一个工具:common-logging,使用方法参考: Apache Commons-logging使用实例


三、Socket服务:

1.新建一个Java工程,步骤:File —— new —— Java Project;

2.资源导入:

        解压上面下载的Netty4.0.42压缩包,将netty-4.0.42.Final\jar\all-in-one目录下面的netty-all-4.0.42.Final.jar和commons-logging-1.2中的commons-logging-1.2.jar一起复制到eclipse中项目的libs目录下面(没有这个目录的话可以自行创建,专门用于存放外部导入的jar包):


3.编写服务代码:

        在src目录下新建一个包名com.tw.login.server,然后新建两个类:

LoginSocketServer.java:用于管理Socket,例如:ip端口绑定,Socket启动和关闭等;

SocketServerHandler.java:Socket数据接收监听和解析中心;

        大致过程就是我们在LoginSocketServer中启动一个ServerBootsrap服务器,并且绑定线程组,并且绑定用来监听数据的继承自SocketChannel的自定义监听器类,在其中设置网络传输数据的编码方式,然后设置连接到此服务器需要访问的IP和端口。

        关于传输数据协议,这里我们先以最简单的字符串为例,以("\n")为结尾分割的解码器作为数据协议格式,关于更复杂的数据类型协议在后面的篇章会继续设计。


LoginSocketServer源码:

public class LoginSocketServer {
	private static final Log logger = LogFactory.getLog(LoginSocketServer.class);
	private static final String IP = "127.0.0.1";
	private static final int PORT = 8088;
	
	//分配用于处理业务的线程组数量
	protected static final int BisGroupSize = Runtime.getRuntime().availableProcessors()*2;
	//每个线程组中线程的数量
	protected static final int worGroupSize = 4;
	
	private static final EventLoopGroup bossGroup = new NioEventLoopGroup(BisGroupSize);
	private static final EventLoopGroup workerGroup = new NioEventLoopGroup(worGroupSize);
	
	protected static 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服务器...");
		run();
	}
	
}
SocketServerHandler源码:

public class SocketServerHandler extends SimpleChannelInboundHandler<String> {
	private static final Log logger = LogFactory.getLog(LoginSocketServer.class);

	@Override
	public void exceptionCaught(ChannelHandlerContext arg0, Throwable arg1) throws Exception {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void channelRead(ChannelHandlerContext arg0, Object msg) throws Exception {
		// TODO Auto-generated method stub
        logger.info("数据内容:data="+data);
	}
}

为了测试还需要创建一个客户端,其实创建过程很简单:直接复制一份上述服务器的代码,稍作修改即可:
LoginSocketClient源码:

private static final Log logger = LogFactory.getLog(LoginSocketClient.class);
	private static final String IP = "127.0.0.1";
	private static final int PORT = 8088;
	
	private static final 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 {
				// TODO Auto-generated method stub
				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 SocketClientHandler());
			}
		});
		
		// 连接服务端
        Channel ch = bootstrap.connect(IP,PORT).sync().channel();
        
        
        ch.writeAndFlush("客户端数据"+"\r\n");
		
		logger.info("向Socket服务器发送数据:"+"客户端数据"+"\r\n");
	}
	
	public static void  main(String[] args){
		logger.info("开始连接Socket服务器...");
		try {
			run();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			group.shutdownGracefully();
		}
	}
SocketClientHandler源码:

public class SocketClientHandler extends SimpleChannelInboundHandler<String> {
	private static final Log logger = LogFactory.getLog(LoginSocketClient.class);

	@Override
	public void exceptionCaught(ChannelHandlerContext arg0, Throwable arg1) throws Exception {
		// TODO Auto-generated method stub
	}

	@Override
	public void channelRead(ChannelHandlerContext arg0, Object msg) throws Exception {
		// TODO Auto-generated method stub
		String data = msg.toString();
		logger.info("数据内容:data="+data);
	}

	@Override
	protected void channelRead0(ChannelHandlerContext arg0, String data) throws Exception {
		// TODO Auto-generated method stub
		logger.info("数据内容:data="+data);
	}
}

4.运行:

        运行方式其实很简单,直接在Eclipse中选中项目根目录,右键——Run As——Java Application,注意要先运行服务端工程再运行客户端工程,运行结果如下:


服务端Console结果:



客户端Console结果:



阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/linshuhe1/article/details/53671820
个人分类: java服务器
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭