Socket(通訊端)是一種通訊機制,可以實現單機或跨網路進行通訊,其建立需要明確的區分C(用戶端)/S(伺服器端),支援多個用戶端串連到同一個伺服器。有兩種傳輸模式:
1)、連線導向的傳輸:基於TCP協議,可靠性高,但效率低;
2)、面向不需連線的傳輸:基於UDP協議,可靠性低,但效率高;
Android中,直接採用Socket通訊應該是我們遇到的最低級的網路運用。儘管已經作了很大程度的抽象,但是純粹的Socket通訊,仍然給開發人員留下很多細節需要處理,尤其在伺服器端,開發人員需要處理多線程以及資料緩衝等的設計問題。相對而言,處於更高抽象層的HTTP等,已經對Socket通訊中需要處理的技術細節進行了很好的封裝,開發人員無須關心,因此,HTTP在網路開發中通常具有決定性的優勢。
Android在其核心庫的java包中,提供了用於用戶端的Socket class和用於伺服器端的ServerSocket class,分別位於$SOURCE/libcore/luni/src/main/java/java/net/Socket.java和$SOURCE/libcore/luni/src/main/java/java/net/ServerSocket.java檔案中。分析兩個class的源碼,可以看出封裝考慮的很全面,只構造方法一向每個class都考慮了很多種使用方式。由於本人只是初學者,很多理解的不深入,這裡只拋磚引玉的對兩個class的構造方法分別介紹一種,就是我下面的程式中用到的:
Socket(String dstName, int dstPort):建立一個以流的方式(基於TCP協議)串連到目標機(這裡可以理解為伺服器)的用戶端Socket;dstName是目標機的IP地址,dstPort是要串連的目標機的端 口號。這裡要注意對連接埠的理解,它不能理解為物理上的一個介面,而是對電腦中一塊特殊記憶體地區的形象表述。
ServerSocket(int aport):建立一個綁定到本機指定連接埠的服務端Socket;aport就是指定的本機連接埠。與上述用戶端Socket對應,通過TCP串連時,ServerSocket建立後需要在aport連接埠上進行監聽,等待用戶端的串連。
上面所寫都是些背景知識,下面對本人的編程實踐進行詳細說明。
1、功能描述
1)、簡單的基於Socket的資料通訊;
2)、採用TCP方式串連;
3)、採用C/S結構,但服務端只支援一個串連;
4)、用戶端能夠向服務端發送資料,並顯示服務端的返回資訊;
5)、服務端能夠接收用戶端的資料,並將收到的資料以特定的方式返回給用戶端;
2、程式實現思路
1)、服務端:設計為在後台執行的service,用一個獨立的線程來處理用戶端的串連請求、資料接收和返回。為了啟動該service,編寫個簡單的Activity。
2)、用戶端:設計為一個Activity,介面由三部分組成:顯示服務端返回資訊的文本地區(一個文字框);進行資料輸入的編輯地區(一個編輯框);以及觸發串連請求並執行資料發送的觸發地區(一個按鈕)。
3、服務端來源程式
1)、Activity檔案SocketServerDemo.java
package com.android.sample.SocketServerDemo;import android.app.Activity;import android.content.Intent;import android.os.Bundle;public class SocketServerDemo extends Activity{@Overrideprotected void onCreate(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.onCreate(savedInstanceState);setContentView(R.layout.main);System.out.println("begin start service"); this.startService(new Intent(this, SocketService.class));}@Overrideprotected void onDestroy() {// TODO Auto-generated method stubsuper.onDestroy();this.stopService(new Intent(this, SocketService.class));}}
2)、service檔案SocketService.java
package com.android.sample.SocketServerDemo;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.ServerSocket;import java.net.Socket;import android.app.Service;import android.content.Intent;import android.os.IBinder;public class SocketService extends Service{Thread mServiceThread;Socket client;@Overridepublic IBinder onBind(Intent intent) {// TODO Auto-generated method stubreturn null;}@Overridepublic void onCreate() {// TODO Auto-generated method stubsuper.onCreate();mServiceThread = new Thread(new SocketServerThread());}@Overridepublic void onStart(Intent intent, int startId) {// TODO Auto-generated method stubsuper.onStart(intent, startId);mServiceThread.start();}@Overridepublic void onDestroy() {// TODO Auto-generated method stubsuper.onDestroy();}public class SocketServerThread extends Thread {private static final int PORT = 54321;private SocketServerThread(){}@Overridepublic void run() {try {ServerSocket server = new ServerSocket(PORT);while(true){System.out.println("begin client connected");client = server.accept();System.out.println("client connected");BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));System.out.println("read from client:");String textLine = reader.readLine();if(textLine.equalsIgnoreCase("EXIT")){System.out.println("EXIT invoked, closing client");break;}System.out.println(textLine);PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())));writer.println("ECHO from server: " + textLine);writer.flush();writer.close();reader.close();}} catch (IOException e) {// TODO Auto-generated catch blockSystem.err.println(e);}} }}
3)、AndroidManifest.xml檔案,因為需要在其中添加service和網路存取權限,這裡一併貼出
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.sample.SocketServerDemo" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="9" /> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".ScreenCastServer" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name="com.android.sample.SocketServerDemo.SocketService"> </service> </application> <uses-permission android:name="android.permission.INTERNET"/></manifest>
4、用戶端程式
1)、布局檔案main.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" ><TextViewandroid:id="@+id/receive_msg" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <EditTextandroid:id="@+id/edit_msg"android:layout_width="fill_parent"android:layout_height="wrap_content"/><Buttonandroid:id="@+id/send_msg"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="send"/></LinearLayout>
2)、Activity檔案SocketClientDemo.java
package com.android.sample.SocketClientDemo;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.net.UnknownHostException;import android.app.Activity;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;public class SocketClientDemo extends Activity {private static final String SERVERIP = "192.168.1.68";private static final int SERVERPORT = 54321;TextView mMsgRev;EditText mMsgEdit;Button mMsgSendBtn;String mSendMsg;String mReceivedMsg; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mMsgRev = (TextView)findViewById(R.id.receive_msg); mMsgEdit = (EditText)findViewById(R.id.edit_msg); mMsgSendBtn = (Button)findViewById(R.id.send_msg); mMsgSendBtn.setOnClickListener(new OnClickListener(){@Overridepublic void onClick(View v) {Socket socket = null;mSendMsg = mMsgEdit.getText().toString();try {socket = new Socket(SERVERIP, SERVERPORT);PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())));writer.println(mSendMsg);writer.flush();BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));mReceivedMsg = reader.readLine();if(mReceivedMsg != null){mMsgRev.setText(mReceivedMsg);}else{mMsgRev.setText("receive data error");}writer.close();reader.close();socket.close();} catch (UnknownHostException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}} }); }}
3)、在AndroidManifest.xml中向伺服器端的該檔案一樣,添加網路存取權限:<uses-permission android:name="android.permission.INTERNET"/>
5、執行環境搭建
服務端程式在開發板上執行,用戶端程式在模擬器上執行,實現了基於Socket的資料收發。但是我這裡只是進行了區域網路內的通訊,至於跨網路能不能行,目前條件不夠,沒法進行驗證。