Apache MiNa 实现多人聊天室
Apache MiNa 实现多人聊天室
开发环境:
System:Windows
JavaSDK:1.6
IDE:eclipse、MyEclipse 6.6
开发依赖库:
Jdk1.4+、mina-core-2.0.4.jar、slf4j-api-1.5.11.jar、slf4j-log4j12-1.5.11.jar
Email:hoojo_@126.com
Blog:http://blog.csdn.net/IBM_hoojo
http://hoojo.cnblogs.com/
http://hoojo.blogjava.net
前不久用Socket写的聊天程序,主要是手机端程序通过Socket连接服务器端的ServerSocket,然后服务器端根据客户端发送过来统一规范的报文。进行解析再用smack框架转发到openfire服务器,最后由openfire服务器向客户端程序发送聊天信息。
最近发现socket服务器资源消耗比较大。我是采用阻塞式多线程通信方式,这种方式会造成大量的服务器资源浪费、长期的占用服务器的CUP调度权,并且会长时间阻塞程序,等待客户端连接、发送消息等。
为了解决上面的状况,Apache MiNa能很好的解决这个问题。Mina采用的是非阻塞式、单线程、NIO通信方式。
非阻塞式通信的思想是:让一个线程同时完成多件事,这个线程会利用完成这件事的空余时间去完成另一件事,一刻也不闲着。这个线程同时也会不断监控每件事情中需要处理时间的发生,发生一个就处理一件,然后继续监听各自事情。
一、介绍
首先,Mina是个什么东西?看下官方网站(http://mina.apache.org/)对它的解释:
Apache的Mina(Multipurpose Infrastructure Networked Applications)是一个网络应用框架,可以帮助用户开发高性能和高扩展性的网络应用程序;它提供了一个抽象的、事件驱动的异步API,使Java NIO在各种传输协议(如TCP/IP,UDP/IP协议等)下快速高效开发。
Apache Mina也称为:
NIO框架
网络套接字(networking socket)类库
事件驱动的异步API(注意:在JDK7中也新增了异步API)
总之:我们简单理解它是一个封装底层IO操作,提供高级操作API的通讯框架!
二、服务器端编码工作
第一步:
使用Apache MiNa框架,你需要下载jar
下载地址:http://mina.apache.org/dyn/closer.cgi/mina/2.0.4/apache-mina-2.0.4-bin.zip
你需要添加jar如下
如果你使用日志,需要添加日志配置文件log4j.properties
第二步:
编写通信要用的解码工厂和编码器、解码器类,代码如下
package com.hoo.mina.code.factory;<!--CRLF-->
<!--CRLF-->
import org.apache.mina.core.session.IoSession;<!--CRLF-->
import org.apache.mina.filter.codec.ProtocolCodecFactory;<!--CRLF-->
import org.apache.mina.filter.codec.ProtocolDecoder;<!--CRLF-->
import org.apache.mina.filter.codec.ProtocolEncoder;<!--CRLF-->
import com.hoo.mina.code.CharsetDecoder;<!--CRLF-->
import com.hoo.mina.code.CharsetEncoder;<!--CRLF-->
<!--CRLF-->
/**<!--CRLF-->
* <b>function:</b> 字符编码、解码工厂类,编码过滤工厂<!--CRLF-->
* @author hoojo<!--CRLF-->
* @createDate 2012-6-26 下午01:08:50<!--CRLF-->
* @file CharsetCodecFactory.java<!--CRLF-->
* @package com.hoo.mina.code.factory<!--CRLF-->
* @project ApacheMiNa<!--CRLF-->
* @blog http://blog.csdn.net/IBM_hoojo<!--CRLF-->
* @email hoojo_@126.com<!--CRLF-->
* @version 1.0<!--CRLF-->
*/<!--CRLF-->
public class CharsetCodecFactory implements ProtocolCodecFactory {<!--CRLF-->
<!--CRLF-->
@Override<!--CRLF-->
public ProtocolDecoder getDecoder(IoSession session) throws Exception {<!--CRLF-->
return new CharsetDecoder();<!--CRLF-->
}<!--CRLF-->
<!--CRLF-->
@Override<!--CRLF-->
public ProtocolEncoder getEncoder(IoSession session) throws Exception {<!--CRLF-->
return new CharsetEncoder();<!--CRLF-->
}<!--CRLF-->
}<!--CRLF-->
解码类
package com.hoo.mina.code;<!--CRLF-->
<!--CRLF-->
import java.nio.charset.Charset;<!--CRLF-->
import org.apache.log4j.Logger;<!--CRLF-->
import org.apache.mina.core.buffer.IoBuffer;<!--CRLF-->
import org.apache.mina.core.session.IoSession;<!--CRLF-->
import org.apache.mina.filter.codec.ProtocolDecoder;<!--CRLF-->
import org.apache.mina.filter.codec.ProtocolDecoderOutput;<!--CRLF-->
<!--CRLF-->
/**<!--CRLF-->
* <b>function:</b> 字符解码<!--CRLF-->
* @author hoojo<!--CRLF-->
* @createDate 2012-6-26 上午11:14:18<!--CRLF-->
* @file CharsetDecoder.java<!--CRLF-->
* @package com.hoo.mina.code<!--CRLF-->
* @project ApacheMiNa<!--CRLF-->
* @blog http://blog.csdn.net/IBM_hoojo<!--CRLF-->
* @email hoojo_@126.com<!--CRLF-->
* @version 1.0<!--CRLF-->
*/<!--CRLF-->
public class CharsetDecoder implements ProtocolDecoder {<!--CRLF-->
<!--CRLF-->
private final static Logger log = Logger.getLogger(CharsetDecoder.class);<!--CRLF-->
<!--CRLF-->
private final static Charset charset = Charset.forName("UTF-8");<!--CRLF-->
// 可变的IoBuffer数据缓冲区<!--CRLF-->
private IoBuffer buff = IoBuffer.allocate(100).setAutoExpand(true);<!--CRLF-->
<!--CRLF-->
@Override<!--CRLF-->
public void decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {<!--CRLF-->
log.info("#########decode#########");<!--CRLF-->
<!--CRLF-->
// 如果有消息<!--CRLF-->
while (in.hasRemaining()) {<!--CRLF-->
// 判断消息是否是结束符,不同平台的结束符也不一样;<!--CRLF-->
// windows换行符(\r\n)就认为是一个完整消息的结束符了; UNIX 是\n;MAC 是\r<!--CRLF-->
byte b = in.get();<!--CRLF-->
if (b == '\n') {<!--CRLF-->
buff.flip();<!--CRLF-->
byte[] bytes = new byte[buff.limit()];<!--CRLF-->
buff.get(bytes);<!--CRLF-->
String message = new String(bytes, charset);<!--CRLF-->
<!--CRLF-->
buff = IoBuffer.allocate(100).setAutoExpand(true);<!--CRLF-->
<!--CRLF-->
// 如果结束了,就写入转码后的数据<!--CRLF-->
out.write(message);<!--CRLF-->
//log.info("message: " + message);<!--CRLF-->
} else {<!--CRLF-->
buff.put(b);<!--CRLF-->
}<!--CRLF-->
}<!--CRLF-->
}<!--CRLF-->
<!--CRLF-->
@Override<!--CRLF-->
public void dispose(IoSession session) throws Exception {<!--CRLF-->
log.info("#########dispose#########");<!--CRLF-->
log.info(session.getCurrentWriteMessage());<!--CRLF-->
}<!--CRLF-->
<!--CRLF-->
@Override<!--CRLF-->
public void finishDecode(IoSession session, ProtocolDecoderOutput out) throws Exception {<!--CRLF-->
log.info("#########完成解码#########");<!--CRLF-->
}<!--CRLF-->
}<!--CRLF-->
上面的decode方法是解码方法,它主要是把读取到数据中的换行符去掉。因为在mina通信协议中以换行符为结束符,如果不定义结束符那么程序会在那里一直等待下一条发送的数据。
这里用到了IoBuffer,MiNa中传输的所有二进制信息都存放在IoBuffer中,IoBuffer是对Java NIO中ByteBuffer的封装(Mina2.0以前版本这个接口也是ByteBuffer),提供了更多操作二进制数据,对象的方法,并且存储空间可以自增长,用起来非常方便;简单理解,它就是个可变长度的byte字节数组!
1. static IoBuffer allocate(int capacity,boolean useDirectBuffer)
创建IoBuffer实例,第一个参数指定初始化容量,第二个参数指定使用直接缓冲区还是JAVA 内存堆的缓存区,默认为false。
2.IoBuffer setAutoExpand(boolean autoExpand)
这个方法设置IoBuffer 为自动扩展容量,也就是前面所说的长度可变,那么可以看出长度可变这个特性默认是不开启的。
3. IoBuffer flip()
limit=position, position=0,重置mask,为了读取做好准备,一般是结束buffer操作,将buffer写入输出流时调用;这个必须要调用,否则极有可能position!=limit,导致position后面没有数据;每次写入数据到输出流时,必须确保position=limit。
4. IoBuffer clear()与IoBuffer reset()
clear:limit=capacity , position=0,重置mark;它是不清空数据,但从头开始存放数据做准备---相当于覆盖老数据。
reset就是清空数据
5. int remaining()与boolean hasRemaining()
这两个方法一般是在调用了flip方法后使用的,remaining()是返回limt-position的值!hasRemaining()则是判断当前是否有数据,返回position < limit的boolean值!
编码类
package com.hoo.mina.code;<!--CRLF-->
<!--CRLF-->
import java.nio.charset.Charset;<!--CRLF-->
import org.apache.log4j.Logger;<!--CRLF-->
import org.apache.mina.core.buffer.IoBuffer;<!--CRLF-->
import org.apache.mina.core.session.IoSession;<!--CRLF-->
import org.apache.mina.filter.codec.ProtocolEncoder;<!--CRLF-->
import org.apache.mina.filter.codec.ProtocolEncoderOutput;<!--CRLF-->
import org.apache.mina.filter.codec.textline.LineDelimiter;<!--CRLF-->
<!--CRLF-->
/**<!--CRLF-->
* <b>function:</b> 字符编码<!--CRLF-->
* @author hoojo<!--CRLF-->
* @createDate 2012-6-26 上午11:32:05<!--CRLF-->
* @file CharsetEncoder.java<!--CRLF-->
* @package com.hoo.mina.code<!--CRLF-->
* @project ApacheMiNa<!--CRLF-->
* @blog http://blog.csdn.net/IBM_hoojo<!--CRLF-->
* @email hoojo_@126.com<!--CRLF-->
* @version 1.0<!--CRLF-->
*/<!--CRLF-->
public class CharsetEncoder implements ProtocolEncoder {<!--CRLF-->
private final static Logger log = Logger.getLogger(CharsetEncoder.class);<!--CRLF-->
private final static Charset charset = Charset.forName("UTF-8");<!--CRLF-->
<!--CRLF-->
@Override<!--CRLF-->
public void dispose(IoSession session) throws Exception {<!--CRLF-->
log.info("#############dispose############");<!--CRLF-->
}<!--CRLF-->
<!--CRLF-->
@Override<!--CRLF-->
public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {<!--CRLF-->
log.info("#############字符编码############");<!--CRLF-->
IoBuffer buff = IoBuffer.allocate(100).setAutoExpand(true);<!--CRLF-->
buff.putString(message.toString(), charset.newEncoder());<!--CRLF-->
// put 当前系统默认换行符<!--CRLF-->
buff.putString(LineDelimiter.DEFAULT.getValue(), charset.newEncoder());<!--CRLF-->
// 为下一次读取数据做准备<!--CRLF-->
buff.flip();<!--CRLF-->
<!--CRLF-->
out.write(buff);<!--CRLF-->
}<!--CRLF-->
}<!--CRLF-->
第三步:
编写IoHandler实现类代码,IoHander这里是Io读写的事件驱动类,这里的Io操作都会触发里面的事件。你所有的业务逻辑都应当在这个类中完成。
package com.hoo.mina.server.message;<!--CRLF-->
<!--CRLF-->
import java.text.SimpleDateFormat;<!--CRLF-->
import java.util.Collection;<!--CRLF-->
import java.util.Date;<!--CRLF-->
import org.apache.mina.core.future.CloseFuture;<!--CRLF-->
import org.apache.mina.core.future.IoFuture;<!--CRLF-->
import org.apache.mina.core.future.IoFutureListener;<!--CRLF-->
import org.apache.mina.core.service.IoHandler;<!--CRLF-->
import org.apache.mina.core.session.IdleStatus;<!--CRLF-->
import org.apache.mina.core.session.IoSession;<!--CRLF-->
import org.slf4j.Logger;<!--CRLF-->
import org.slf4j.LoggerFactory;<!--CRLF-->
<!--CRLF-->
/**<!--CRLF-->
* <b>function:</b> 处理服务器端消息<!--CRLF-->
* @author hoojo<!--CRLF-->
* @createDate 2012-6-26 下午01:12:34<!--CRLF-->
* @file ServerMessageHandler.java<!--CRLF-->
* @package com.hoo.mina.server.message<!--CRLF-->
* @project ApacheMiNa<!--CRLF-->
* @blog http://blog.csdn.net/IBM_hoojo<!--CRLF-->
* @email hoojo_@126.com<!--CRLF-->
* @version 1.0<!--CRLF-->
*/<!--CRLF-->
public class ServerMessageHandler implements IoHandler {<!--CRLF-->
<!--CRLF-->
private final static Logger log = LoggerFactory.getLogger(ServerMessageHandler.class);<!--CRLF-->
<!--CRLF-->
@Override<!--CRLF-->
public void exceptionCaught(IoSession session, Throwable cause) throws Exception {<!--CRLF-->
log.info("服务器发生异常: {}", cause.getMessage());<!--CRLF-->
}<!--CRLF-->
<!--CRLF-->
@Override<!--CRLF-->
public void messageReceived(IoSession session, Object message) throws Exception {<!--CRLF-->
log.info("服务器接收到数据: {}", message);<!--CRLF-->
String content = message.toString();<!--CRLF-->
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");<!--CRLF-->
String datetime = sdf.format(new Date());<!--CRLF-->
<!--CRLF-->
log.info("转发 messageReceived: " + datetime + "\t" + content);<!--CRLF-->
<!--CRLF-->
// 拿到所有的客户端Session<!--CRLF-->
Collection<IoSession> sessions = session.getService().getManagedSessions().values();<!--CRLF-->
// 向所有客户端发送数据<!--CRLF-->
for (IoSession sess : sessions) {<!--CRLF-->
sess.write(datetime + "\t" + content);<!--CRLF-->
}<!--CRLF-->
}<!--CRLF-->
<!--CRLF-->
@Override<!--CRLF-->
public void messageSent(IoSession session, Object message) throws Exception {<!--CRLF-->
log.info("服务器发送消息: {}", message);<!--CRLF-->
}<!--CRLF-->
<!--CRLF-->
@Override<!--CRLF-->
public void sessionClosed(IoSession session) throws Exception {<!--CRLF-->
log.info("关闭当前session:{}#{}", session.getId(), session.getRemoteAddress());<!--CRLF-->
<!--CRLF-->
CloseFuture closeFuture = session.close(true);<!--CRLF-->
closeFuture.addListener(new IoFutureListener<IoFuture>() {<!--CRLF-->
public void operationComplete(IoFuture future) {<!--CRLF-->
if (future instanceof CloseFuture) {<!--CRLF-->
((CloseFuture) future).setClosed();<!--CRLF-->
log.info("sessionClosed CloseFuture setClosed-->{},", future.getSession().getId());<!--CRLF-->
}<!--CRLF-->
}<!--CRLF-->
});<!--CRLF-->
}<!--CRLF-->
<!--CRLF-->
@Override<!--CRLF-->
public void sessionCreated(IoSession session) throws Exception {<!--CRLF-->
log.info("创建一个新连接:{}", session.getRemoteAddress());<!--CRLF-->
session.write("welcome to the chat room !");<!--CRLF-->
}<!--CRLF-->
<!--CRLF-->
@Override<!--CRLF-->
public void sessionIdle(IoSession session, IdleStatus status) throws Exception {<!--CRLF-->
log.info("当前连接{}处于空闲状态:{}", session.getRemoteAddress(), status);<!--CRLF-->
}<!--CRLF-->
<!--CRLF-->
@Override<!--CRLF-->
public void sessionOpened(IoSession session) throws Exception {<!--CRLF-->
log.info("打开一个session:{}#{}", session.getId(), session.getBothIdleCount());<!--CRLF-->
}<!--CRLF-->
}<!--CRLF-->
sessionCreated:当一个新的连接建立时,由I/O processor thread调用;
sessionOpened:当连接打开是调用;
messageReceived: 当接收了一个消息时调用;
messageSent:当一个消息被(IoSession#write)发送出去后调用;
sessionIdle:当连接进入空闲状态时调用;
sessionClosed:当连接关闭时调用;
exceptionCaught:实现IoHandler的类抛出异常时调用;
一般情况下,我们最关心的只有messageReceived方法,接收消息并处理,然后调用IoSession的write方法发送出消息!(注意:这里接收到的消息都是Java对象,在IoFilter中所有二进制数据都被解码)一般情况下很少有人实现IoHandler接口,而是继承它的一个实现类IoHandlerAdapter,这样不用覆盖它的7个方法,只需要根据具体需求覆盖其中的几个方法就可以!
Iohandler的7个方法其实是根据session的4个状态值间变化来调用的:
Connected:会话被创建并使用;
Idle:会话在一段时间(可配置)内没有任何请求到达,进入空闲状态;
Closing:会话将被关闭(剩余message将被强制flush);
Closed:会话被关闭;
状态转换图如下:
第四步:
编写server启动类,bind端口、设置编码过程和核心业务处理器
package com.hoo.mina.server;<!--CRLF-->
<!--CRLF-->
import java.io.IOException;<!--CRLF-->
import java.net.InetSocketAddress;<!--CRLF-->
import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;<!--CRLF-->
import org.apache.mina.core.session.IdleStatus;<!--CRLF-->
import org.apache.mina.filter.codec.ProtocolCodecFilter;<!--CRLF-->
import org.apache.mina.transport.socket.SocketAcceptor;<!--CRLF-->
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;<!--CRLF-->
import com.hoo.mina.code.factory.CharsetCodecFactory;<!--CRLF-->
import com.hoo.mina.server.message.ServerMessageHandler;<!--CRLF-->
<!--CRLF-->
/**<!--CRLF-->
* <b>function:</b> 服务器启动类<!--CRLF-->
* @author hoojo<!--CRLF-->
* @createDate 2012-6-29 下午07:11:00<!--CRLF-->
* @file MinaServer.java<!--CRLF-->
* @package com.hoo.mina.server<!--CRLF-->
* @project ApacheMiNa<!--CRLF-->
* @blog http://blog.csdn.net/IBM_hoojo<!--CRLF-->
* @email hoojo_@126.com<!--CRLF-->
* @version 1.0<!--CRLF-->
*/<!--CRLF-->
public class MinaServer {<!--CRLF-->
<!--CRLF-->
private SocketAcceptor acceptor;<!--CRLF-->
<!--CRLF-->
public MinaServer() {<!--CRLF-->
// 创建非阻塞的server端的Socket连接<!--CRLF-->
acceptor = new NioSocketAcceptor();<!--CRLF-->
}<!--CRLF-->
<!--CRLF-->
public boolean start() {<!--CRLF-->
DefaultIoFilterChainBuilder filterChain = acceptor.getFilterChain();<!--CRLF-->
// 添加编码过滤器 处理乱码、编码问题<!--CRLF-->
filterChain.addLast("codec", new ProtocolCodecFilter(new CharsetCodecFactory()));<!--CRLF-->
<!--CRLF-->
/*LoggingFilter loggingFilter = new LoggingFilter();<!--CRLF-->
loggingFilter.setMessageReceivedLogLevel(LogLevel.INFO);<!--CRLF-->
loggingFilter.setMessageSentLogLevel(LogLevel.INFO);<!--CRLF-->
// 添加日志过滤器<!--CRLF-->
filterChain.addLast("loger", loggingFilter);*/<!--CRLF-->
<!--CRLF-->
// 设置核心消息业务处理器<!--CRLF-->
acceptor.setHandler(new ServerMessageHandler());<!--CRLF-->
// 设置session配置,30秒内无操作进入空闲状态<!--CRLF-->
acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 30);<!--CRLF-->
<!--CRLF-->
try {<!--CRLF-->
// 绑定端口3456<!--CRLF-->
acceptor.bind(new InetSocketAddress(3456));<!--CRLF-->
} catch (IOException e) {<!--CRLF-->
e.printStackTrace();<!--CRLF-->
return false;<!--CRLF-->
}<!--CRLF-->
return true;<!--CRLF-->
}<!--CRLF-->
<!--CRLF-->
public static void main(String[] args) {<!--CRLF-->
MinaServer server = new MinaServer();<!--CRLF-->
server.start();<!--CRLF-->
}<!--CRLF-->
}<!--CRLF-->
上面的代码主要完成启动参数的设置,如端口、session参数;消息核心业务处理器,这个比较关键,我们所有的业务都要在这里完成;然后就是日志、编码过滤器,我们可以对发送或接收到的消息进行处理、编码操作,在网络中传递数据都是字节流传递的,我们要获取消息必须把二进制的字节流转换的字符串来处理,所以这个也是必须的;同时你还可以对服务器添加日志过滤器,来显示日志。
这样服务器端程序就已经完成,你可以用socket或mina client等方式连接服务器,进行通信。
启动服务器,在浏览器中输入http://localhost:3456 这里的服务器绑定的端口是3456
然后你在控制台中可以看到当前浏览器的一些基本信息,如果你看到这些信息就表示你服务器代码编写没有什么问题,应该可以成功建立客户端连接。信息如下:
2012-08-01 09:55:56,046 INFO [com.hoo.mina.server.message.ServerMessageHandler:75-NioProcessor-1] - 创建一个新连接:/127.0.0.1:2542<!--CRLF-->
2012-08-01 09:55:56,046 INFO [com.hoo.mina.code.CharsetEncoder:34-NioProcessor-1] - #############字符编码############<!--CRLF-->
2012-08-01 09:55:56,062 INFO [com.hoo.mina.server.message.ServerMessageHandler:86-NioProcessor-1] - 打开一个session:3#0<!--CRLF-->
2012-08-01 09:55:56,062 INFO [com.hoo.mina.server.message.ServerMessageHandler:55-NioProcessor-1] - 服务器发送消息: welcome to the chat room !<!--CRLF-->
2012-08-01 09:55:56,062 INFO [com.hoo.mina.code.CharsetDecoder:31-NioProcessor-1] - #########decode#########<!--CRLF-->
2012-08-01 09:55:56,062 INFO [com.hoo.mina.server.message.ServerMessageHandler:38-NioProcessor-1] - 服务器接收到数据: GET / HTTP/1.1<!--CRLF-->
2012-08-01 09:55:56,062 INFO [com.hoo.mina.server.message.ServerMessageHandler:43-NioProcessor-1] - 转发 messageReceived: 2012-08-01 09:55:56 GET / HTTP/1.1<!--CRLF-->
2012-08-01 09:55:56,062 INFO [com.hoo.mina.code.CharsetEncoder:34-NioProcessor-1] - #############字符编码############<!--CRLF-->
2012-08-01 09:55:56,062 INFO [com.hoo.mina.server.message.ServerMessageHandler:38-NioProcessor-1] - 服务器接收到数据: Host: localhost:3456<!--CRLF-->
2012-08-01 09:55:56,062 INFO [com.hoo.mina.server.message.ServerMessageHandler:43-NioProcessor-1] - 转发 messageReceived: 2012-08-01 09:55:56 Host: localhost:3456<!--CRLF-->
2012-08-01 09:55:56,062 INFO [com.hoo.mina.code.CharsetEncoder:34-NioProcessor-1] - #############字符编码############<!--CRLF-->
2012-08-01 09:55:56,062 INFO [com.hoo.mina.server.message.ServerMessageHandler:38-NioProcessor-1] - 服务器接收到数据: User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:14.0) Gecko/20100101 Firefox/14.0.1<!--CRLF-->
2012-08-01 09:55:56,062 INFO [com.hoo.mina.server.message.ServerMessageHandler:43-NioProcessor-1] - 转发 messageReceived: 2012-08-01 09:55:56 User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:14.0) Gecko/20100101 Firefox/14.0.1<!--CRLF-->
2012-08-01 09:55:56,062 INFO [com.hoo.mina.code.CharsetEncoder:34-NioProcessor-1] - #############字符编码############<!--CRLF-->
2012-08-01 09:55:56,062 INFO [com.hoo.mina.server.message.ServerMessageHandler:38-NioProcessor-1] - 服务器接收到数据: Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8<!--CRLF-->
2012-08-01 09:55:56,062 INFO [com.hoo.mina.server.message.ServerMessageHandler:43-NioProcessor-1] - 转发 messageReceived: 2012-08-01 09:55:56 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8<!--CRLF-->
其他内容省略……<!--CRLF-->
三、客户端编码工作
第一步:
编写客户端消息核心处理业务类型,消息处理器
package com.hoo.mina.client.message;<!--CRLF-->
<!--CRLF-->
import org.apache.mina.core.service.IoHandlerAdapter;<!--CRLF-->
import org.apache.mina.core.session.IoSession;<!--CRLF-->
import org.slf4j.Logger;<!--CRLF-->
import org.slf4j.LoggerFactory;<!--CRLF-->
<!--CRLF-->
/**<!--CRLF-->
* <b>function:</b> 客户端消息处理类<!--CRLF-->
* @author hoojo<!--CRLF-->
* @createDate 2012-6-29 下午07:24:22<!--CRLF-->
* @file ClientMessageHandlerAdapter.java<!--CRLF-->
* @package com.hoo.mina.client.message<!--CRLF-->
* @project ApacheMiNa<!--CRLF-->
* @blog http://blog.csdn.net/IBM_hoojo<!--CRLF-->
* @email hoojo_@126.com<!--CRLF-->
* @version 1.0<!--CRLF-->
*/<!--CRLF-->
public class ClientMessageHandlerAdapter extends IoHandlerAdapter {<!--CRLF-->
<!--CRLF-->
private final static Logger log = LoggerFactory.getLogger(ClientMessageHandlerAdapter.class);<!--CRLF-->
<!--CRLF-->
public void messageReceived(IoSession session, Object message) throws Exception {<!--CRLF-->
String content = message.toString();<!--CRLF-->
log.info("client receive a message is : " + content);<!--CRLF-->
}<!--CRLF-->
<!--CRLF-->
public void messageSent(IoSession session , Object message) throws Exception{<!--CRLF-->
log.info("messageSent 客户端发送消息:" + message);<!--CRLF-->
}<!--CRLF-->
<!--CRLF-->
@Override<!--CRLF-->
public void exceptionCaught(IoSession session, Throwable cause) throws Exception {<!--CRLF-->
log.info("服务器发生异常: {}", cause.getMessage());<!--CRLF-->
}<!--CRLF-->
}<!--CRLF-->
这里我们没有实现IoHandler这个接口,而是继承了IoHandlerAdapter这类,覆盖了messageReceived、messageSent这两个方法。IoHandlerAdapter是IoHandler接口的一个实现,我们这里没有必要实现IoHandler的所有方法。
第二步:
编写连接服务器的代码,设置核心消息处理器
package com.hoo.mina.client;<!--CRLF-->
<!--CRLF-->
import java.net.InetSocketAddress;<!--CRLF-->
import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;<!--CRLF-->
import org.apache.mina.core.future.CloseFuture;<!--CRLF-->
import org.apache.mina.core.future.ConnectFuture;<!--CRLF-->
import org.apache.mina.core.session.IoSession;<!--CRLF-->
import org.apache.mina.filter.codec.ProtocolCodecFilter;<!--CRLF-->
import org.apache.mina.transport.socket.SocketConnector;<!--CRLF-->
import org.apache.mina.transport.socket.nio.NioSocketConnector;<!--CRLF-->
import com.hoo.mina.client.message.ClientMessageHandlerAdapter;<!--CRLF-->
import com.hoo.mina.code.factory.CharsetCodecFactory;<!--CRLF-->
<!--CRLF-->
/**<!--CRLF-->
* <b>function:</b> mina客户端<!--CRLF-->
* @author hoojo<!--CRLF-->
* @createDate 2012-6-29 下午07:28:45<!--CRLF-->
* @file MinaClient.java<!--CRLF-->
* @package com.hoo.mina.client.message<!--CRLF-->
* @project ApacheMiNa<!--CRLF-->
* @blog http://blog.csdn.net/IBM_hoojo<!--CRLF-->
* @email hoojo_@126.com<!--CRLF-->
* @version 1.0<!--CRLF-->
*/<!--CRLF-->
public class MinaClient {<!--CRLF-->
<!--CRLF-->
private SocketConnector connector;<!--CRLF-->
private ConnectFuture future;<!--CRLF-->
private IoSession session;<!--CRLF-->
<!--CRLF-->
public boolean connect() {<!--CRLF-->
<!--CRLF-->
// 创建一个socket连接<!--CRLF-->
connector = new NioSocketConnector();<!--CRLF-->
// 设置链接超时时间<!--CRLF-->
connector.setConnectTimeoutMillis(3000);<!--CRLF-->
// 获取过滤器链<!--CRLF-->
DefaultIoFilterChainBuilder filterChain = connector.getFilterChain();<!--CRLF-->
// 添加编码过滤器 处理乱码、编码问题<!--CRLF-->
filterChain.addLast("codec", new ProtocolCodecFilter(new CharsetCodecFactory()));<!--CRLF-->
<!--CRLF-->
/*<!--CRLF-->
// 日志<!--CRLF-->
LoggingFilter loggingFilter = new LoggingFilter();<!--CRLF-->
loggingFilter.setMessageReceivedLogLevel(LogLevel.INFO);<!--CRLF-->
loggingFilter.setMessageSentLogLevel(LogLevel.INFO);<!--CRLF-->
filterChain.addLast("loger", loggingFilter);*/<!--CRLF-->
<!--CRLF-->
// 消息核心处理器<!--CRLF-->
connector.setHandler(new ClientMessageHandlerAdapter());<!--CRLF-->
<!--CRLF-->
// 连接服务器,知道端口、地址<!--CRLF-->
future = connector.connect(new InetSocketAddress(3456));<!--CRLF-->
// 等待连接创建完成<!--CRLF-->
future.awaitUninterruptibly();<!--CRLF-->
// 获取当前session<!--CRLF-->
session = future.getSession();<!--CRLF-->
return true;<!--CRLF-->
}<!--CRLF-->
<!--CRLF-->
public void setAttribute(Object key, Object value) {<!--CRLF-->
session.setAttribute(key, value);<!--CRLF-->
}<!--CRLF-->
<!--CRLF-->
public void send(String message) {<!--CRLF-->
session.write(message);<!--CRLF-->
}<!--CRLF-->
<!--CRLF-->
public boolean close() {<!--CRLF-->
CloseFuture future = session.getCloseFuture();<!--CRLF-->
future.awaitUninterruptibly(1000);<!--CRLF-->
connector.dispose();<!--CRLF-->
return true;<!--CRLF-->
}<!--CRLF-->
<!--CRLF-->
public SocketConnector getConnector() {<!--CRLF-->
return connector;<!--CRLF-->
}<!--CRLF-->
<!--CRLF-->
public IoSession getSession() {<!--CRLF-->
return session;<!--CRLF-->
}<!--CRLF-->
}<!--CRLF-->
第三步:
完成启动、在控制台输入你发送的内容
package com.hoo.mina.client.main;
<!--CRLF-->
<!--CRLF-->
import java.util.Scanner;
<!--CRLF-->
import com.hoo.mina.client.MinaClient;
<!--CRLF-->
<!--CRLF-->
/**
<!--CRLF-->
* <b>function:</b> 运行客户端程序
<!--CRLF-->
* @author hoojo
<!--CRLF-->
* @createDate 2012-6-29 下午07:36:44
<!--CRLF-->
* @file RunClient.java
<!--CRLF-->
* @package com.hoo.mina.client.main
<!--CRLF-->
* @project ApacheMiNa
<!--CRLF-->
* @blog http://blog.csdn.net/IBM_hoojo
<!--CRLF-->
* @email hoojo_@126.com
<!--CRLF-->
* @version 1.0
<!--CRLF-->
*/
<!--CRLF-->
public class RunClient {
<!--CRLF-->
<!--CRLF-->
public static void main(String[] args) {
<!--CRLF-->
MinaClient client = new MinaClient();
<!--CRLF-->
if (client.connect()) {
<!--CRLF-->
client.send("连接服务器成功!");
<!--CRLF-->
Scanner scanner = new Scanner(System.in);
<!--CRLF-->
while (scanner.hasNext()) {
<!--CRLF-->
client.send(scanner.next());
<!--CRLF-->
}
<!--CRLF-->
}
<!--CRLF-->
}
<!--CRLF-->
}
<!--CRLF-->
启动服务器,运行客户端程序可以看到控制台:
2012-08-01 10:01:15,953 INFO [com.hoo.mina.code.CharsetEncoder:34-main] - #############字符编码############
<!--CRLF-->
2012-08-01 10:01:15,953 INFO [com.hoo.mina.code.CharsetDecoder:31-NioProcessor-2] - #########decode#########
<!--CRLF-->
2012-08-01 10:01:15,953 INFO [com.hoo.mina.client.message.ClientMessageHandlerAdapter:25-NioProcessor-2] - client receive a message is : welcome to the chat room !
<!--CRLF-->
2012-08-01 10:01:15,984 INFO [com.hoo.mina.client.message.ClientMessageHandlerAdapter:29-NioProcessor-2] - messageSent 客户端发送消息:连接服务器成功!
<!--CRLF-->
2012-08-01 10:01:15,984 INFO [com.hoo.mina.code.CharsetDecoder:31-NioProcessor-2] - #########decode#########
<!--CRLF-->
2012-08-01 10:01:15,984 INFO [com.hoo.mina.client.message.ClientMessageHandlerAdapter:25-NioProcessor-2] - client receive a message is : 2012-08-01 10:01:15
<!--CRLF-->
服务器控制台:
2012-08-01 10:01:15,921 INFO [com.hoo.mina.server.message.ServerMessageHandler:75-NioProcessor-2] - 创建一个新连接:/192.168.8.22:2644
<!--CRLF-->
2012-08-01 10:01:15,937 INFO [com.hoo.mina.code.CharsetEncoder:34-NioProcessor-2] - #############字符编码############
<!--CRLF-->
2012-08-01 10:01:15,937 INFO [com.hoo.mina.server.message.ServerMessageHandler:86-NioProcessor-2] - 打开一个session:1#0
<!--CRLF-->
2012-08-01 10:01:15,937 INFO [com.hoo.mina.server.message.ServerMessageHandler:55-NioProcessor-2] - 服务器发送消息: welcome to the chat room !
<!--CRLF-->
2012-08-01 10:01:15,984 INFO [com.hoo.mina.code.CharsetDecoder:31-NioProcessor-2] - #########decode#########
<!--CRLF-->
2012-08-01 10:01:15,984 INFO [com.hoo.mina.server.message.ServerMessageHandler:38-NioProcessor-2] - 服务器接收到数据: 连接服务器成功!
<!--CRLF-->
2012-08-01 10:01:15,984 INFO [com.hoo.mina.server.message.ServerMessageHandler:43-NioProcessor-2] - 转发 messageReceived: 2012-08-01 10:01:15 连接服务器成功!
<!--CRLF-->
2012-08-01 10:01:15,984 INFO [com.hoo.mina.code.CharsetEncoder:34-NioProcessor-2] - #############字符编码############
<!--CRLF-->
2012-08-01 10:01:15,984 INFO [com.hoo.mina.server.message.ServerMessageHandler:55-NioProcessor-2] - 服务器发送消息: 2012-08-01 10:01:15 连接服务器成功!
<!--CRLF-->
2012-08-01 10:01:45,984 INFO [com.hoo.mina.server.message.ServerMessageHandler:81-NioProcessor-2] - 当前连接/192.168.8.22:2644处于空闲状态:both idle
<!--CRLF-->
在客户端控制台输入聊天内容
hello,MiNaChat~!
<!--CRLF-->
2012-08-01 10:03:49,093 INFO [com.hoo.mina.code.CharsetEncoder:34-main] - #############字符编码############
<!--CRLF-->
2012-08-01 10:03:49,093 INFO [com.hoo.mina.client.message.ClientMessageHandlerAdapter:29-NioProcessor-2] - messageSent 客户端发送消息:hello,MiNaChat~!
<!--CRLF-->
2012-08-01 10:03:49,093 INFO [com.hoo.mina.code.CharsetDecoder:31-NioProcessor-2] - #########decode#########
<!--CRLF-->
2012-08-01 10:03:49,093 INFO [com.hoo.mina.client.message.ClientMessageHandlerAdapter:25-NioProcessor-2] - client receive a message is : 2012-08-01 10:03:49
<!--CRLF-->
服务器端接收到内容
2012-08-01 10:03:49,093 INFO [com.hoo.mina.code.CharsetDecoder:31-NioProcessor-2] - #########decode#########
<!--CRLF-->
2012-08-01 10:03:49,093 INFO [com.hoo.mina.server.message.ServerMessageHandler:38-NioProcessor-2] - 服务器接收到数据: hello,MiNaChat~!
<!--CRLF-->
2012-08-01 10:03:49,093 INFO [com.hoo.mina.server.message.ServerMessageHandler:43-NioProcessor-2] - 转发 messageReceived: 2012-08-01 10:03:49 hello,MiNaChat~!
<!--CRLF-->
2012-08-01 10:03:49,093 INFO [com.hoo.mina.code.CharsetEncoder:34-NioProcessor-2] - #############字符编码############
<!--CRLF-->
2012-08-01 10:03:49,093 INFO [com.hoo.mina.server.message.ServerMessageHandler:55-NioProcessor-2] - 服务器发送消息: 2012-08-01 10:03:49 hello,MiNaChat~!
<!--CRLF-->
2012-08-01 10:04:19,093 INFO [com.hoo.mina.server.message.ServerMessageHandler:81-NioProcessor-2] - 当前连接/192.168.8.22:2644处于空闲状态:both idle
<!--CRLF-->
作者:hoojo
出处: http://www.blogjava.net/hoojo/archive/2012/08/01/384490.html
blog:http://blog.csdn.net/IBM_hoojo
http://hoojo.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
Apache MiNa 实现多人聊天室相关推荐
- SpringBoot——SpringBoot集成WebSocket实现简单的多人聊天室
文章目录: 1.什么是WebSocket? 2.Java中的WebSocket API 2.1 WebSocket开发中的相关注解及API方法 2.2 前端技术对WebSocket的支持 3.多人聊天 ...
- java实现多人聊天室+私聊+Derby数据库
java实现多人聊天室+私聊+Derby数据库(没有实现注册功能) 这个聊天室困扰了我好久好久,一步一步的修改,终于不负我的努力啊,可算完成了,对于一个初学java的来说,完成第一个比较完整的项目,也 ...
- NIO网络编程实战之简单多人聊天室
NIO网络编程实战 利用NIO编程知识,实现多人聊天室. 1. NIO编程实现步骤 第一步:创建Selector 第二步:创建ServerSocketChannel,并绑定监听端口 第三步:将Chan ...
- java 网络编程 聊天_Java——网络编程(实现基于命令行的多人聊天室)
目录: 1.ISO和TCP/IP分层模型 2.IP协议 3.TCP/UDP协议 4.基于TCP的网络编程 5.基于UDP的网络编程 6.基于TCP的多线程的聊天室的实现 1.ISO和TCP/IP分层模 ...
- 多人聊天室(Java)
第1部分 TCP和UDP TCP:是一种可靠地传输协议,是把消息按一个个小包传递并确认消息接收成功和正确才发送下一个包,速度相对于UDP慢,但是信息准确安全:常用于一般不要求速度和需要准确发送消息的场 ...
- 使用Go语言+Protobuf协议完成一个多人聊天室
软件环境:Goland Github地址 一.目的 之前用纯逻辑垒完了一个可登入登出的在线多人聊天室(代码仓库地址),这次学习了Protobuf协议,于是想试着更新下聊天室的版本. 主要目的是为了掌握 ...
- Java——网络编程(实现基于命令行的多人聊天室)
2019独角兽企业重金招聘Python工程师标准>>> 目录: 1.ISO和TCP/IP分层模型 2.IP协议 3.TCP/UDP协议 4.基于TCP的网络编程 5.基于UDP的网络 ...
- python实现简易聊天需要登录博客园zip下载_Python基于Socket实现简易多人聊天室的示例代码...
前言 套接字(Sockets)是双向通信信道的端点. 套接字可以在一个进程内,在同一机器上的进程之间,或者在不同主机的进程之间进行通信,主机可以是任何一台有连接互联网的机器. 套接字可以通过多种不同的 ...
- Java BIO多人聊天室
基于上篇NIO的多人聊天室,这篇将用BIO也实现一遍 首先是服务端的设计: /*** @author Jing* @create 2020/5/17*/ public class ChatServer ...
最新文章
- Vagrant控制管理器——“Hobo”
- Vector反向迭代器使用
- 深度学习每层的通道数如何计算_深度学习基础系列(一)| 一文看懂用kersa构建模型的各层含义(掌握输出尺寸和可训练参数数量的计算方法)...
- Spring5参考指南:SpringAOP简介
- 获取字符串中的.前面的长度_算法连载之求解不含有重复字符的最长子串长度...
- 携程编程大赛 (预赛第二场)第一题【剪刀石头布】
- 【渝粤题库】陕西师范大学200781 数据库原理及应用 作业
- spring技术内幕——深入解析spring架构与设计原理
- python文件输出log_Python同时向控制台和文件输出日志logging的方法
- Eclipse中Mybatis的自动提示的配置
- css3中的transform,渐变,rgba
- mysql execute stmt_[转载]MySql 数据库--stmt语句
- DOTween的常用方法
- 服务器系统做成iso镜像,把服务器做成镜像
- Linux 下 strace 命令用法总结
- 易语言对象--Word之精确定位表格单元格中并写入文本
- 《海岛纪元》游戏评测
- python哈姆雷特字数统计_python 哈姆雷特 字数统计 词云
- 北航 计算机学院 讲师 清华大学,楚中毅 中文主页 北京航空航天大学教师个人主页系统...
- php利用swfuplod、flash进行图片上传
热门文章
- anaconda新建python2环境安装不了jupyterlab_python/conda/jupyter-lab虚拟环境管理
- 洛谷——P1146 硬币翻转
- mpvue 初始化微信小程序
- THREEJS - 利用UV偏移模拟传送带运动
- wpf esc key 检测不到_爬虫笔记之requests检测网站编码方式(zozo.jp)(碎碎念) - CC11001100...
- 从Discuz迁移帐号密码到NodeBB
- java键盘控制英雄左右移动_js实现键盘操作实现div的移动或改变的原理及代码
- mysql查询重复用户名_mysql取出以上用户名(去除重复),score值最高的所有记录,查出重复记录...
- 在Eclipse中,如何将项目中的src/main/java目录设置为源代码包?
- 【Java每日一题】20161124