Android TCP socket通訊
之前做過的一個區域網內TCP通訊、聊天的demo(當然不是區域網也可以進行通訊,剛試了一下可以的),週日整理了一下。該demo可以做客戶端,也可以做服務端。兩個手機都安裝該應用,一個做客戶端、一個做服務端,然後進行通訊(連線在同一區域網,或伺服器是外網ip,客戶端可以上網)。
20180728修改:Android7.0以上socket傳送資料失敗,需要將傳送的操作放到子執行緒中,最新demo以更新。
1 簡介
TCP(Transmission Control Protocol):傳輸控制協議,是一種面向連線的、可靠的、基於位元組流的傳輸層通訊協議。
在Internet協議族(Internet protocol suite)中,TCP層是位於IP層之上,應用層之下的中間層。不同主機的應用層之間經常需要可靠的、像管道一樣的連線,但是IP層不提供這樣的流機制,而是提供不可靠的包交換。
Socket 英文原義是“孔”或“插座”。通常稱為”套接字”,用於描述IP地址和埠,是一個通訊鏈的控制代碼,可以用來實現不同虛擬機器或不同計算機之間的通訊。在Internet上的主機一般運行了多個服務軟體,同時提供幾種服務。每種服務都開啟一個Socket,並繫結到一個埠上,不同的埠對應於不同的服務(客戶若是需要哪種服務,就講插頭連到相應的插座上),客戶的“插頭”也是一個socket。
Socket通訊,需要服務端和客戶端,兩端都要例項化一個Socket。但伺服器和客戶端的Socket是不一樣的。
- 客戶端:可以連線服務端、傳送資料、接收資料、關閉連線等。
- 服務端:可以實現繫結繫結埠,接收客戶端的連線、接收資料,傳送資料等。
Android在包java.net包下提供了ServerSocket和Socket類,ServerSocket用於建立伺服器的Socket。Socket用於例項化客戶端的Socket。當連線成功,客戶端和服務端都會產生一個Socket例項,通過此Socket進行通訊。
先看下效果圖:
2 服務端
服務端ServerSocket的構造方法有以下幾種:
- ServerSocket ():構造一個新的未繫結的ServerSocket。
- ServerSocket (int port):構造一個新的ServerSocket並繫結到指定埠,如果port等於0,則埠由系統自動分配。
- ServerSocket (int port, int backlog):構造一個新的ServerSocket並繫結到指定埠,並指定進入佇列的數目。如果port等於0,則埠由系統自動分配。
- ServerSocket (int port, int backlog, InetAddress localAddress):構造一個新的ServerSocket並繫結到指定埠和指定的地址,並指定進入佇列的數目。如果port等於0,則埠由系統自動分配。如果localAddress為null,則可以使用任意地址。 -
TCP服務端工作具體步驟:
步驟1:建立一個ServerSocket,並繫結到指定埠上。
try {
//開啟服務、指定埠號
ServerSocket mServerSocket = new ServerSocket(5566);
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, "服務開啟失敗", Toast.LENGTH_SHORT).show();
return;
}
步驟2:呼叫ServerSocket的accept(),監聽連線請求,如果客戶端請求連線,則接收連線,返回Scoekt物件。
Socket mSocket = mServerSocket.accept();
步驟3:呼叫Socket類的getInputStream()和getOutputStream()獲取輸入輸出流。
class SocketAcceptThread extends Thread{
@Override
public void run() {
try {
//等待客戶端的連線,Accept會阻塞,直到建立連線,
//所以需要放在子執行緒中執行。
mSocket = mServerSocket.accept();
//獲取輸入流
mInStream = mSocket.getInputStream();
//獲取輸出流
mOutStream = mSocket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
mHandler.sendEmptyMessage(MSG_SOCKET_ACCEPTFAIL);
return;
}
Log.i(TAG, "accept success");
mHandler.sendEmptyMessage(MSG_SOCKET_CONNECT);
}
}
步驟4:開始傳送、接收資料。
傳送資料:
private void writeMsg(String msg){
if(msg.length() == 0 || mOutStream == null)
return;
try { //傳送
mOutStream.write(msg.getBytes());
mOutStream.flush();
}catch (Exception e) {
e.printStackTrace();
}
}
接收資料:(從輸入流讀取資料,需要在一個子執行緒中迴圈執行下面的方法。)
byte[] buffer = new byte[1024];
//迴圈執行read,用來接收資料。
//資料存在buffer中,count為讀取到的資料長度。
int count = mInStream.read(buffer);
步驟5:服務不再需要,則關閉服務。
if(mServerSocket != null){
try {
mServerSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
3 客戶端
TCP客戶端工作具體步驟:
步驟1:建立一個Socket,連線到伺服器端、指定埠號。放在子執行緒中執行,否則會有問題。
class SocketConnectThread extends Thread{
public void run(){
try {
//指定ip地址和埠號
mSocket = new Socket(mIpAddress,mClientPort);
if(mSocket != null){
//獲取輸出流、輸入流
mOutStream = mSocket.getOutputStream();
mInStream = mSocket.getInputStream();
}
} catch (Exception e) {
e.printStackTrace();
mHandler.sendEmptyMessage(MSG_SOCKET_CONNECTFAIL);
return;
}
Log.i(TAG,"connect success");
mHandler.sendEmptyMessage(MSG_SOCKET_CONNECT);
}
}
步驟2:呼叫Socket類的getInputStream()和getOutputStream()獲取輸入輸出流。
具體程式碼如上所示。
步驟3:傳送、接收資料。(傳送接收資料與服務端方法相同。)
傳送資料:
private void writeMsg(String msg){
if(msg.length() == 0 || mOutStream == null)
return;
try { //傳送
mOutStream.write(msg.getBytes());
mOutStream.flush();
}catch (Exception e) {
e.printStackTrace();
}
}
接收資料:(從輸入流讀取資料,需要在一個子執行緒中迴圈執行下面的方法。)
byte[] buffer = new byte[1024];
//迴圈執行read,用來接收資料。
//資料存在buffer中,count為讀取到的資料長度。
int count = mInStream.read(buffer);
步驟4:關閉Socket
public void closeConnection(){
try {
if (mOutStream != null) {
mOutStream.close(); //關閉輸出流
mOutStream = null;
}
if (mInStream != null) {
mInStream.close(); //關閉輸入流
mInStream = null;
}
if(mSocket != null){
mSocket.close(); //關閉socket
mSocket = null;
}
} catch (IOException e) {
e.printStackTrace();
}
if(mReceiveThread != null){
mReceiveThread.threadExit();
mReceiveThread = null;
}
}
4 總結
在清單檔案中新增許可權
<uses-permission android:name="android.permission.INTERNET"/>
TCP通訊的使用基本就完成了,最後看一下圖片,更容易理解其通訊機制。