Android與服務器的通信方式主要有兩種:
1.Http通信
2.Socket通信
兩者的最大差異在於:
1.Http連接使用的是“請求-響應方式”,即在請求時建立連接通道,當客戶端向服務器發送請求後,服務端才能向客戶端返回數據。
2.Socket通信則是在雙方建立連接後,可以直接進行數據的傳輸,在連接時可實現信息的主動推送,而不需要每次由客戶端向服務器發送請求。
1.何爲Socket?
(1)我們常將Socket翻譯爲套接字,在程序內部提供與外界通信的端口,即端口通信。通過建立socket連接,可爲通信雙方的數據傳輸提供通道;
(2)socket的主要特點有數據丟失率低,使用簡單易於移植;
(3)Socket是在應用層和傳輸層之間的一個抽象層,它把TCP/IP層複雜的操作抽象爲幾個簡單的接口供應用層調用以實現進程在網絡中通信。
2.Socket分類
根據不同的底層協議,Socket的實現是多樣化的。這裏主要介紹TCP/IP協議簇當中主要的Socket類型:流套接字(streamsocket)和數據報套接字(datagramsocket)。
(1)流套接字將TCP作爲其端對端協議,提供了一個可信賴的字節流服務。
(2)數據報嵌套字使用UDP協議,提供數據打包發送數據。
筆者在做物聯網智能監控系統APP過程中,需要實現手機客戶端向後臺服務器端發送Socket,於是寫了一個客戶端demo,實現三個簡單的小功能“連接”、“發送”、“取消連接”:
(客戶端)具體代碼爲:
Client:
package com.xy.socket;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SocketActivity extends AppCompatActivity {
private EditText mEditText;
private TextView mTextView;
private View view1;
private View view2;
private View view3;
private Button btn1;
private Button btn2;
private Button btn3;
private static final String TAG = "TAG";
private static final String HOST = "192.168.XXX.XXX"; //ip地址
private static final int PORT = 12345; //端口號
private PrintWriter printWriter;
private BufferedReader in;
private ExecutorService mExecutorService = null;
private String receiveMsg;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mEditText = (EditText) findViewById(R.id.mEditText);
mTextView = (TextView) findViewById(R.id.mTextView);
btn1 = (Button) findViewById(R.id.btn1);
btn2 = (Button) findViewById(R.id.btn2);
btn3 = (Button) findViewById(R.id.btn3);
mExecutorService = Executors.newCachedThreadPool();
**//爲三個按鈕設置監聽**
btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
connect(view1);
}
});
btn2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
send(view2);
}
});
btn3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
disconnect(view3);
}
});
}
public void connect(View view) {
mExecutorService.execute(new connectService());
}
public void send(View view) {
String sendMsg = mEditText.getText().toString();
Log.e(TAG, ("connectService:" + sendMsg));
mExecutorService.execute(new sendService(sendMsg));
}
public void disconnect(View view) {
mExecutorService.execute(new sendService("0"));
}
private class sendService implements Runnable {
private String msg;
sendService(String msg) {
this.msg = msg;
}
@Override
public void run() {
printWriter.println(this.msg);
}
}
private class connectService implements Runnable {
@Override
public void run() {
try {
Socket socket = new Socket(HOST, PORT);
socket.setSoTimeout(60000);
printWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
socket.getOutputStream(), "UTF-8")), true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
} catch (Exception e) {
Log.e(TAG, ("connectService:" + e.getMessage()));
}
}
}
}
安卓佈局文件中只需要添加三個按鈕控件以及一個編輯框即可,然後在上述代碼中爲相應控件添加監聽。
佈局代碼爲:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/mEditText"
android:layout_width="match_parent"
android:layout_height="50dp"/>
<TextView
android:id="@+id/mTextView"
android:layout_width="match_parent"
android:layout_height="50dp"/>
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="連接"/>
<Button
android:id="@+id/btn2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="發送"/>
<Button
android:id="@+id/btn3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="取消連接"/>
</LinearLayout>
服務端可以使用 TCP Test Tool 小工具進行測試(圖爲向後臺服務器發送十六進制數據):
注:在進行測試時,通過APP發送的應爲編輯框中的字符,如果想發送字節流需要通過Java字節數組進行轉換。
例如:
定義private byte[ ] msgol = new byte[2];
msgol[0] = (byte) 0x06;
msgol[1] = (byte) 0x01;
圖所示爲通過字節數組處理後的結果: