游戏服务器Mina框架开发

2,358 阅读10分钟

作者:老九—技术大黍

社交:知乎

公众号:老九学堂(新人有惊喜)

特别声明:原创不易,未经授权不得转载或抄袭,如需转载可联系笔者授权

前言

如果要使用Java语言来开发游戏服务器,那么Mina框架是当之无愧的首先。

什么是Mina

Mina是一个开源的网络应用框架,这种框架帮助应用程序员便捷的开始出高性能、高并发处理的网络应用。Mina框架使用Java NI封装了TCP/IP和UDP/IP协议,封装结果表现基于事件的异步API给应用程序员使用。

因此,Mina又被叫做:NIO框架库、C/S框架库或者网络套接字库。

基于mina框架有扩展的服务器:Asyncweb、FtpServer和SSHd服务器。

Mina的功能

Mina提供了常用如下功能:

  • 为各种传输类型的提供统一的API接口
    • 使用Java NIO技术的TCP/IP和UDP/IP
    • 使用RXTX的串行化通讯(RS232)
    • In-VM管道通讯
    • 自定义的实现为各种传输类型的提供统一的API接口
  • 类似Servlet技术过滤器的Fitler接口
  • 低阶和高阶API
    • 使用ByteBuffers低阶API
    • 使用自定义消息对象的高阶API
  • 高级自定义线程模型
    • 单线程
    • 单个线程池
    • 多个线程池
  • 通过StreamIoHandler支持基本的IO流

服务端代码演示

package com.mr.game.server.app;
import java.util.Date;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;

public class TimeServerHandler extends IoHandlerAdapter{
	
    public void exceptionCaught( IoSession session, Throwable cause ) throws Exception
    {
        cause.printStackTrace();
    }

    
    public void messageReceived( IoSession session, Object message ) throws Exception
    {
        String str = message.toString();
        if( str.trim().equalsIgnoreCase("quit") ) {
            session.close();
            return;
        }

        Date date = new Date();
        String messge = "Hello(中文), today is " + date.toString();
        session.write("Your request: " + str);
        
        System.out.println("Message written..." + str);
    }

    
    public void sessionIdle( IoSession session, IdleStatus status ) throws Exception
    {
        System.out.println( "IDLE " + session.getIdleCount( status ));
    }
    
    public void sessionOpened(IoSession session) throws Exception{
		//none
    }
}
package com.mr.game.server.app;
import java.io.IOException;
import java.nio.charset.Charset;
import java.net.InetSocketAddress;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.apache.mina.core.session.IdleStatus;

/**
 * 功能:这是第一个Mina服务端应用示例
 * 作者:技术大黍
 *
 */
public class App 
{
	private static final int PORT = 9123;
	
    public static void main( String[] args )
    {
    	//定义一个接收器
        IoAcceptor acceptor = new NioSocketAcceptor();
        //添加日志
        acceptor.getFilterChain().addLast("logger",new LoggingFilter());
        acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"))));
        //处理客户端请求
        acceptor.setHandler(new TimeServerHandler());
        //设置通讯的字节流量单位
        acceptor.getSessionConfig().setReadBufferSize(2048);
        acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
        try{
	        //绑定端口
	        acceptor.bind(new InetSocketAddress(PORT));
	        System.out.println("The mina server has been started.");
	    }catch(IOException e){
	    	e.printStackTrace();
	    }
    }
}

小结

  • mina是一个封装Java NIO技术的框架
  • mina实现了网络异步通讯的概念
  • mina把网络的物理通讯与业务逻辑代码解耦合,方便了应用程序员编程

Mina框架原理

Mina架构

Mina的框架如下图所示:

image-20210429113744186.png

Mina封装了TCP/UDP/串行化以及虚通道通讯等物理网络通讯机制,应用程序员只关注自己的业务逻辑即可。

image-20210429114041216.png

  • Mina分为三层结构
    • I/O服务层--执行实际的I/O操作
    • I/O过滤器链路层--过滤和转换字节为期望的数据结构
    • I/O句柄处理层--在这层放实际的业务逻辑代码
  • 创建一个最基本的Mina应用,必须做以下几步
    • 创建I/O服务层--选择一个可以使用服务(Acceptor)或者自己创建服务
    • 创建过滤器链--选择一个现存的过滤器,或者自己创建一个过滤器
    • 创建一个I/O句柄对象--写业务逻辑,处理不同的消息

服务端架构

image-20210429114847255.png

服务端架构

服务端在一个端口监听网络请求,处理来自网络的消息,响应这些网络请求。mina使用Session来处理客户端(基于TCP和UDP协议的客户端),IOAcceptor类监听网络连接和数据包。有一个新连接,那么mina会个新的session对象,从session获取的数据包都是穿过指定的过滤器链。因此,过滤器链可修改数据包。比如,把字节信息转换成高阶对象使用,打包和解码特定的数据包等。最后打包/转换对象到IOHandler中,因此IOHandler用来填充业务逻辑代码。

小结

  • mina框架是一个封装各种物理底层通讯机制,让应用程序员关注业务逻辑
  • mina框架分为三层:IO服务层、过滤器链路层和句柄处理层
  • IOAcceptor对象封装了网络物理链接
  • 服务对象处理的结果是产生一个Sessions对象
  • 继承IoHandlerAdapter实现业务逻辑

IoService服务层

IoService服务

IoService设计用来抽象服务端网络链接,而IoConnector接口用来抽象客户端链接。 IoService接口提供了基本的I/O服务,并且管理I/O的Session功能。它是mina框架中最关键的部分。

image-20210429115111530.png

IoService提供以下功能

  • session管理:创建和删除session,以及侦测闲置连接
  • 过滤器链管理:处理过滤器,允许用户任意修改链
  • 句柄调用:当有接收到消息时调用处理句柄(基于事件驱动概念)
  • 统计管理:实时更新消息发送的数量、字节发送数量等
  • 监听管理:监听服务以便客户端可以连接
  • 通讯管理:处理通讯双方的数据传输

核心接口方法

image-20210429115228306.png

image-20210429143939525.png

image-20210429143957375.png

image-20210429144013258.png

IoService详解

image-20210429144029864.png

IoService对象实现两个最重要的类:IoAcceptor和IoConnector接口。

服务端构建一个服务需要实现一个IoAcceptor接口的实现类。客户端需要实现IoConnector的实现类。

IoAcceptor接口定义一个业务逻辑方法accept()用来负责创建一个新的连接。服务端接收网络连接请求。

mina对象各种类型的物理网络连接,提供了如下的四种IoAcceptor实现类。

IoService对象实现两个最重要的类:IoAcceptor和IoConnector接口 服务端构建一个服务需要实现一个IoAcceptor接口的实现类。客户端需要实现IoConnector的实现类 IoAcceptor接口定义一个业务逻辑方法accept()用来负责创建一个新的连接。服务端接收网络连接请求 mina对象各种类型的物理网络连接,提供了如下的四种IoAcceptor实现类

image-20210429144139648.png

image-20210429144354003.png

小结

  • IoService是mina的核心API
  • IoService封装了各种物理网络连接,提供了四种IoService实现的工具类:NioSocketAcceptor 、NioDatagramAcceptor 、AprSocketAcceptor 、VmPipeSocketAcceptor
  • IoService主要提供session管理、过滤器管理、句柄处理(消息/事件处理)和通讯管理等功能

Session、过滤器和事件处理

Session的状态

Session是mina框架的核心。每次一个新客户端连接到服务器,那么会创建一个新的session对象,然后保存在内存到客户端连接。

image-20210429144521739.png

session有三种状态:

  • 连接
  • 闲置
  • 关闭

session用来保存连接的持久化信息,以及额外的服务器信息。这些信息是服务器处理客户端请求时需要的信息,并且持续整个session的生命周期 session在它的生命周期中有如下状态:

  • Connected: 一个session被创建并且可以被使用
  • Idle: 一个不处理任何请求
    • Idle for read: 持续没有从session读的闲置时间
    • Idle for write: 持续没有向session写的闲置时间
    • Idle for both: 即没有读也没有写的闲置时间
  • Closing: 一个session正在关闭的状态
  • Closed: 一个session已经被关闭了的状态

Session配置

使用一个Session时需要配置,对session有两种配置:

  • 标准配置
    • 接收buffer的大小
    • 发送buffer的大小
    • 闲置时间
    • 写超时时间
  • 自定义配置,除了以上标准配置可能还有额外信息需要知道。比如,需要知道已经连接了多少个连接的客户端数量。

session是一个实现Map接口的实现类,使用键/值的方式来保存值。当一个session被创建之后,有一个容器也会自动被创建

image-20210429145156786.png

不要在session保存过大的信息,或者生命周期很长的数据结构内容。也就是,被保存的对象的生命周期不能大于session的生命周期 每个session会关联一个过滤器链,这些链会在网络连接请求和消息读写过程处理

Session的默认功能

每个session会保存以下记录信息

  • 网络发送和接收的字节数量
  • 网络发送和接收的消息数量
  • 闲置状态
  • 吞吐量
  • 其它有用信息

句柄(事件)处理,一个session至少会关联一个句柄(事件)处理。这个句柄(事件)主要负责向应用分发消息。同时,句柄也负责通过session发送响应的数据包--使用write()方法即可

session.write(<消息内容>);

了解默认的过滤器

image-20210429145313133.png

重写事件

我们继承IoAdapter而不是直接实现IoFilter接口时,需要重写相应的事件处理方法。

image-20210429145436621.png

image-20210429145449775.png

常用事件

image-20210429145909348.png

小结

  • session是mina框架的核心每个客户端连接会有一个session对象对应
  • 与客户端通讯是通过session的read和write方法来实现
  • session有三种主要状态:连接、闲置和关闭状态
  • 一个session关联一个过滤器链,过滤器实现事件处理机制
  • 过滤器有7个主要事件:
    • sessionCreate
    • sessionOpened
    • sessionClosed
    • sessionIdle
    • exceptionCaught
    • messageReceived
    • messageSent

Mina核心API讲解

为什么使用IoBuffer

  1. IoBuffer是Mina应用使用字节buffer对象。它用来替换NIO包中的ByteBuffer类。
  2. 不使用ByteBuffer有两个理由
    • 不提供getter和setter方法
    • 对于可变长度的数据很处理
  3. IoBuffer是一个抽象类,因此不能直接实例化,需要实现allocate()方法来实例化

image-20210429150114479.png

设置和使用buffer

设置buffer一般有两种方式

  • 默认分配为堆buffer-- IoBuffer.setUseDirectBuffer(false);
  • 返回一个堆buffer -- IoBuffer buf = IoBuffer.allocate(1024);

创建自动可扩展buffer

  • IoBuffer buffer = IoBuffer.allocate(8);
  • buffer.setAutoExpand(true);
  • buffer.putString("12345678", encoder);
  • buffer.put((byte)10); // Add more to this buffer

创建可伸缩buffer

  • IoBuffer buffer = IoBuffer.allocate(16);
  • buffer.setAutoShrink(true);
  • buffer.put((byte)1);
  • System.out.println("Initial Buffer capacity = "+buffer.capacity());
  • buffer.shrink();
  • System.out.println("Initial Buffer capacity after shrink = "+buffer.capacity());
  • buffer.capacity(32);
  • System.out.println("Buffer capacity after incrementing capacity to 32 = "+buffer.capacity());
  • buffer.shrink();
  • System.out.println("Buffer capacity after shrink= "+buffer.capacity());

掌握ProtocolCodecFilter

TCP保证正确的发送数据包,但是不保证发送方的写操作能够在接收方正确的读取。

在Mina术语中,如果没有使用 ProtocolCodecFilter而进行IoSession.write(消息)操作,会导致接收产生多个messageReceive事件发生,以及多次IoSession.write(消息)操作会导致单个messageReceived事件发生。这种如果服务端和客户端在同一台机器上不会发生,但是在不同机器上一定会发生。

大多数和的网络应用都需要当前消息结束之后,才能进行下一个消息的操作。

应用程序员在可以在IoHander类中实现以上所有逻辑,但是如果使用了ProtocolCodecFilter对象,那么可以我们编程更加便捷。使用ProtocolCodecFilter对象,可以实现把网络协议逻辑与我们在IoHandler对象中的业务逻辑解耦合。因为,我们的客户端是针对C++的,所以,传输的消息都文本字符串作为载体,因此,我们在服务端只需要这样使用即可。

image-20210429150405412.png

小结

  • IoBuffer实现了可伸缩的buffer功能
  • ProtocolCodecFilter简化了消息编码和解码方式

最后

记得给大黍❤️关注+点赞+收藏+评论+转发❤️

作者:老九学堂—技术大黍

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。