mina 断网之后的一系列问题 ,mina linux 系统下的问题

肩上蝶@lilerong 2015-02-03 02:10:08
使用mina作为通讯的服务端,一开始的一个月用的挺好的,结果这在Linux系统下面出现了打开文件数过多的情况。
然后仔细看程序,感觉是客户端断网和断电导致在服务端的session没有被关闭,所以才会再Linux系统下出现打开文件数过多的情况。由于所以改动要求在服务端完成,所以我在项目中进行了如下操作:
这是配置文件
<!-- session config -->
<bean id="sessionConfig" factory-bean="ioAcceptor"
factory-method="getSessionConfig" >
<property name="bothIdleTime" value="${idle.timeout}"/>
<property name="receiveBufferSize" value="${receiveBufferSize}"/>
<property name="writeTimeout" value="${writeTimeout}"></property>
</bean>

<!-- 开始运行socket服务 -->
<bean id="ioAcceptor" class="org.apache.mina.transport.socket.nio.NioSocketAcceptor" init-method="bind" destroy-method="unbind">
<property name="defaultLocalAddress" ref="address" />
<property name="handler" ref="serverHandler" />
<property name="filterChainBuilder" ref="filterChainBuilder" />
<property name="reuseAddress" value="true" />
</bean>


下面是具体的处理类


@Service
public class ServerHandler extends IoHandlerAdapter {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private AppUserService appUserService;
@Autowired
private AppMessageService appMessageService;
@Resource
private Map<String, MessageTask> tasks;
@Value("${offline.timeout}")
private String offline_timeout;

@Override
public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
super.exceptionCaught(session, cause);
String username = (String) session.getAttribute(Constants.APPUSERNAME);
logger.error("exceptionCaught:" + cause.getMessage() + "用户:" + username);// 日志输出
session.close(true);
// session.getService().dispose();
}

/**
* 接收发过来的请求 根据key的值,看是什么协议过来
*/
// 接收客户端新的消息的时候调用
@Override
public void messageReceived(IoSession session, Object message) throws Exception {
super.messageReceived(session, message);
byte[] bytes = (byte[]) message;
IoBuffer buffer = IoBuffer.wrap(bytes);// wrap 取前面两个的协议号
short key = buffer.getShort();
String head = ShortUtilsByteConvert.shortToString(key);
MessageTask task = tasks.get(String.valueOf(key));
logger.info("message received: " + head + "");
if (task == null) {
logger.error("unknow message received: " + head);
return;
}
task.executeReceived(buffer, session);

}

@Override
public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
super.sessionIdle(session, status);
logger.info("Session sessionIdle" + "***********************" + status);
String username = (String) session.getAttribute(Constants.APPUSERNAME);
long last = (Long) session.getAttribute(Constants.TIMESTAMP);
long offset = System.currentTimeMillis() - last;
// System.out.println("sessionIdle " + username + " " + offset);
if (username == null) {

logger.info("userName is null*******************");
if (offset > Integer.parseInt(offline_timeout)) {// 当前时间和登陆的时间之差大于超时时间
session.write(IoBuffer.wrap("timeout\r\n".getBytes()));
session.close(false);
} else {// 没大过超时时间
session.write(IoBuffer.wrap("are you still here?\r\n".getBytes()));

}
} else {
String msg = "are you still here?\r\n";
// 写消息到客户端!
session.write(IoBuffer.wrap(msg.getBytes()));
logger.info("userName isn't null*******************" + username);
}
}

@Override
public void sessionOpened(IoSession session) throws Exception {
super.sessionOpened(session);
logger.info("Session sessionOpened...");
}

/**
* 发送完消息调用的事件
*/
@Override
public void messageSent(IoSession session, Object message) {
}

/**
* 当一个客户端连接到服务器的时候被调用
*
* @throws Exception
*/
@Override
public void sessionCreated(IoSession session) throws Exception {
super.sessionCreated(session);
logger.info("Session created...");
}

@Override
public void sessionClosed(IoSession session) throws Exception {
super.sessionClosed(session);
String appUserName = (String) session.getAttribute(Constants.APPUSERNAME);
if (appUserName == null) {
return;
}
// 根据用户名查询出一个对象
try {
if (!StringUtil.isEmpty(appUserName)) {
AppUser appUser = appUserService.findOneByAppUserName(appUserName);
if (appUser != null && !StringUtil.isEmpty(appUser.getAppUserName())) {
appUser.setUserStatus("3");// 表示退出登录
appUserService.save(appUser);// 用户退出时进行了修改状态
} else {
logger.debug("mina的sessionClosed时,app客户查询失败!");
}
MessageFactory.unregisterSession(appUserName);
}

} catch (Exception e) {
logger.debug("mina的sessionClosed时候出现了异常");
// TODO: handle exception
e.printStackTrace();
}

}
}

这样做的原理是这样的,如果客户端的session断开了,我一直发送消息,过一会就会报错,如果报错,我就在exceptionCaught()方法中关闭这个链接。


好吧!我欢天喜地的改好了,把项目放到Linux系统上去测试,结果失望了,就是客户的session是断网,断电的,我在sessionIdle()方法中不停的写也不会报错,不报错我就无法关闭连接,所以小女子在此跪求大神帮忙解决






跪求啊!项目急!
...全文
779 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
肩上蝶@lilerong 2015-12-01
  • 打赏
  • 举报
回复
解决断网和段电的方法: 不停的向客户端写,如: 主要操作以下两个方法 /***session空闲方法,设置空闲时间为10秒,设置时间在配置文件中完成:**/ @Override public void sessionIdle(IoSession session, IdleStatus status) throws Exception { super.sessionIdle(session, status); String userName = (String) session.getAttribute(Constants.APPUSERNAME); String msg = AppMsgConstants.MINAMSG; String activeState = (String) session.getAttribute(AppMsgConstants.MINA_SESSION_ACTIVE_STATE); logger.debug("********Session sessionIdle******用户名:" + userName); logger.debug("********msg length******:" + msg.getBytes().length); if (userName == null) { session.close(true); logger.debug("****关闭userName为空的链接***"); } else { // 写消息到客户端! if (activeState == AppMsgConstants.MINA_SESSION_NO_ACTIVITY) { logger.debug("sessionIdle ---关闭失效"); session.close(true); } else { session.write(IoBuffer.wrap(msg.getBytes())); logger.debug("sessionIdle ---向客户端写"); session.setAttribute(AppMsgConstants.MINA_SESSION_ACTIVE_STATE, AppMsgConstants.MINA_SESSION_NO_ACTIVITY); } } } /** *session出现异常 */ @Override public void exceptionCaught(IoSession session, Throwable cause) throws Exception { String userName = (String) session.getAttribute(Constants.APPUSERNAME); logger.info("error occur in method exceptionCaught()!"); logger.info(cause + " 用户:" + userName);// 日志输出 String appMsg = (String) session.getAttribute("appMsg"); if (StringUtils.isNotEmpty(appMsg)) { logger.debug("*************msg*************" + appMsg); Boolean f = appMessageService.findMsgAndUpdateMsgStatus(userName); if (!f) { logger.error("修改异常客户的消息失败!"); } } session.close(true); } //上面这样写,如果客户端段网了,就可以及时监控到,而且关闭session的打开数可以减少Linux系统的打开文件数, //在Linux系统写字符需要达到一定的数量的:写的字符需要达到768,才可以达到发送的缓存字节数,没达到这么多无法进行发送。不需要担心这个会占用客户很多流量。这个不会太多
abc1314521abc 2015-09-11
  • 打赏
  • 举报
回复
请问一下是怎么解决的?
zjcflowers 2015-09-10
  • 打赏
  • 举报
回复
可以告知怎么解决的吗?我现在很需要,邮箱:zjcflowers@126.com
肩上蝶@lilerong 2015-08-10
  • 打赏
  • 举报
回复
刚上来看到你的留言,现在你还需要不?
qizhihang 2015-03-31
  • 打赏
  • 举报
回复
引用 8 楼 lileronglilerong 的回复:
目前这个问题已经解决了!
怎么解决的呢,分享一下,。。
肩上蝶@lilerong 2015-03-31
  • 打赏
  • 举报
回复
目前这个问题已经解决了!
肩上蝶@lilerong 2015-02-03
  • 打赏
  • 举报
回复
首先!你那句话说“没有心跳本身就表示着客户端已经异常”我不是很理解,我这个程序没实现mina的心跳机制,只是给每个session设置了一个空闲时间,一旦空闲了,空闲时间一到,我就调用sessionIdle方法,执行里面的session.write的操作。 补充点:客户端和服务端建立连接之后,客户端断网和断电,在mina框架中没什么方法去处理这个无效session。此时:在ssessionldle()方法 中不停的对这个失效session进行wirte()。wiondows系统会抛出如下错误: java.io.IOException: 您的主机中的软件中止了一个已建立的连接。 at sun.nio.ch.SocketDispatcher.read0(Native Method) ~[?:1.7.0_25] 这个错抛出之后,我就程序就可以把这个session关闭掉 但是在Linux系统中,不会抛出这个错。因此我才发这个贴。
ctl71801 2015-02-03
  • 打赏
  • 举报
回复
引用 5 楼 lileronglilerong 的回复:
这个方法不允许!我这边的需要给客户长连接,直到客户主动退出或者是断网断电了,我才会端口这个session。
没有心跳本身就表示着客户端已经异常了 idletime设为1分钟 心跳半分钟,只要不调整服务端时间(一旦服务端调整时间,并且时间大于空等的时间,所有客户端都会断开连接) 不会有客户端未断开而服务端主动断开连接的情况
肩上蝶@lilerong 2015-02-03
  • 打赏
  • 举报
回复
这个方法不允许!我这边的需要给客户长连接,直到客户主动退出或者是断网断电了,我才会端口这个session。
ctl71801 2015-02-03
  • 打赏
  • 举报
回复
客户端加上心跳,客户端心跳消息来的时候在session中加一个date,sessionIdle的时候不需要重新给客户端回消息,在idle超过对应时间,直接关闭这个session
肩上蝶@lilerong 2015-02-03
  • 打赏
  • 举报
回复
这个怎么用?s是表示什么?是session吗?session没有sendUrgentData()这个方法。
my_mtx 2015-02-03
  • 打赏
  • 举报
回复
public boolean isConnected(){ try{ s.sendUrgentData(0xFF); return true; }catch(Exception e){ return false; } }
肩上蝶@lilerong 2015-02-03
  • 打赏
  • 举报
回复
在windows系统下,我对一个已经断网,断电的用户 String msg = "are you still here?\r\n"; // 写消息到客户端! session.write(IoBuffer.wrap(msg.getBytes())); 是会报错的。 但是在Linux系统就不会!

50,528

社区成员

发帖
与我相关
我的任务
社区描述
Java相关技术讨论
javaspring bootspring cloud 技术论坛(原bbs)
社区管理员
  • Java相关社区
  • 小虚竹
  • 谙忆
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧