Android实现服务端与客户端netty交互(windows)包括部分问题及解决方案
---------------2020-3-14初次记录
初入Netty,了解参考:跳转
服务端使用eclipse,客户端使用android studio3.5.2:
代码参考:跳转包括(环境搭建)
服务端客户端均在eclipse,代码参考:跳转
后期待修补(android studio测试可运行,但是我的netty版本以及as的SDK版本及真机测试API版本有问题,待修补)。
---------------2020-3-15更新
因为昨日在android测试的时候出现了很多问题,今天解决了挺多,在此记录一下问题与解决方案。
前提:
我的as版本3.5.2,compilesdkversion 8.0,targetsdkversion 8.0,compiletoolversion 29.0.2,客户端netty版本4.1.11,真机测试android 7& android9
1:在创建连接的代码处:group = new NioEventLoopGroup(); 报错:No class deffound error log/apache/loging/log4J2Logger.....
问题原因:因为当时我用的netty版本是4.5.x,总之比4.1.11版本高,我尝试import这个类,果然没有,我觉得是包的问题。
问题解决:重新导入了4.1.11版本的netty包,问题解决。
在此额外提一句,如果是as由原来的包更换的话,注意把之前的依赖项给remove,如果是通过maven-Gradle导入的包,现在的好像自动移除之前的依赖,问题可能会出现在你通过jar包添加的依赖!
2:纯属我的as问题:在activity的代码处:setContentView(R.layout.activity_sign); 报错:java.lang.ClassNotFoundException: Didn't find class "android.view.View$OnUnhandledKeyEventListener" on path: DexPathList[[zip file "/data/app/com.example.login_server-2/base.apk"],nativeLibraryDirectories=[/data/app/com.example.login_server-2/lib/arm64, /data/app/com.example.login_server-2/base.apk!/lib/arm64-v8a, /system/lib64, /vendor/lib64]]
问题原因:因为系统编译版本更新以后,编译工具都是29的了,而我的android真机还是7.0,API24所以对比起来差了很多,以前我跑这个真机从来每出现过。。。
问题解决:我用android9的真机测试了一下,把targetversion和compileversion改为28,tool还是29.0.2好歹还接近一点28,就没有这个问题了,对于这个问题的解决方案是忽视:事实上即使提醒了,这个问题至目前为止除了报错还没有出现其他问题,后期也考虑使用android9手机测试。如果你尝试将gradle中的targetversion和compileversion改为24,那不行,除非你的tool有低版本的。。。我最低的就是28.0了(哭泣! 而且谷歌那边好像要求targetversion最低26..
---------2020-3-17 我来更新这个问题啦,在昨晚上跑系统的时候一如既往地报这个错误(问题2),我突然注意到错误里面提到了AppcompatActivity,众所周知,activity要么extends AppcompatActivity要么extends activity,于是我怀疑是不是因为不能集成AppcompatActivity的原因,因为我一直继承的AppcompatActivity,只有单独使用button的on click方法时才考虑更换。--3-18 我又来了,如果你的activity里面有图片(drawable)那此解决方法无效。
解决方法:将activity的继承更改为Activity,就不再报这个错误!!!(也许仅适用于我的module... maybe you can try it....
3:在一个activity使用netty连接上服务端后,在另一个activity获取之前连接的channel(来自自定义类--用于连接服务端,获取channel等等,但是获取到的channel为空,也就是没有连接???我的服务端显示已经连接上了!
问题原因:因为我在不同的activity中都创建了自定义类的对象,既然是不同对象,里面的类型值自然不一样,自然不能有B这个对象获取A这个对象之前保存的值!
问题解决:自定义了一个类,设置static channel值,连接上服务端后就setchannel,其他activity获取值也从这里面直接拿就行,测试可行,代码在后面有。
4:连接成功发送数据失败:1)注意你的结束符设置的是什么?发送的数据要以你设置的为准。2)如果你的测试代码里面连接之后就是发送信息,很有可能出现你还没连接上,就开始跑发送数据这块代码,我jio得可能因为子线程跑完在跑的同时,按顺序执行代码也在继续,就出现这个问题了!注意在连接之后再发送数据,或者直接在你的连接里面发数据也可以。
5:服务端返回信息给客户,客户获取并发送至activity:可以使用handle,突然想到一个问题,如果发送到其他普通类呢?emmmm待解决。
6:因为我在项目中加密了一些数据,加密后的数据包含换行符('\n')需要发到服务端,而我初始化的发送数据以换行符为结束符,这样我服务端就无法收到完整正确的数据了!
问题原因:自定义结束符为\n。
问题解决:修改结束符为$E$,貌似$是特殊字符一般不会在加密后的数据中出现!
7:activity中的AlertDialog定义后调用不弹出!
我的链接(单独开出来一个随笔嘿嘿嘿
我的代码(客户端),仅供参考:
nettyTcpThread:连接服务端
1 package com.example.login_server.netty; 2 3 import android.util.Log; 4 5 import io.netty.bootstrap.Bootstrap; 6 import io.netty.channel.Channel; 7 import io.netty.channel.ChannelFuture; 8 import io.netty.channel.ChannelOption; 9 import io.netty.channel.EventLoopGroup; 10 import io.netty.channel.nio.NioEventLoopGroup; 11 import io.netty.channel.socket.nio.NioSocketChannel; 12 13 /** 14 *2020-3-15 15 * by Zhang Liling 16 **/ 17 public class nettyTcpThread { 18 private String TAG = "nettyTcpClient"; 19 private String mIp = "XXX,XXX,XX,XX"; // 插入你的ip地址 20 private int mPort = 7327; 21 22 private Channel mChannel; 23 private EventLoopGroup group; 24 25 public void startConnect(){ 26 if (nettyChannelFuture.getIsConnect()){ 27 return; 28 } 29 group = new NioEventLoopGroup(); 30 try{ 31 Bootstrap clientBootStrap = new Bootstrap(); 32 clientBootStrap.group(group) 33 .channel(NioSocketChannel.class) 34 .option(ChannelOption.TCP_NODELAY,true) // 屏蔽Nagle算法试图 35 .option(ChannelOption.CONNECT_TIMEOUT_MILLIS,5000) 36 .handler(new ClientInitializer()); 37 ChannelFuture future = clientBootStrap.connect(mIp, mPort).sync(); 38 mChannel = future.channel(); 39 nettyChannelFuture.setChannel(mChannel); 40 nettyChannelFuture.setGroup(group); 41 nettyChannelFuture.setIsConnect(true); 42 Log.i(TAG,"已连接到服务器!"); 43 44 mChannel.closeFuture().sync(); 45 Log.i(TAG,"已从服务器断开"); 46 }catch (InterruptedException e){ 47 e.printStackTrace(); 48 }finally { 49 group.shutdownGracefully(); 50 } 51 } 52 53 // 发起连接&发送数据data 54 public void startConnectSendData(String data){ 55 if (nettyChannelFuture.getIsConnect()){ 56 return; 57 } 58 group = new NioEventLoopGroup(); 59 try{ 60 Bootstrap clientBootStrap = new Bootstrap(); 61 clientBootStrap.group(group) 62 .channel(NioSocketChannel.class) 63 .option(ChannelOption.TCP_NODELAY,true) // 屏蔽Nagle算法试图 64 .option(ChannelOption.CONNECT_TIMEOUT_MILLIS,5000) 65 .handler(new ClientInitializer()); 66 ChannelFuture future = clientBootStrap.connect(mIp, mPort).sync(); 67 mChannel = future.channel(); 68 nettyChannelFuture.setChannel(mChannel); 69 nettyChannelFuture.setGroup(group); 70 nettyChannelFuture.setIsConnect(true); 71 Log.i(TAG,"已连接到服务器!"); 72 mChannel.writeAndFlush(data+"\n"); 73 74 mChannel.closeFuture().sync(); 75 Log.i(TAG,"已从服务器断开"); 76 }catch (InterruptedException e){ 77 e.printStackTrace(); 78 }finally { 79 group.shutdownGracefully(); 80 } 81 } 82 83 public void sendMsg(Channel channel, final String data){ 84 if(channel != null){ 85 channel.writeAndFlush(data+"\n"); 86 }else{ 87 new Thread(){ 88 @Override 89 public void run() { 90 startConnectSendData(data); 91 } 92 }.start(); 93 } 94 } 95 96 public void disconnect(Channel channel){ 97 if(channel != null){ 98 channel.close(); 99 Log.e(TAG,"disconnect"); 100 nettyChannelFuture.setIsConnect(false); 101 }else{ 102 Log.i(TAG,"channel已断开连接"); 103 } 104 } 105 106 }
ClientInitializer :设置channel参数
1 package com.example.login_server.netty;
2
3 import io.netty.channel.Channel;
4 import io.netty.channel.ChannelInitializer;
5 import io.netty.channel.ChannelPipeline;
6 import io.netty.handler.codec.DelimiterBasedFrameDecoder;
7 import io.netty.handler.codec.Delimiters;
8 import io.netty.handler.codec.string.StringDecoder;
9 import io.netty.handler.codec.string.StringEncoder;
10 import io.netty.util.CharsetUtil;
11 /**
12 *2020-3-15
13 * by Zhang Liling
14 **/
15 public class ClientInitializer extends ChannelInitializer {
16 @Override
17 protected void initChannel(Channel ch) throws Exception {
18 ChannelPipeline pipeline = ch.pipeline();
19 pipeline
20 .addLast("framer",new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()))
21 .addLast("decoder",new StringDecoder(CharsetUtil.UTF_8))
22 .addLast("encoder",new StringEncoder(CharsetUtil.UTF_8))
23 .addLast("handler",new EchoClientHandler());
24 }
25 }
EchoClientHandler:处理返回数据
1 package com.example.login_server.netty;
2
3 import android.os.Bundle;
4 import android.os.Message;
5 import android.util.Log;
6
7 import com.example.login_server.loginInfo.MainActivity;
8
9 import io.netty.channel.ChannelHandlerContext;
10 import io.netty.channel.SimpleChannelInboundHandler;
11 /**
12 *2020-3-15
13 * by Zhang Liling
14 **/
15 public class EchoClientHandler extends SimpleChannelInboundHandler<String> {
16 private String TAG = "EchoClientHandler";
17 @Override
18 public void channelActive(ChannelHandlerContext ctx) throws Exception {
19 super.channelActive(ctx);
20 }
21
22 @Override
23 public void channelInactive(ChannelHandlerContext ctx) throws Exception {
24 super.channelInactive(ctx);
25 }
26
27 private String getmsg(String data,String msg){
28 String getmsg = null;
29 String[] data0 = data.split(msg+"=");
30 if(data0!=null && data0.length > 1){
31 String[] data1 = data0[1].split(";");
32 getmsg = data1[0];
33 }
34 return getmsg;
35 }
36
37 @Override
38 protected void channelRead0(ChannelHandlerContext channelHandlerContext, String data) throws Exception {
39 // 处理data
40 manageData(data);
41
42 Log.i(TAG,"received msg from server:" + data);
43 }
44
45 private void manageData(String data) {
46 String req="";
47 req = getmsg(data,"res");
48 if(req.equals("login")){
49 String myname = "";
50 myname = getmsg(data,"myname");
51 Message message = new Message();
52 message.what= MainActivity.RECNAME_LOGIN;
53 Bundle bundle = new Bundle();
54 bundle.putString("msg",myname);
55 message.setData(bundle);
56 MainActivity.getMainActivity().getMsghandler().sendMessage(message);
57 }else if(req.equals("loginmyinfo")){
58 Message message = new Message();
59 message.what= MainActivity.RECINFO_LOGIN;
60 Bundle bundle = new Bundle();
61 bundle.putString("pubkey",getmsg(data,"pubkey"));
62 bundle.putString("seckey",getmsg(data,"seckey"));
63 message.setData(bundle);
64 MainActivity.getMainActivity().getMsghandler().sendMessage(message);
65
66 }
67 }
68
69 @Override
70 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
71 cause.printStackTrace();
72 ctx.close();
73 }
74 }
nettyChannelFuture:存储变量供其他类连接使用
1 package com.example.login_server.netty;
2
3 import io.netty.channel.Channel;
4 import io.netty.channel.EventLoopGroup;
5 /**
6 *2020-3-15
7 * by Zhang Liling
8 **/
9 public class nettyChannelFuture {
10 private static Channel mchannel;
11 private static EventLoopGroup mgroup;
12 private static boolean misConnect;
13
14 public static void setIsConnect(boolean isConnect) {
15 misConnect = isConnect;
16 }
17
18 public static boolean getIsConnect() {
19 return misConnect;
20 }
21
22 public static void setGroup(EventLoopGroup group) {
23 mgroup = group;
24 }
25
26 public static void setChannel(Channel channel) {
27 mchannel = channel;
28 }
29
30 public static Channel getChannel(){
31 return mchannel;
32 }
33
34 public static EventLoopGroup getGroup(){
35 return mgroup;
36 }
37 }
T