轉載請註明出處:http://blog.csdn.net/vnanyesheshou/article/details/74896575
之前做過的一個局域網內TCP通信、聊天的demo(當然不是局域網也可以進行通信,剛試了一下可以的),週日整理了一下。該demo可以做客戶端,也可以做服務端。兩個手機都安裝該應用,一個做客戶端、一個做服務端,然後進行通信(連接在同一局域網,或服務器是外網ip,客戶端可以上網)。
20180728修改:Android7.0以上socket發送數據失敗,需要將發送的操作放到子線程中,最新demo以更新。
demo下載地址:http://www.demodashi.com/demo/10567.html
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通信的使用基本就完成了,最後看一下圖片,更容易理解其通信機制。