apache mina server 是一个网络通信应用框架,也就是说,它主要是对基于 tcp/ip、udp/ip协议栈的通信框架(当然,也可以提供 java 对象的序列化服务、虚拟机管道通信服务等),mina 可以帮助我们快速开发高性能、高扩展性的网络通信应用,mina 提供了事件驱动、异步(mina 的异步 io 默认使用的是 java nio 作为底层支持)操作的编程模型。
从官网文档“mina based application architecture”中可以看到mina作为一个通信层框架,在实际应用所处的位置,如图所示:
mina位于用户应用程序和底层java网络api(和in-vm通信)之间,我们开发基于mina的网络应用程序,就无需关心复杂的通信细节。
应用整体架构
再看一下,mina提供的基本组件,如图所示:
也就是说,无论是客户端还是服务端,使用mina框架实现通信的逻辑分层在概念上统一的,即包含如下三层:
i/o service – performs actual i/o
i/o filter chain – filters/transforms bytes into desired data structures and vice-versa
i/o handler – here resides the actual business logic
想要开发基于mina的应用程序,你只需要做如下事情:
create an i/o service – choose from already available services (*acceptor) or create your own
create a filter chain – choose from already existing filters or create a custom filter for transforming request/response
create an i/o handler – write business logic, on handling different messages
下面看一下使用mina的应用程序,在服务器端和客户端的架构细节:
服务器端架构
服务器端监听指定端口上到来的请求,对这些请求经过处理后,回复响应。它也会创建并处理一个链接过来的客户会话对象(session)。服务器端架构如图所示:
对服务器端的说明,引用官网文档,如下所示:
ioacceptor listens on the network for incoming connections/packets
for a new connection, a new session is created and all subsequent request from ip address/port combination are handled in that session
all packets received for a session, traverses the filter chain as specified in the diagram. filters can be used to modify the content of packets (like converting to objects, adding/removing information etc). for converting to/from raw bytes to high level objects, packetencoder/decoder are particularly useful
finally the packet or converted object lands in iohandler. iohandlers can be used to fulfill business needs.
客户端架构
客户端主要做了如下工作:
连接到服务器端
向服务器发送消息
等待服务器端响应,并处理响应
客户端架构,如图所示:
对客户端架构的说明,引用官网文档内容,如下所示:
client first creates an ioconnector (mina construct for connecting to socket), initiates a bind with server
upon connection creation, a session is created and is associated with connection
application/client writes to the session, resulting in data being sent to server, after traversing the filter chain
all the responses/messages received from server are traverses the filter chain and lands at iohandler, for processing
应用实例开发
下面根据上面给出的架构设计描述,看一下mina(版本2.0.7)自带的例子,如何实现一个简单的c/s通信的程序,非常容易。
服务端
首先,服务器端需要使用的组件有ioadaptor、iohandler、iofilter,其中iofilter可选.
我们基于mina自带的例子进行了简单地修改,实现服务端iohandler的代码如下所示:
<code>01</code>
<code>package</code> <code>org.shirdrn.mina.server;</code>
<code>02</code>
<code>03</code>
<code>import</code> <code>org.apache.mina.core.service.iohandleradapter;</code>
<code>04</code>
<code>import</code> <code>org.apache.mina.core.session.idlestatus;</code>
<code>05</code>
<code>import</code> <code>org.apache.mina.core.session.iosession;</code>
<code>06</code>
<code>import</code> <code>org.slf4j.logger;</code>
<code>07</code>
<code>import</code> <code>org.slf4j.loggerfactory;</code>
<code>08</code>
<code>09</code>
<code>public</code> <code>class</code> <code>tinyserverprotocolhandler</code><code>extends</code> <code>iohandleradapter {</code>
<code>10</code>
<code></code><code>private</code> <code>final</code> <code>static</code> <code>logger logger = loggerfactory.getlogger(tinyserverprotocolhandler.</code><code>class</code><code>);</code>
<code>11</code>
<code></code>
<code>12</code>
<code></code><code>@override</code>
<code>13</code>
<code></code><code>public</code> <code>void</code> <code>sessioncreated(iosession session) {</code>
<code>14</code>
<code></code><code>session.getconfig().setidletime(idlestatus.both_idle,</code><code>10</code><code>);</code>
<code>15</code>
<code></code><code>}</code>
<code>16</code>
<code>17</code>
<code>18</code>
<code></code><code>public</code> <code>void</code> <code>sessionclosed(iosession session)</code><code>throws</code> <code>exception {</code>
<code>19</code>
<code></code><code>logger.info(</code><code>"closed"</code><code>);</code>
<code>20</code>
<code>21</code>
<code>22</code>
<code>23</code>
<code></code><code>public</code> <code>void</code> <code>sessionopened(iosession session)</code><code>throws</code> <code>exception {</code>
<code>24</code>
<code></code><code>logger.info(</code><code>"opened"</code><code>);</code>
<code>25</code>
<code>26</code>
<code>27</code>
<code>28</code>
<code></code><code>public</code> <code>void</code> <code>sessionidle(iosession session, idlestatus status) {</code>
<code>29</code>
<code></code><code>logger.info(</code><code>"*** idle #"</code> <code>+ session.getidlecount(idlestatus.both_idle) +</code><code>" ***"</code><code>);</code>
<code>30</code>
<code>31</code>
<code>32</code>
<code>33</code>
<code></code><code>public</code> <code>void</code> <code>exceptioncaught(iosession session, throwable cause) {</code>
<code>34</code>
<code></code><code>session.close(</code><code>true</code><code>);</code>
<code>35</code>
<code>36</code>
<code>37</code>
<code>38</code>
<code></code><code>public</code> <code>void</code> <code>messagereceived(iosession session, object message)</code>
<code>39</code>
<code></code><code>throws</code> <code>exception {</code>
<code>40</code>
<code></code><code>logger.info(</code><code>"received : "</code> <code>+ message );</code>
<code>41</code>
<code></code><code>if</code><code>(!session.isconnected()) {</code>
<code>42</code>
<code>43</code>
<code>44</code>
<code>45</code>
<code>}</code>
这个版本中,iohandleradapter实现了iohandler接口,里面封装了一组用于事件处理的空方法,其中包含服务端和客户端的事件。在实际应用中,客户端可以选择客户端具有的事件,服务器端选择服务器端具有的事件,然后分别对这两类事件进行处理(有重叠的事件,如连接事件、关闭事件、异常事件等)。
客户端的iohandler的具体实现也是类似的,不过多累述。
下面看启动服务器的主方法类,代码如下所示:
<code>import</code> <code>java.net.inetsocketaddress;</code>
<code>import</code> <code>org.apache.mina.filter.codec.protocolcodecfilter;</code>
<code>import</code> <code>org.apache.mina.filter.codec.textline.textlinecodecfactory;</code>
<code>import</code> <code>org.apache.mina.transport.socket.socketacceptor;</code>
<code>import</code> <code>org.apache.mina.transport.socket.nio.niosocketacceptor;</code>
<code>public</code> <code>class</code> <code>tinyminaserver {</code>
<code></code><code>private</code> <code>final</code> <code>static</code> <code>logger log = loggerfactory.getlogger(tinyminaserver.</code><code>class</code><code>);</code>
<code></code><code>/** choose your favorite port number. */</code>
<code></code><code>private</code> <code>static</code> <code>final</code> <code>int</code> <code>port =</code><code>8080</code><code>;</code>
<code></code><code>public</code> <code>static</code> <code>void</code> <code>main(string[] args)</code><code>throws</code> <code>exception {</code>
<code></code><code>socketacceptor acceptor =</code><code>new</code> <code>niosocketacceptor();</code>
<code></code><code>acceptor.setreuseaddress(</code><code>true</code><code>);</code>
<code></code><code>acceptor.getfilterchain().addlast(</code><code>"codec"</code><code>,</code><code>new</code> <code>protocolcodecfilter(</code><code>new</code><code>textlinecodecfactory()));</code>
<code></code><code>// bind</code>
<code></code><code>acceptor.sethandler(</code><code>new</code> <code>tinyserverprotocolhandler());</code>
<code></code><code>acceptor.bind(</code><code>new</code> <code>inetsocketaddress(port));</code>
<code></code><code>log.info(</code><code>"listening on port "</code> <code>+ port);</code>
<code></code><code>log.info(</code><code>"server started!"</code><code>);</code>
<code></code><code>for</code> <code>(;;) {</code>
<code></code><code>log.info(</code><code>"r: "</code> <code>+ acceptor.getstatistics().getreadbytesthroughput() +</code><code>", w: "</code> <code>+ acceptor.getstatistics().getwrittenbytesthroughput());</code>
<code></code><code>thread.sleep(</code><code>3000</code><code>);</code>
客户端
实现客户端iohandler的代码如下所示:
<code>package</code> <code>org.shirdrn.mina.client;</code>
<code>public</code> <code>class</code> <code>tinyclientprotocolhandler</code><code>extends</code> <code>iohandleradapter {</code>
<code></code><code>private</code> <code>final</code> <code>static</code> <code>logger logger = loggerfactory</code>
<code></code><code>.getlogger(tinyclientprotocolhandler.</code><code>class</code><code>);</code>
<code></code><code>logger.info(</code><code>"client::created"</code><code>);</code>
<code></code><code>logger.info(</code><code>"client::closed"</code><code>);</code>
<code></code><code>logger.info(</code><code>"client::opened"</code><code>);</code>
<code></code><code>logger.info(</code><code>"client::*** idle #"</code>
<code></code><code>+ session.getidlecount(idlestatus.both_idle) +</code><code>" ***"</code><code>);</code>
<code></code><code>logger.info(</code><code>"client::exceptioncaught"</code><code>);</code>
<code></code><code>cause.printstacktrace();</code>
<code></code><code>public</code> <code>void</code> <code>messagesent(iosession session, object message)</code><code>throws</code> <code>exception {</code>
<code></code><code>logger.info(</code><code>"client::messagesent: "</code> <code>+ message);</code>
下面看启动客户端的主方法类,代码如下所示:
<code>import</code> <code>org.apache.mina.core.future.connectfuture;</code>
<code>import</code> <code>org.apache.mina.transport.socket.socketconnector;</code>
<code>import</code> <code>org.apache.mina.transport.socket.nio.niosocketconnector;</code>
<code>public</code> <code>class</code> <code>tinyminaclient {</code>
<code></code><code>private</code> <code>final</code> <code>static</code> <code>logger log = loggerfactory.getlogger(tinyminaclient.</code><code>class</code><code>);</code>
<code></code><code>socketconnector connector =</code><code>new</code> <code>niosocketconnector();</code>
<code></code><code>// connect</code>
<code></code><code>connector.getfilterchain().addlast(</code><code>"codec"</code><code>,</code><code>new</code> <code>protocolcodecfilter(</code><code>new</code><code>textlinecodecfactory()));</code>
<code></code><code>connector.sethandler(</code><code>new</code> <code>tinyclientprotocolhandler());</code>
<code></code><code>for</code> <code>(</code><code>int</code> <code>i =</code><code>0</code><code>; i <</code><code>10</code><code>; i++) {</code>
<code></code><code>connectfuture future = connector.connect(</code><code>new</code> <code>inetsocketaddress(port));</code>
<code></code><code>log.info(</code><code>"connect to port "</code> <code>+ port);</code>
<code></code><code>future.awaituninterruptibly();</code>
<code></code><code>future.getsession().write(string.valueof(i));</code>
<code></code><code>thread.sleep(</code><code>1500</code><code>);</code>
我们只是发送了十个数字,每发一次间隔1500ms。
测试上述服务器端与客户端交互,首先启动服务器端,监听8080端口。
接着启动客户端,连接到服务器端8080端口,然后发送消息,服务器端接收到消息后,直接将到客户端的连接关闭掉。