• 安卓之必须了解的实时通信(Socket)


    Socket:

    有服务器和客户端之分,其是对TCP/IP的封装,使用IP地址加端口,确定一个唯一的点。在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。值得注意的是用户使用的端口最好大于1024,因为小于1024的大部分端口都是被系统占用的。此章将实现安卓socket客户端编程。

    安卓的线程基本机制

    一个程序就是一个进程,一个进程里可以有多个线程,每个进程必须有一个主线程。对应安卓一个应用程序就是一个进程,其主线程就是平常所说的安卓主UI线程。安卓实现多线程编程,其有一个重要的原则就是更新UI必须在主线程,但耗时操作必须在子线程中,如果耗时操作在主线程编写(如网络访问)当阻塞时间达到一定时,应用就会强制退出,那网络访问就面临着一个不可避免的问题:子线程更新UI操作如何实现。

    Handler

    Handler主要用于异步消息的处理: 有点类似辅助类,封装了消息投递、消息处理等接口。当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分在消息队列中逐一将消息取出,然后对消息进行处理,也就是发送消息和接收消息不是同步的处理。 这种机制通常用来处理相对耗时比较长的操作。

    Message

    Handler接收与处理的消息对象,其中消息类型有

    public int arg1和public int arg2:存放简单的整数类型消息

    public Object obj:发送给接收器的任意对象,不管是整数,字符串,某个类对象均可

    public int what:用户自定义的消息代码,这样接受者可以了解这个消息的信息,每个handler各自包含自己的消息代码,所以不用担心自定义的消息跟其他handlers有冲突。

     安卓端实现效果

    在同一网络下的一个设备开启一个端口的监听,做为socket服务器,并获取到服务器设备的IP地址和端口号,将其格式化为 “IP:端口” 进行输入,如 “193.169.44.198:8081” ,点击连接即可。安卓作为socket客户端与服务器交互数据。

     

    编程实现

    获取网络访问权限:

    实现socket编程,必须开启网络访问权限

    <uses-permission android:name="android.permission.INTERNET" />

    编写Handler消息处理类:

    handler消息处理类是MainActivity类的内部类,当消息队列不为空时将自动进入,获取到消息值并分析其中内容

     1 private Handler mainhandler=new Handler(){
     2     @Override
     3     public void handleMessage(Message msg) {
     4         //获取到命令,进行命令分支
     5         int handi=msg.arg1;
     6         switch (handi){
     7             case 0:
     8                 String ormsg=(String)msg.obj;
     9                 disSocket();//断开网络
    10                 Toast.makeText(MainActivity.this,"发生错误=>:"+ormsg,Toast.LENGTH_SHORT).show();
    11                 break;
    12             case 1:
    13                 Toast.makeText(MainActivity.this,"连接成功",Toast.LENGTH_SHORT).show();
    14                 break;
    15             case 2:
    16                 //收到数据
    17                 String str1=(String)msg.obj;
    18                 main_rx.setText(str1);
    19                 break;
    20             default:break;
    21         }
    22     }
    23 };

    连接按钮监听:

    当连接按钮按下时,将会立即获取输入框的内容并进行字符串分隔,得到IP地址和端口号,开启线程进行网络连接 

     1 //连接按钮监听
     2 main_conn.setOnClickListener(new View.OnClickListener() {
     3       @Override
     4       public void onClick(View v) {
     5           String strip=main_ip.getText().toString().trim();
     6           if(strip.indexOf(":")>=0){
     7              
     8               //开始启动连接线程
     9               new Socket_thread(strip).start();
    10 
    11           }
    12 
    13       }
    14 });

    发送数据按钮监听:

    当发送数据按钮按下时,将会立即获取到发送输入框的内容,分别可以调用字符串发送函数和十六进制发送函数进行数据发送

     1 //发送按钮监听
     2 main_send.setOnClickListener(new View.OnClickListener() {
     3  @Override
     4  public void onClick(View v) {
     5    //得到输入框内容
     6    final String senddata=main_tx.getText().toString().trim();
     7 
     8         if(!senddata.equals("")){
     9             //发送因为使用的是线程,所以先后顺序不一定
    10             //发送字符串数据
    11             sendStrSocket(senddata);
    12             //发送十六进制数据
    13             sendByteSocket(new byte[]{0x01,0x02,0x03});
    14 
    15            }else Toast.makeText(MainActivity.this,"输入不可为空",Toast.LENGTH_SHORT).show();
    16             }
    17 });

    开始网络连接线程:

    该类为MainActivity类的内部类,实现线程连接socket服务器,并获取输入输出流,并开启接收线程

     1 class Socket_thread extends Thread
     2 {
     3     private String IP="";//ip地址
     4     private int PORT=0;//端口号
     5     public Socket_thread(String strip){
     6         //构造方法需要传递服务器的IP地址和端口号
     7         //如: 192.168.43.222:8099
     8         //进行字符串分隔,得到服务器IP地址和端口号
     9         String[] stripx= strip.split(":");
    10         this.IP=stripx[0];
    11         this.PORT=Integer.parseInt(stripx[1]);
    12     }
    13     @Override
    14     public void run() {
    15         try {
    16 
    17             disSocket();//断开上次连接
    18             if(sock !=null){
    19                 outx.close();
    20                 inx.close();
    21                 sock.close();//关闭
    22                 sock=null;
    23             }
    24             //开始连接服务器,此处会一直处于阻塞,直到连接成功
    25             sock=new Socket(this.IP,this.PORT);
    26 
    27             //阻塞停止,表示连接成功,发送连接成功消息
    28             Message message=new Message();
    29             message.arg1=1;
    30             mainhandler.sendMessage(message);
    31 
    32          }catch (Exception e) {
    33             Message message=new Message();
    34             message.arg1=0;
    35             message.obj="连接服务器时异常";
    36             mainhandler.sendMessage(message);
    37 
    38             System.out.println("建立失败////////////////////////////////////////////");
    39             e.printStackTrace();
    40             return;
    41         }
    42         try {
    43             //获取到输入输出流
    44             outx=sock.getOutputStream();
    45             inx=sock.getInputStream();
    46         } catch (Exception e) {
    47             //发送连接失败异常
    48             Message message=new Message();
    49             message.arg1=0;
    50             message.obj="获取输入输出流异常";
    51             mainhandler.sendMessage(message);
    52 
    53             System.out.println("流获取失败////////////////////////////////////////////");
    54             e.printStackTrace();
    55             return;
    56         }
    57 
    58        // new Outx().start();
    59         new Inx().start();
    60     }
    61 }

    关闭socket函数:

    关闭socket之前将先关闭输入输出流,这样才能更加安全的关闭socket 

     1 private void disSocket(){
     2     //如果不为空,则断开socket
     3     if(sock !=null){
     4         try {
     5             outx.close();
     6             inx.close();
     7             sock.close();//关闭
     8             sock = null;
     9         }catch (Exception e){
    10             //发送连接失败异常
    11             Message message=new Message();
    12             message.arg1=0;
    13             message.obj="断开连接时发生错误";
    14             mainhandler.sendMessage(message);
    15 
    16         }
    17     }
    18 
    19 }

      

    数据接收线程实现: 

    接收线程将实现数据的接收,并把接收到的数据通过消息发送给处理类,特别注意的是 inx.read(bu) 返回如果是 -1 则表示服务器断开了连接或者其它非主动调用关闭socket方法断开造成的错误

     1 //循环接收数据
     2 class Inx extends Thread{
     3         @Override
     4         public void run() {
     5         while(true){
     6 
     7         byte[] bu=new byte[1024];
     8         try {
     9            //得到-1表示服务器断开
    10           int conut=inx.read(bu);//设备重启,异常 将会一直停留在这
    11             if(conut==-1){
    12                 //发送连接失败异常
    13                 Message message=new Message();
    14                 message.arg1=0;
    15                 message.obj="服务器断开";
    16                 mainhandler.sendMessage(message);
    17                 disSocket();//断开连接
    18                 System.out.println("**********服务器异常*********:"+conut);
    19                 return;
    20             }
    21 
    22           //必须去掉前后空字符,不然有这个会有1024个字符每次
    23           strread=new String(bu,"GBK").trim();
    24           //发送出收到的数据
    25           Message message=new Message();
    26           message.arg1=2;
    27           message.obj=strread;
    28           mainhandler.sendMessage(message);
    29 
    30           } catch (IOException e) {
    31               System.out.println(e);
    32 
    33            }
    34  } }}

    发送字符串函数: 

    网络编程的最终发送的内容是字节,所以发送字符串需要通过getBytes进行编码

     1 //发送字符串
     2 private void sendStrSocket(final String senddata){
     3     new Thread(new Runnable() {
     4     @Override
     5     public void run() {
     6       try {
     7          //可以经过编码发送字符串
     8          outx.write(senddata.getBytes("gbk"));//"utf-8"
     9 
    10       } catch (Exception e) {
    11          //发送连接失败异常
    12          Message message=new Message();
    13          message.arg1=0;
    14          message.obj="数据发送异常";
    15          mainhandler.sendMessage(message);
    16         }
    17         }
    18    }).start();
    19 }

    发送十六进制函数:

    通过字节数组,可以实现多个十六进制数据的发送

     1 //发送十六进制
     2  private void sendByteSocket(final byte[] senddata){
     3     new Thread(new Runnable() {
     4       @Override
     5       public void run() {
     6        try {
     7             //发送十六进制
     8             outx.write(senddata);
     9 
    10         } catch (Exception e) {
    11             //发送连接失败异常
    12              Message message=new Message();
    13              message.arg1=0;
    14              message.obj="数据发送异常";
    15              mainhandler.sendMessage(message);
    16            }
    17       }
    18     }).start();
    19 }

      


     参考:

    https://blog.csdn.net/rabbit_in_android/article/details/50585156

    https://www.imooc.com/article/25134?block_id=tuijian_wz

  • 相关阅读:
    iis 站点部署后 Microsof .Net Framework异常
    数据库中GUID的生成
    影响世界的100个经典管理定律
    帕金森定律(Parkinson's Law)
    eclipse 弹出智能提示、代码自动换行
    eclipse 快捷键
    extjs grid 分页
    extjs gride 显示序号
    SharePoint 2013 Deploy Master Page And Page Layout
    正则表达式匹配并替换字符串
  • 原文地址:https://www.cnblogs.com/dongxiaodong/p/10280104.html
Copyright © 2020-2023  润新知