---------------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