基於android的Socket通信
一、Socket簡介:
1.1 Android與服務器的通信方式主要有兩種,一是Http通信,一是Socket通信。兩者最大差異在於:
Http: 使用的是“請求—響應方式”,即在請求時建立連接通道,當客戶端向服務器發送請求后,服務器端才能向客戶端返回數據。
Socket: 在雙方建立起連接后就可以直接進行數據的傳輸,在連接時可實現信息的主動推送,而不需要每次由客戶端想服務器發送請求。
1.2 通俗理解:Socket(套接字)可以看成是兩個網絡應用程序進行通信時,各自通信連接中的端點,這是一個邏輯上的概念。
Socket是應用層與傳輸層的一個抽象,將復雜的TCP/IP協議隱藏在Socket接口之后,只對用戶暴露簡單的接口。
1.3 表示方法:socket=(IP地址:端口號)
1.4 主要類型(根據底層不同的協議):a.流套接字(SOCK_STREAM)基於Tcp傳輸;b.數據報套接字(SOCK_DGRAM)基於udp傳輸;
c.原始套接字(SOCK_RAW)原始套接字可以讀寫內核沒有處理的IP數據包,而流套接字只能讀取TCP協議的數據,數據報套接字只能讀取UDP協議的數據。
因此,如果要訪問其他協議發送的數據必須使用原始套接
1.5 基本通信模型如下圖:
其中針對TCP socket通信通過,inputstream/outputstream進行流讀取和寫入。UDP經過datagram進行數據包的收發。
二、Socket在android中的應用。
2.1 服務器端首先聲明一個ServerSocket對象並且指定端口號,然后調用Serversocket的accept()方法接收客戶端的數據。
accept()方法在沒有數據進行接收的處於堵塞狀態。(Socket socket=serversocket.accept()),一旦接收到數據,
通過inputstream讀取接收的數據。
客戶端創建一個Socket對象,指定服務器端的ip地址和端口號(Socket socket=newSocket("172.168.10.108",8080);),
通過inputstream讀取數據,獲取服務器發出的數據(OutputStream outputstream=socket.getOutputStream()),
最后將要發送的數據寫入到outputstream即可進行TCP協議的socket數據傳輸。
2.2 通過如下demo 模擬socket在客戶端的使用:
2.2.1 在manifest進行權限聲明,此處用的路由局域網。
<uses-permission android:name="android.permission.INTERNET" />
2.2.2 使用TCP通信,界面布局做一簡單布置,不做過多介紹,界面只有有一button,來啟動客戶端和服務器兩個線程,客戶端寫入的內容,將在服務端進行解析,后handler發送 在ui的TextView中顯示。UI如下:
public class MainActivity<countClient> extends AppCompatActivity { TextView textView; EditText editText; Button bt; String tag = this.toString(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bt = (Button) findViewById(R.id.bt); textView = (TextView) findViewById(R.id.textV); bt.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View view) { new ServiceThread().start();//啟動服務器 new ClientThread().start();//啟動客戶端 } } ); }
public Handler mHandler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 100:
if(msg.obj == null) return;
Log.d(tag,"service receive..msg:" + msg.obj.toString());
textView.append(msg.obj.toString());
break;
}
}
};
2.2.3.客戶端代碼如下:
public int countClient = 0; class ClientThread extends Thread { Socket mSocket = null; @Override public void run() { try { Log.d(tag,"ClientThread"); mSocket = new Socket(/*"172.16.99.105"*/InetAddress.getLocalHost(), 9001);//鏈接服務器地址,端口9001.在使用此ip時可以先進行ping Log.d(tag,"InetAddress.getLocalHost:" + InetAddress.getLocalHost()); OutputStream outputStream = mSocket.getOutputStream(); PrintWriter pw = new PrintWriter(outputStream); countClient ++; pw.write("客戶端給服務器端發送的數據"+countClient); pw.flush(); //關閉輸出流 mSocket.shutdownOutput(); pw.close(); mSocket.close(); } catch (IOException e) { e.printStackTrace(); Log.d(tag,"Client IOExcept..e: " + e); } } }
2.2.4.服務端代碼如下:
class ServiceThread extends Thread { ServerSocket mServiceSocket = null; Socket mSocket = null; TextView textView; @Override public void run() { try { Log.d(tag,"serviceThread"); mServiceSocket = new ServerSocket(9001);//統一端口 mSocket = mServiceSocket.accept(); InputStream inputStream = mSocket.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(inputStream)); String line; StringBuilder sb = new StringBuilder(); try { while ((line = br.readLine()) != null) { sb.append(line); sb.append("\n"); } br.close(); } catch (IOException e) { e.printStackTrace(); } Message msg = new Message(); msg.what =100; msg.obj = sb; mHandler.sendMessage(msg);
mSocket.close();
} catch (IOException e) { e.printStackTrace(); } } }
三、總結:
使用UDP方式通信和TCP發送接受大相徑庭,相同都是進行ip端口設置,只是TCP使用的是流的方式發送,UDP是以包的形式發送。
完整demo可以下載:https://download.csdn.net/download/code_code_88/25312072