安卓開發學習之TCP通訊
阿新 • • 發佈:2019-01-26
背景
這幾天在學習安卓的程序間通訊,而socket也可以實現這一功能,以可靠連線協議TCP為例,實現一個服務端和客戶端的應答應用
功能:客戶端發訊息,服務端把訊息原樣返回,如果收到小寫over,退出
步驟
1、服務端,在Service的onCreate()裡面,新建一個執行緒建立服務端的socket,並實現客戶端的應答
public class PeopleService extends Service { private boolean isOut = false; private boolean isOver = false; @Override public void onCreate() { new Thread(new Runnable() { @Override public void run() { try { final ServerSocket serverSocket = new ServerSocket(12345); System.out.println("等待客戶端連線"); while (!isOut) { // 服務端不退出,這個執行緒一直執行 final Socket client = serverSocket.accept(); // 阻塞在此,直到客戶端連線 System.out.println("客戶端已連線,ip地址:" + client.getInetAddress().getHostAddress() + "埠號:" + client.getLocalPort()); // 客戶端連線後,等待1s以便客戶端發訊息 Thread.sleep(1000); try { BufferedInputStream inputStream = new BufferedInputStream(client.getInputStream()); BufferedOutputStream outputStream = new BufferedOutputStream(client.getOutputStream()); System.out.println("開始和客戶端通訊"); while (!isOver) { // 訊息不為over,執行緒一直執行 while (inputStream.available() <= 0); // 過濾空訊息 byte[] bytes = new byte[1024]; int len; StringBuffer stringBuffer = new StringBuffer(); while (inputStream.available() > 0 && (len = inputStream.read(bytes)) != -1) { stringBuffer.append(new String(bytes, 0, len)); } // 下面就不對stringBuffer的內容判空了,因為是空的話,根本跳不出上面的過濾 String fromClient = stringBuffer.toString(); System.out.println("客戶端資訊:" + fromClient); outputStream.write(fromClient.getBytes()); outputStream.flush(); isOver = fromClient.equals("over"); isOut = isOver; } System.out.println("over.."); inputStream.close(); outputStream.close(); } catch (Exception e) { e.printStackTrace(); } } } catch (Exception e) { e.printStackTrace(); } } }).start(); } @Override public IBinder onBind(Intent intent) { logger("有連線請求"); logger(intent.toString()); return null; } }
整個過程在一個新執行緒的原因是service裡不讓進行耗時操作,所以才新建執行緒
在利用BufferedOutputStream讀訊息時,一定先要利用available()過濾空訊息。
這和檔案IO不一樣,檔案IO的話輸入流的長度是固定的,所以只用read(byte[])就可以讀到輸入流的末尾,但如果是通訊的話,似乎客戶端不關閉socket,輸入流就一直不到頭,所以得用available()得到可讀位元組數,從而進行訊息過濾
2、客戶端
佈局的話,就是上面一個EditText輸入訊息,下面一個Button傳送,訊息,佈局檔案略過
連線伺服器、讀取伺服器訊息放在onStart()裡面,同樣要新建一個執行緒
@Override protected void onStart() { super.onStart(); if (!isConnected) { final Intent intent = new Intent(this, PeopleService.class); intent.setAction("com.example.songzeceng"); bindService(intent, connection, BIND_AUTO_CREATE); new Thread(new Runnable() { @Override public void run() { // 等待1s,以便服務端建立socket try { Thread.sleep(1000); server = new Socket("localhost", 12345); inputStream = new BufferedInputStream(server.getInputStream()); while (!isOver) { // 只要不結束,這個執行緒一直執行 while (inputStream.available() <= 0); // 過濾空訊息 byte[] bytes = new byte[1024]; int len; StringBuffer stringBuffer = new StringBuffer(); while (inputStream.available() > 0 && (len = inputStream.read(bytes)) != -1) { stringBuffer.append(new String(bytes, 0, len)); } String fromServer = stringBuffer.toString(); System.out.println(fromServer); isOver = fromServer.equals("over"); } System.out.println("over.."); inputStream.close(); outputStream.close(); } catch (InterruptedException e) { e.printStackTrace(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }).start(); } }
在傳送按鈕的監聽裡面,傳送資訊,當然也是一個新執行緒,在onCreate()裡面
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_input = (EditText) findViewById(R.id.client_input);
findViewById(R.id.client_send).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
try {
outputStream = new BufferedOutputStream(server.getOutputStream());
String toServer = et_input.getText().toString();
outputStream.write(toServer.getBytes());
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
});
}
執行結果
服務端
客戶端
結語
Socket通訊應該是安卓IPC裡僅有的不用Binder的方式了(忽略共享檔案),而java的TCP/UDP比C的要簡潔一些