上学期搞定了小车,现在终于结束了大机器人,趁还没真正进入考研复习的高潮赶紧把以前的代码整理整理。
先讲讲我们那小车,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(); } });
//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