这是一个什么系列
最近做了一个IM的项目,客户端是Android,服务器是Linux,不算什么新颖,但是我在爬坑的过程中总结的问题能够给你一个知道,使你迅速定位问题,当然我只是一个研究生,如果你是大牛请多指教。
服务器的文章写过一篇,但是一直没有时间继续写,最近有需要写客户端还有就是我在做这个项目的一些思考。最近加把劲争取每周都能更新一篇吧。
写在前面
之前用Linux写过一个服务器,有这篇博客
https://github.com/wsrspirit/Linux_Server_Reactor
现在开始接手Android。
通信协议基本已经确定,通信协议会单独写博客,这个需要我完完全全搞定通信再写,目前也在摸着石头过河。
下面就是通信了。Android的TCP框架有两个框架可以选择Netty和Mina,为此我还专门看过博客,虽然并没有什么实质性进展。。大家对Netty比较看好,虽然这两个都是一个作者,于是使用Netty,资料其少无比!!英文的也不多。。于是弃坑了,Android Mina还是较多的。没办法赶进度,不能慢慢啃了。。
配置
Mina开发非常简单,首先是包
compile 'org.slf4j:slf4j-android:1.7.7'
compile 'org.apache.mina:mina-core:2.0.4'
如果你出现了Failed to load class “org.slf4j.impl.StaticLoggerBinder”说明你没有加载slf4j的依赖。但是如果你gradle编译出现了依赖重复,
com.android.dex.DexException: Multiple dex files define Lorg/slf4j/ILoggerFactory;
下面的网站帮助你~
http://blog.csdn.net/hyr83960944/article/details/41825087
大端小端?
这个是要最先考虑的,Linux没什么说的肯定是小端了,Java和网络流是大端,这就需要对应了。写个测试
Java:
boolean b = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN;
C测试的思路就是一个union中一个int和char然后int=1 判断char
Linux需要使用小端数据,所以网络数据需要转换。ntohl是Linux提供的方法
同时网络数据需要时大端数据,Linux在发送的时候还需要再转换。htonl是Linux提供的方法
http://linux.die.net/man/3/htons
如果你传了一个很小的id,结果对面解析成了很大的数字,那么基本就是大端小端的问题了。这个问题会单独写一篇博客的。
写代码
然后就可以愉快的编代码了,Mina非常简单
首先写个Thread
public class MinaThread implements Runnable{
private IoSession session = null;
@Override
public void run() {
// TODO Auto-generated method stub
Log.d("TEST", "客户端链接开始...");
IoConnector connector = new NioSocketConnector();
//设置链接超时时间
connector.setConnectTimeoutMillis(3000);
//添加过滤器
connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory()));
connector.setHandler(new MinaClientHandler());
try{
ConnectFuture future = connector.connect(new InetSocketAddress(PathUtil.IP,PathUtil.PORT));//创建链接
future.awaitUninterruptibly();// 等待连接创建完成
session = future.getSession();//获得session
MessageEntity messageEntity = new MessageEntity();
messageEntity.setDesId(PackageFlag.REGISTE_USER);
Gson gson = new Gson();
session.write(gson.toJson(messageEntity));
}catch (Exception e){
Log.d("TEST","客户端链接异常...");
e.printStackTrace();
}
CloseFuture closeFuture = session.getCloseFuture();
closeFuture.awaitUninterruptibly();
// session.getCloseFuture().awaitUninterruptibly();//等待连接断开
Log.d("TEST", "客户端断开...");
connector.dispose();
}
}
然后再写个handler
public class MinaClientHandler extends IoHandlerAdapter {
private static final String TAG = "IM_Client_Core";
@Override
public void exceptionCaught(IoSession session, Throwable cause)
throws Exception {
Log.d("TEST", "客户端发生异常");
super.exceptionCaught(session, cause);
}
@Override
public void sessionCreated(IoSession session) throws Exception {
Log.d(TAG,"sessionCreated");
super.sessionCreated(session);
}
@Override
public void sessionOpened(IoSession session) throws Exception {
Log.d(TAG,"sessionOpened");
super.sessionOpened(session);
}
@Override
public void sessionClosed(IoSession session) throws Exception {
Log.d(TAG,"sessionClosed");
super.sessionClosed(session);
}
@Override
public void messageReceived(IoSession session, Object message)
throws Exception {
String msg = message.toString();
System.out.println("客户端接收到的信息为:" + msg);
Log.d("TEST","客户端接收到的信息为:" + msg);
super.messageReceived(session, message);
}
@Override
public void messageSent(IoSession session, Object message) throws Exception {
// TODO Auto-generated method stub
super.messageSent(session, message);
}
}
App发送数据没什么问题,服务器收到了,但是没想到客户端收不到服务器的回复,也就是MinaClientHandler的messageReceived没有执行!奇怪的是sessionCreated和sessionOpened也不打印日志,我一下慌了,以为Log没有配置正确,查之后slf4j是一个接口型日志,而Android自带Log的实现,应该不是这里的问题,关键是没有execption啊!这个问题解决的有点呵呵,我把魅族换成了小米测试,出现了Log消息。。沃日!
再抓包,发现包是发过去了,抓包这个自己搜一个用的顺手的就好了~
再搜索!于是找到了一个靠谱的
http://stackoverflow.com/questions/2735370/apache-mina-nio-connector-help
but the TextLineDecoder created by TextLineCodecFactory will be looking for (by default) a ‘\r’ (0x0d) or ‘\n’ (0x0a) to end the line and generate the completed message to be handled by your IoHandlerAdapter.
这。。。居然是这么flush缓冲区的,我在Iosession方法中没找到flush方法啊,好吧,于是在服务器返回数据中加入’\n’
于是又有了:Caused by: java.nio.charset.MalformedInputException并且还是没有收到信息,查
http://my.oschina.net/gs80140/blog/209700
我想大概意思我懂了,就是应该是在’\n’和包内容结束中间有空隙(我确实留了空袭),于是直接在包最后紧贴着加上’\n’。ok终于收到了。
优化
这个问题还是因为使用了Mina的Encoder类,找不到结束位置,所以还是自己重写这个方法好一些,官网的例子如下~
https://mina.apache.org/mina-project/userguide/ch9-codec-filter/ch9-codec-filter.html
这只是系列的第一个博客,希望能有机会把客户端和服务器都写的好一点,服务器已经上传github。服务器的博客也一直没有时间更新。。。最近好忙,心情也不好 = =b(投了百度的实习居然连笔试都不给。。悲剧)
https://github.com/wsrspirit/Linux_Server_Reactor
愿一切顺利吧