【笔记】两台Android手机的Socket双向通信

  • Post author:
  • Post category:其他


上学期搞定了小车,现在终于结束了大机器人,趁还没真正进入考研复习的高潮赶紧把以前的代码整理整理。

先讲讲我们那小车,2台手机+车体组成。一个手机放车上作为server,用其摄像头、陀螺仪、磁力计来获取图像、确定小车方向。另一台手机负责控制,作为client,并画出小车行进图。如何从server获取陀螺仪、磁力计角度,又如何把client的控制命令发送给server就成了一个问题。这里我们使用socket双向通信。

先讲

client

部分

 // Socket Client connect to Server
    connectButton.setOnClickListener(new Button.OnClickListener()
    {
      @Override
      public void onClick(View v)
      {
        // TODO Auto-generated method stub
        try
        {
          ip = mEditText02.getText().toString();
          Thread thread = new Thread(new mSocketConnectRunnable2());
          thread.start();
//          thread.sleep(100);
          goToControl();           //进行控制命令的处理
          connectButton.setVisibility(View.INVISIBLE);
          mEditText02.setVisibility(View.INVISIBLE);
        }
        catch (Exception e) 
        {
          e.printStackTrace();
        }
      }
    });  

两台手机都连接到同个wifi,这个就不多讲了。首先你得有个EditText填写server的IP地址,然后有个按钮,点击启动个线程,负责与server通信,这里传给Thread的构造函数调用Runnable接口。socket其实就是对某一个服务器的一个端口进行连接,连接上后就形成会话。由于这里socket是全双向的字节流,所以连上的时候其本身就已经具备了双向通信能力。

然后我们看它调用的接口,怎么连接和接受server发来的数据……

 //Socket Client
  public class mSocketConnectRunnable2 implements Runnable
  {         
    @Override
    public void run()
    {
      try
      {
     mSocket02 = new Socket(ip, intSocketServerPort);
     if(mSocket02.isConnected())
        {
          Log.i(TAG, "Socket Client is connected to Server.");
          strTemp01="Socket Client is connected to Server.";       
        }
        BufferedReader br = new BufferedReader(new InputStreamReader(mSocket02.getInputStream()));
        while (true)
        {
         strTemp01 = br.readLine();
         if(!strTemp01.isEmpty())
           handler.post(rReceiveInfo);           
        }
      }
      catch(Exception e)
      {
        if(bIfDebug)
        {
          Log.e(TAG, e.toString());
          e.printStackTrace();
        }
      }
    }
  }

ip上面说了,intSocketServerPort是socket端口,我这里设置为8080。mSocket02就像个水管,可以往里灌水也可以抽水。这里通过BufferedReader获得字节流,用handler传递进行数据处理。这里推荐用handler,不仅增加代码可度性,更容易避免莫名其妙的bug……

以上是建立socket连接并且从server获取字节流的过程,下面是发送字节流的部分……

public static void action(String a){
    //当Socket连接正常且不为空时,流输出给server
    if(mSocket02!=null && mSocket02.isConnected() && !a.equals(""))
    { 
      try
      {             
        bw = new BufferedWriter(new OutputStreamWriter(mSocket02.getOutputStream()));          
        bw.write(a+"\n");
        bw.flush();         
      }
      catch (Exception e)
      {
          Log.e(TAG, e.toString());
          e.printStackTrace();
        }
    }
  }

这里,比如我用 action(“forward”),则字节流将forward发往server,至于这里流输出,不过多解释,详细的需要自己仔细查看socket。

然后是

server

部分……这里我们设置一个按钮用于开启socket server

// Run Socket Server
    mButton01.setOnClickListener(new Button.OnClickListener()
    {
      @Override
      public void onClick(View v)
      {
        // TODO Auto-generated method stub
        mButton01.setEnabled(false);
        mButton02.setEnabled(true);
        setContentView(R.layout.i3);
        Thread thread = new Thread(new mSocketConnectRunnable1());
        thread.start();
      }
    });

这里也是用Thread的构造函数调用Runnable接口

 //Socket Server
  public class mSocketConnectRunnable1 implements Runnable
  {
    @Override
    public void run()
    {
      // TODO Auto-generated method stub
      try
      {
        mServerSocket01 = new ServerSocket(intSocketServerPort);
        mServerSocket01.setReuseAddress(true);
        Log.i(TAG, strDebugPreFix+"Socket Server is Running: "+intSocketServerPort);

        while (!mServerSocket01.isClosed())
        {  
          mSocket01 = mServerSocket01.accept();
          Thread read = new Thread(new Runnable()
          {
            BufferedReader br = new BufferedReader(new InputStreamReader(mSocket01.getInputStream())); 
            @Override
            public void run()
            {
              try
              {                             
                while (mSocket01.isConnected())
                {                  
                  msg = br.readLine();
                  Handler01.post(rManualControl);
                }
              }
              catch (Exception e)
              {
                if(bIfDebug)
                {
                  Log.e(TAG, e.toString());
                  e.printStackTrace();
                }
              }            
            }
          });
          read.start();       
        
          //在接受数据的同时发送数据,实现双向通信
        Thread write = new Thread (new Runnable()
        {
          @Override
          public void run()
          {
            while (mSocket01.isConnected())
            {
              try
              {
                Thread.sleep(100);
              }
              catch (InterruptedException e)
              {
                // TODO Auto-generated catch block
                e.printStackTrace();
              }
              Handler01.post(rSendStr);
            }
          }
        });
         write.start();
        }
      }   
      catch(Exception e)
      {
        if(bIfDebug)
        {
          Log.e(TAG, e.toString());
          e.printStackTrace();
        }
      }
    }
  }  
public Runnable rSendStr = new Runnable(){
    public void run()
    {
      try
      {
        BufferedWriter bw = new BufferedWriter( new OutputStreamWriter(mSocket01.getOutputStream()) );
        strMove = strMoveFlag
        + " "  + String.valueOf((int) gyrAngle) 
        + " " + String.valueOf( ( ((int)corrected_a) +((accX>3)?90:180) )%360)    //由于小车姿态问题,需要进行修正
        + " " + strQR
        +" \n";
        bw.write(strMove);
        bw.flush();
        strQR = "0"; // accX>3 
      }
      catch (IOException e)
      {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }  
    }
  };

这里用了read write两个thread,分别负责流输入和流输出。这里唯独要提的就是write部分,加上sleep控制流输出频率,不然会造成堵塞。将BufferedWriter通过handler放到Runnable中解决莫名其妙的问题(我的基础不行……惭愧惭愧……)。

PS:用handler解决部分问题,多亏了队友

sununs11.这里是我们组项目中截下的代码,会有很多无关于本文双向通信的代码,请无视……后续会放出全部代码与代码解释……这代码是干什么的???请看这个简短视频:

基于OpenCV和Android的语音物标识别车



by:season



版权声明:本文为F_season原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。