博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android开发之无线遥控器
阅读量:5297 次
发布时间:2019-06-14

本文共 8463 字,大约阅读时间需要 28 分钟。

最近弄了一个UDP/TCP的小东西,主要需要实现的功能如下(服务器端):

1、基于局域网

2、服务器端网络接口为无线与有线

3、服务器端接收到客户端的数据需要模拟按键进行处理

4、开机自启动

5、使用UDP进行连接,TCP进行通讯

 

基于以上几点,我们开始分析:

1.需要获取当前的网络IP地址,这里枚举了本机所有的网络地址,只返回ipv4

1 public String getAddressIP() { 2         //检查网络是否连接 3         while (!isNetWorkConnected()) { 4             //等待网络连接 5         } 6         ip = getLocalIpAddress(); 7         return ip; 8     } 9     10     public String getLocalIpAddress() {11         String address = null;12           try { 13             for (Enumeration
en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) { 14 NetworkInterface intf = en.nextElement(); 15 for (Enumeration
enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) { 16 InetAddress inetAddress = enumIpAddr.nextElement(); 17 if (!inetAddress.isLoopbackAddress()) {//127.0.0.118 address = inetAddress.getHostAddress().toString(); 19 //ipV620 if(!address.contains("::")){21 return address;22 }23 }24 } 25 } 26 } catch (SocketException ex) { 27 Log.e("getIpAddress Exception", ex.toString()); 28 } 29 return null; 30 } 31 32 private boolean isNetWorkConnected() {33 // TODO Auto-generated method stub34 try{35 connectivity = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);36 if(connectivity != null){37 netWorkinfo = connectivity.getActiveNetworkInfo();38 if(netWorkinfo != null && netWorkinfo.isAvailable()){39 if(netWorkinfo.getState() == NetworkInfo.State.CONNECTED){40 isConnected = true;41 return true;42 }43 }44 }45 }catch(Exception e){46 Log.e("UdpService : ",e.toString());47 return false;48 }49 return false;50 }

2.获得IP之后,创建一个多播组

1       try { 2              3             while(ip == null){ 4                 ip = getAddressIP(); 5             } 6              7             inetAddress = InetAddress.getByName(BROADCAST_IP);//多点广播地址组 8             multicastSocket = new MulticastSocket(BROADCAST_PORT);//多点广播套接字 9             multicastSocket.setTimeToLive(1);10             multicastSocket.joinGroup(inetAddress);11                 12         } catch (UnknownHostException e) {13             e.printStackTrace();14         } catch (IOException e) {15             e.printStackTrace();16         }

这里设置一组特殊网络地址作为多点广播地址,第一个多点广播地址都被看作是一个组,当客户端需要发送接收广播信息时,加入该组就可以了。

IP协议为多点广播提供这批特殊的IP地址,这些IP地址范围是224.0.0.0---239.255.255.255,其中224.0.0.0为系统自用。

下面BROADCAST_IP是自己声明的一个String类型的变量,其范围也是前面所说的IP范围,比如BROADCAST_IP="224.224.224.224"。

1     private static int BROADCAST_PORT = 1234;2     private static int PORT = 4444;3     private static String BROADCAST_IP = "224.0.0.1";

 

3.服务端开始发送本机IP地址广播,如果网络断开,则结束掉此线程,并设置标识

1     public class UDPBoardcastThread extends Thread { 2         public UDPBoardcastThread() { 3             this.start(); 4         } 5  6         @Override 7         public void run() { 8             DatagramPacket dataPacket = null; 9             //将本机的IP地址放到数据包里10             byte[] data = ip.getBytes();11             dataPacket = new DatagramPacket(data, data.length, inetAddress, BROADCAST_PORT);12             //判断是否中断连接了13             while (isNetWorkConnected()) {14                 try {15                     multicastSocket.send(dataPacket);16                     Thread.sleep(5000);17                     Log.i("UDPService:","再次发送ip地址广播");18                 } catch (Exception e) {19                     e.printStackTrace();20                 }21             }22             isConnected = false;23             Message msg = new Message();24             msg.what = 0x0001;25             mHandler01.sendMessage(msg);26             27         }28     }

 

4.新开一个线程,等待客户端连接,使用TCP进行通讯

1             new Thread() { 2                 @Override 3                 public void run() { 4                     try { 5                     //建立一个线程池,每次收到一个客户端,新开一个线程 6                     mExecutorService = Executors.newCachedThreadPool(); 7                     Socket client = null; 8                     mList.clear(); 9                     while (isConnected) {10                         11                         client = server.accept();12                         //把客户端放入客户端集合中13                         if (!connectOrNot(client)) {14                             mList.add(client);15                             Log.i("UDPService","当前连接数:"+mList.size());16                         }17                         mExecutorService.execute(new Service(client)); 18                     }19                     //释放客户端20                     for(int i = 0 ; i < mList.size() ; i++)21                         mList.get(i).close();22                    23                     } catch (IOException e) {24                         e.printStackTrace();25                     }26                 }27             }.start();

 

5.新开一个客户端的线程,处理客户端发送过来的数据等

1     //客户端线程,组成线程池 2     class Service implements Runnable { 3         private Socket socket; 4         private BufferedReader in = null; 5         private String msg = ""; 6  7         public Service(Socket socket) { 8             this.socket = socket; 9         }10 11         @Override12         public void run() {13             try {14                 in = new BufferedReader(new InputStreamReader(socket.getInputStream()));15                 //等待接收客户端发送的数据16                 while (isConnected) {17                     18                     if ((msg = in.readLine()) != null) {19                     20                         // 创建一个Instrumentation对象,调用inst对象的按键模拟方法21                         Instrumentation inst = new Instrumentation();22                         try{23                             int codeKey = Integer.parseInt(msg);24                             //codeKey对应键值参照KeyCodeTable.txt文件,在客户端中实现25                             inst.sendKeyDownUpSync(codeKey);26                             27                             //发送回执28                             this.sendmsg(socket);29                         }catch(Exception ex){30                             ex.printStackTrace();31                         }32                         33                     }34                 }35             } catch (Exception e) {36                 e.printStackTrace();37             }38         }39 40         private void sendmsg(Socket socket2) {41             // TODO Auto-generated method stub42             PrintWriter pout = null;43             44             try {45                 pout = new PrintWriter(new BufferedWriter(46                         new OutputStreamWriter(socket2.getOutputStream())), true);47                 pout.println("I am ok");48             } catch (IOException e) {49                 // TODO Auto-generated catch block50                 e.printStackTrace();51             }52            53         }54 55     }

这里使用了Instrumentation()对象来模拟按键的处理,在实际使用中,效率还行,没有很严重的延时,若真有延时,感觉也是网络方面的。

使用了socket.getInputStream()与socket.getOutputStream()方法来进行socket数据的接收与发送

 

6.最后新开一个Handler对网络断开时进行处理,也可以监听系统网络变化的广播,有时间研究下service的生命周期

1     private Handler mHandler01 = new Handler(){ 2  3         @Override 4         public void handleMessage(Message msg) { 5             // TODO Auto-generated method stub 6             super.handleMessage(msg); 7             switch(msg.what){ 8             //连接失败 9             case 0x0001:10                 initData();11                 break;12             }13         }14         15     };

 

7.开机自启动,继承BroadcastReceiver,监听系统开机广播就ok了,记得在AndroidManifest.xml文件中声明BOOT_COMPLETED属性

1     if(intent.getAction().equals("android.intent.action.BOOT_COMPLETED")){2             Intent intent2 = new Intent(context, UdpService.class);3             context.startService(intent2);4         }

 

8.还有一个问题,如果我们就这样直接编译,输出apk到电视中,会出现权限不足的error,原因是apk不是系统应用,只有uid为system id才可以去模拟按键事件,所以在

AndroidManifest.xml中加上android:sharedUserId="android.uid.system",以及<uses-permission android:name="android.permission.INJECT_EVENTS" />

再编写Android.mk,最后在android源码中使用mm命令编译apk,这样就ok了。

 

服务器端的流程差不多是这样了,附上完整源码,包含服务器端与客户端Demo:

 

thread与runnable的区别:

 

转载于:https://www.cnblogs.com/pngcui/p/6068983.html

你可能感兴趣的文章
php与java语法的区别
查看>>
对比法记音标
查看>>
第十四节,基本数据类型,列表list
查看>>
win10 64位安装memcache扩展和开启redis扩展
查看>>
安卓只能上百度等少数网站解决办法.txt
查看>>
GDI+中的图像处理
查看>>
Neo4j视频教程 Neo4j 图数据库视频教程
查看>>
android架构
查看>>
js 数组,字符串,json互相转换(在select实现多个输入的时候与后台交互常使用)...
查看>>
js index of()用法
查看>>
XSS原理及防范
查看>>
WPF中Image显示本地图片
查看>>
SVN版本管理
查看>>
匿名函数
查看>>
mongodb安全
查看>>
IntelliJ的.iml文件及相关的Class Not Found 问题
查看>>
nginx指定文件路径有两种方式root和alias
查看>>
随机排序整个数组
查看>>
哈希表等概率情况下查找成功和查找不成功的平均查找长度的计算
查看>>
Kubernetes源码阅读笔记——Scheduler(之一)
查看>>