经过一个星期的研究,终于写好了聊天的服务端,不得不感叹的是网上有些帖子实在copy太多,我还是希望兄弟们能总结自己的经验,弄些原创,免得不懂的兄弟们走弯路,也算为这个行业的发展做点点推动. 不罗嗦了,说正事:
开始我用了serverScoket直接监听来做聊天,后来发现有阻塞问题,于是采用了nio 但是 也不是很满意,于是采用了mina框架和我之前的框架整合起来,其实这个服务端主要是为了android做聊天整合的。O了,直接说代码:
首先说下mina部分:
mina 服务端启动类:
public class ServerMain extends Thread {
private static final int PORT = 9420;
/** Set this to true if you want to make the server SSL */
private static final boolean USE_SSL = false;
@Override
public void run() {
try {
//启动监听服务
//用Nio方式创建一个非阻塞的serever端socket
IoAcceptor acceptor = new NioSocketAcceptor();
// 添加接收数据的过滤器
acceptor.getFilterChain().addLast( "logger", new LoggingFilter() );
//过滤编码
acceptor.getFilterChain().addLast( "filter", new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" ))));//指定编码过滤器
//指定业务逻辑处理器
acceptor.setHandler( new ChatProtocolHandler() );
//设置端口号
acceptor.setDefaultLocalAddress( new InetSocketAddress(PORT) );
acceptor.bind();//启动监听
System.out.println("开始监听端口:" + PORT);
} catch (Exception e) {
e.printStackTrace();
}
}
}
消息处理类:
public class ChatProtocolHandler extends IoHandlerAdapter {
//注入数据层接口,必须是static类型 因为ChatProtocolHandler 这个是继承mina框架内IoHandlerAdapter的 每次都会被重新实例,
//如果不static那么那怕 spring启动时set了值也是没有用的
private static IOffLineMessageDao impl;
public void setImpl(IOffLineMessageDao impl) {
this.impl = impl;
}
private final Map<Long, IoSession> userLinks = new LinkedHashMap<Long, IoSession>();
private final Map<IoSession, Long> userLinks_back = new LinkedHashMap<IoSession, Long>();
@Override
public void exceptionCaught(IoSession session, Throwable cause) {
// System.out.println("Unexpected exception." + cause);
// 出现异常时,关闭连接对象
session.close(true);
}
/**
* 有消息进入的时候 处理方法 message 是一个IoBuffer 类
*/
@Override
public void messageReceived(IoSession session, Object message) {
try {
String theMessage = (String) message;
String[] result = theMessage.split(" ", 2);
// 获得的消息
String theCommand = result[0];
System.out.println(" 收到消息:" + theCommand);
//下面是消息处理,看的朋友们可以不管,只要知道这里面用接口对象处理自己的业务就可以了。
Map<String, String> map = JsonOpt.jsonOpt(theCommand);
long fid = Long.parseLong(map.get("fid"));
long uid = Long.parseLong(map.get("id"));
// 保存用户的连接对象
userLinks.put(uid, session);
userLinks_back.put(session, uid);
// 消息处理
// 判断是否为保持长连接的消息
if (fid == -11) {
System.out.println(uid + ":连接信号..读取离线消息..");
// 获取离线消息
List<Offlinemessage> list = impl.getOffLineMessage(uid);
if (list != null && list.size() > 0) {
List<Map<String, String>> listMap = new ArrayList<Map<String, String>>();
for (int i = 0; i < list.size(); i++) {
Offlinemessage offlinemessage = list.get(i);
Map<String, String> map1 = new LinkedHashMap<String, String>();
map1.put("uid", offlinemessage.getUsersByUid()
.getUserid()
+ "");
map1.put("fid", offlinemessage.getUsersByFid()
.getUserid()
+ "");
map1.put("info", offlinemessage.getInfo());
String date = new SimpleDateFormat("MM-dd-HH-mm-ss")
.format(offlinemessage.getSendtime());
map1.put("time", date);
listMap.add(map1);
}
String infos = JsonOpt.getJsonArray(listMap);
session.write(infos);
}
return;
}
// 判断是否转发消息
if (userLinks.containsKey(fid)) {
IoSession ioSession = userLinks.get(fid);
if (ioSession.isConnected()) {
ioSession.write(theMessage);
return;
}
}
// 保存为离线消息
impl.addInfo(uid, fid, map.get("info").toString());
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void sessionOpened(IoSession session) throws Exception {
// TODO Auto-generated method stub
super.sessionOpened(session);
System.out.println("打开连接..");
}
@Override
public void sessionClosed(IoSession session) throws Exception {
// 移除连接
if (userLinks_back.containsKey(session)) {
Long id = userLinks_back.get(session);
// 删除连接
userLinks.remove(id);
userLinks_back.remove(session);
}
System.out.println("关闭连接..");
}
}
下面监听类,就是在tomcat启动的时候就启动第一个线程类,服务端线程 也可以不用线程启动
public class ServletListener implements ServletContextListener {
public void contextDestroyed(ServletContextEvent arg0) {
// TODO Auto-generated method stub
}
public void contextInitialized(ServletContextEvent arg0) {
// TODO Auto-generated method stub
// 启动mina
new ServerMain().start();
}
}
下面是web.xml里面需要加的一句监听:
<listener>
<listener-class>lbaca.mina.ServletListener</listener-class>
</listener>
下面是spring配置文件需要加的配置:
<!-- ===数据层=== -->
<bean id="offLineMessageDaoImpl" class="lbaca.dao.daoImpl.OffLineMessageDaoImpl" >
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- ====mina数据处理==== -->
<bean id="chatProtocolHandler" class="lbaca.mina.ChatProtocolHandler">
<property name="impl" ref="offLineMessageDaoImpl"></property>
</bean>
到次为止已经搞定了,其实就是将数据层的接口注入到消息处理的类里面。一个星期的工作到此总结收工,继续android端的开发。