• Android开发之无线遥控器


    最近弄了一个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<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) { 
    14               NetworkInterface intf = en.nextElement(); 
    15               for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) { 
    16                 InetAddress inetAddress = enumIpAddr.nextElement(); 
    17                 if (!inetAddress.isLoopbackAddress()) {//127.0.0.1
    18                     address = inetAddress.getHostAddress().toString(); 
    19                     //ipV6
    20                     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 stub
    34         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         @Override
    12         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 stub
    42             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 block
    50                 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:

    http://download.csdn.net/detail/u012062785/9684842

    thread与runnable的区别:https://www.oschina.net/question/565065_86563

  • 相关阅读:
    delphi 对TThread扩充TSimpleThread
    delphi 关于命名
    Delphi 实现Ini文件参数与TEdit和TCheckBox绑定(TSimpleParam)
    delphi 操作 TWebBrowser 实现自动填表(JQuery脚本与 OleVariant 方法)
    delphi idhttp 实战用法(TIdhttpEx)
    每周总结(10)
    每周总结(9)(补)
    每周总结(8)
    《大话设计模式》读书笔记(四)
    《大话设计模式》读书笔记(三)
  • 原文地址:https://www.cnblogs.com/pngcui/p/6068983.html
Copyright © 2020-2023  润新知