Android入門筆記 - 網路通訊 - Socket
今天來學習一下android中通訊方式中的socket。上兩次我們分別使用了HttpURLConnection , 和 HttpClient來實現通訊,他們都是在使用HTTP協議,Socket被稱為通訊端,使用的協議有TCP和UDP,TCP和UDP的區別在於TCP是可靠穩定的,內建容錯處理等優點,所以效率要低一點。然後UDP就不那麼穩定了,當使用UDP發送資料的時候,每次send,那麼socket只管send,不會管send之後對方是否收到,是否順序正確,所以UDP被稱為不穩定的傳輸,但是其效率卻很高,因為做的事情很簡單。
今天我們學習socket是使用tcp協議,在android中使用socket其實就是使用java中的socket,看應用的包 java.net.*; 就知道。
我們來完成一個小的區域網路聊天的程式,包含一個伺服器,android應用作為用戶端,一台手機發送資訊後,伺服器會轉寄給其他手機,很簡單。代碼比較多,後面附上,我們先來看怎麼使用socket。
用戶端:
用戶端Socket使用步驟:
(1)Socket mSocket = new Socket( SERVER_IP, SERVER_PORT); // 兩個參數分別為伺服器ip地址,和伺服器監聽的連接埠號碼
(2)BufferedReader mBufferReader = new BufferedReader(new InputStreamReader( mSocket.getInputStream())); // 通過socket.getInputStream() 可以得到輸入資料流,可從socket擷取伺服器發送的資料。為了能一行一行讀取,我們將其轉化為 BufferReader,可以使用BufferReader.readLine() 一行一行得去取等待在socket上的資料。
(3)PrintWriter mPrintWriter = new PrintWriter(mSocket.getOutputStream(), true); // 通過socket.getOutputStream() 可以得到輸入資料流,就可以通過socket向伺服器發送資料, 這裡需要套上 PrintWriter 標準的介面,就可以使用 mPrintWriter.println(“ ... ),直接發送資料。
(4)使用完後中斷連線。
我們來看一看執行個體:
mSocket = new Socket(SERVER_IP, SERVER_POART);mBufferReader = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));mPrintWriter = new PrintWriter(mSocket.getOutputStream(), true);
如果沒有報 Exception那就表示連結的伺服器已經成功,這時就可以使用 mBufferReader和 mPrintWrinter 來實現資料的收發。
讀取資料:
while (true) { if (null != (mStrMsg = mBufferReader.readLine())) { mStrMsg += ; if(mStrMsg.trim().equals(exit)){ mHandler.sendMessage(mHandler.obtainMessage(2)); return ; } mHandler.sendMessage(mHandler.obtainMessage(0)); } }發送資料:
private View.OnClickListener sendListener = new View.OnClickListener() { @Override public void onClick(View v) { String str = mEtMessage.getText().toString().trim(); if(mPrintWriter == null){ showToast(請先串連伺服器!); return ; } mPrintWriter.println(str); mPrintWriter.flush(); mEtMessage.setText(); } };中斷連線:
showToast(中斷連線); try {mBufferReader.close();mPrintWriter.close();mSocket.close();mSocket = null;if (mGetMessageThread != null) { mGetMessageThread = null;} } catch (IOException e) { e.printStackTrace(); }
服務端:
在服務端我們需要有一個ServerSocket,這是用來接收串連請求的,當每接收到一個一個請求後就會返回一個Socket通訊端,就可以使用這個通訊端與用戶端通訊。注意:伺服器端應該是一直運行,且可以接收很多串連的程式,伺服器程式一定不能被關閉或者崩掉,所以在設計伺服器的時候要考慮很多很多事情,我們這裡只是簡單的處理收到用戶端請求串連,然後將其加入到list當中,如果收到訊息,就將收到的訊息轉寄給列表中其他所有socket,很簡單的處理。
使用步驟:
(1)ServerSocket mServerSocket = new ServerSocket(SERVERPORT); // 建立監聽串連ServerSocket
(2)不停監聽 mServerSocket, 使用accept() 接收串連,如果偵測到串連,就啟動一個線程,將傳回值socket傳入,那麼每個線程就對應一個用戶端,可以與用戶端通訊了。
(3)接下來就是 每個線程如果收到資料,就需要將訊息轉寄給 客戶單列表中所有其他使用者。
(4)用戶端取消連結後,清除客戶socket資訊。
啟動監聽串連,並啟動一個線程池,當監聽到串連請求時建立啟動線程,並放入到線程池中運行:
try { mServerSocket = new ServerSocket(SERVERPORT); mExecutorService = Executors.newCachedThreadPool(); System.out.println(Server Start...); Socket client = null; while (true) { client = mServerSocket.accept(); mClientList.add(client); mExecutorService.execute(new ThreadServer(client)); } } catch (IOException e) { e.printStackTrace(); }線程中接收訊息,以及轉寄訊息:
private class ThreadServer implements Runnable { private Socket mClient; private BufferedReader mBufferedReader; private PrintWriter mPrintWriter; private String mStrMsg; public ThreadServer(Socket client) throws IOException { this.mClient = client; mBufferedReader = new BufferedReader(new InputStreamReader( mClient.getInputStream())); mStrMsg = usr: + this.mClient.getInetAddress() + connected + ,total: + mClientList.size(); sendMessage(mStrMsg); } private void sendMessage(String msg) throws IOException { System.out.println(msg); for (Socket client : mClientList) { mPrintWriter = new PrintWriter(client.getOutputStream(), true); mPrintWriter.println(msg); } } @Override public void run() { try { while (null != (mStrMsg = mBufferedReader.readLine())) { if (mStrMsg.trim().equals(exit)) { mStrMsg = user: + mClient.getInetAddress() + exit! + total: + (mClientList.size() - 1); sendMessage(mStrMsg); mPrintWriter = new PrintWriter( mClient.getOutputStream(), true); mPrintWriter.println(exit); mBufferedReader.close(); mPrintWriter.close(); mClientList.remove(mClient); mClient.close(); break; } else { mStrMsg = mClient.getInetAddress() + : + mStrMsg; sendMessage(mStrMsg); } } } catch (SocketException e) { // e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
如果沒有網路基礎,這個理解起來還是有點複雜的,下面貼了項目代碼,大家自己去體會一下吧,使用socket其實很簡單,主要是邏輯處理。
這裡有幾點需要注意的:
(1)android2.3之後不能再UI主線程中調用網路請求,會直接崩潰,所以需要使用handler來發送訊息處理
(2)執行個體代碼放在你的機子上不一定能使用,SERVER_IP為電腦在區域網路的本機地址,需要自己修改。 android用戶端需要連上電腦,這樣用戶端就能找到伺服器了。