首頁 > android開發之網路編程百度雲網盤

android開發之網路編程百度雲網盤

互聯網 2021-11-29 13:38:49

大家好,我是徐愛卿。博客地址:flutterall.com

引言

Android網路編程一直都是我想記錄的一篇文章,由於種種原因,一直推遲,終於在在今天開始寫了。這是一個好的開始,O(∩_∩)O哈哈~。網路上有很多關於Android網路編程的文章,我感覺沒有一個適當的總結合適我的。所以,今天我決定將Android網路編程的系列文章做一個總結,在這裡與大家分享。這幾篇系列文章總的分為兩大模塊:Socket編程與HTTP編程(關於在Android中的)。今天我們先來看看通過Socket編程實現的伺服器與客戶端(我們這裡是手機端)之間的通信。

這篇文章你能學到什麼?

了解網路通信的基本原理學會最基礎的Socket通信原理(萬丈高樓平地起)明白TCP協議與UDP協議的區別與適用場景網路編程基礎TCP/IP協議

我們先看看從宏觀上來看兩台機器是如何通信的。我們通過QQ和伺服器進行通信,都需要哪些東西呢?兩台主機進行通信,需要知道雙方電腦的的地址(也就是IP地址);知道兩個電腦的地址之後,我們還需要知道我發送到目的電腦的目的軟體(使用埠標記)。這樣兩台電腦連接成功之後就可以進行通信了。那麼這些東西例如:目的地如何規定,發送的數據如何包裝,放到哪裡?這中間就需要有各種協議。大家都使用這個協議,統一成一個規範,這樣符合這個規範的各種設備之間能夠進行兼容性的通信。最為廣泛的的協議就是OSI協議和TCP/IP協議了,但是OSI協議較為繁瑣,未推廣(想了解的自己Google)。反而TCP/IP(transfer control protocol/internet protocol,傳輸控制協議/網際協議)協議簡單明了,得到現今的廣泛使用。TCP/IP準確的說是一組協議,是很多協議的集合,是一組協議集合的簡稱。來看看:

名稱協議功能應用層HTTP、Telnet、FTP、TFTP提供應用程序網路介面傳輸層TCP、UDP建立端到端的連接網路層IP定址和路由數據鏈路層Ethernet、802.3、PPP物理介質訪問物理層介面和電纜二進位數據流傳輸

下面以QQ的數據傳輸為例子:

QQ的數據傳輸IP地址、埠

在上節中我們知道端到端的連接提到了幾個關鍵的字眼:IP地址、埠;IP地址用來標記唯一的計算機位置,埠號用來標記一台電腦中的不同應用程序。其中IP地址是32為二進位,例如:192.168.0.0.1等等,這個組合方式是一種協議拼起來的,詳情Google。埠號範圍是065536,其中01023是系統專用,例如:

協議名稱協議功能默認埠號HTTP(HypertextTransfer Protocol)超文本傳輸協議瀏覽網頁80FTP(File TransferProtocol) 文件傳輸協議用於網路上傳輸文件21TELNET遠程終端訪問23POP3(Post OfficeProtocol)郵局協議版本110

IP地址和埠號組成了我們的Socket,也就是「套接字」,Socket只是一個API。Socket原理機制:通信的兩端都有Socket網路通信其實就是Socket間的通信數據在兩個Socket間通過IO傳輸

單獨的Socke是沒用任何作用的,基於一定的協議(比如:TCP、UDP協議)下的socket編程才能使得數據暢通傳輸,下面我們就開始吧。

基於TCP(傳輸控制協議)協議的Socket編程

以下將「基於TCP(傳輸控制協議)協議的Socket編程」簡稱為TCP編程

既然基於TCP,那麼就有著它的一套代碼邏輯體系。我們只需要在Socket API的幫助下,使用TCP協議,就可以進行一個完整的TCP編程了。

主要API:Socket,客戶端相關

構造方法public Socket(String host, int port) throws UnknownHostException, IOException釋義:創建一個流套接字並將其連接到指定主機上的指定埠號(就是用來連接到host主機的port埠的)方法

|方法名稱 | 方法功能|| ------------- :|-------------:||getInputStream()) | 拿到此套接字的輸入流,收到的數據就在這裡 ||getOutputStream()| 返回此套接字的輸出流。 要發送的數據放到這裡|

ServerSocket,伺服器相關

構造方法ServerSocket(int port)釋義:創建服務端的監聽port埠的套接字方法Socket accept() throws IOException偵聽並接受到此套接字的連接。此方法在連接傳入之前一直阻塞。服務端通過這個方法拿到與客戶端建立端到端的連接的socket。

總體流程圖示:

Socket通信流程TCP編程的服務端流程:1.創建ServerSocket類對象-serverSocket2.使用serverSocket開始一直阻塞監聽,等待客戶端發送來數據並且得到socket3.根據socket的輸入流讀取客戶端的數據,根據socket的輸出流返回給客戶端數據4.關閉socket以及IO流TCP編程的客戶端對象1.創建客戶端的socket對象2.使用客戶端的socket對象的輸出流發送給伺服器數據,使用客戶端的socket對象的輸入流得到服務端的數據TCP編程

下面我們使用上面的TCP編程的流程來實現:手機發送信息到伺服器,伺服器返回給我們數據。

服務端的話,這裡使用eclipse。使用Eclipse新建一個Server.java來處理伺服器端的邏輯。客戶端的話使用AS來新建一個Client.java文件。然後運行伺服器,在運行手機上的程序,從手機上發送一段內容到伺服器端接收。大概就是這裡流程。

手機發送信息到伺服器,伺服器返回給我們數據

伺服器端:

伺服器端新建TcpSocketDemo工程

Code:

package com.hui;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.net.ServerSocket;import java.net.Socket;public class Server {public static void main(String[] args) { try {// 為了看流程,我就把所有的代碼都放在main函數里了,也沒有捕捉異常,直接拋出去了。實際開發中不可取。// 1.新建ServerSocket對象,創建指定埠的連接ServerSocket serverSocket = new ServerSocket(12306);System.out.println("服務端監聽開始了~~~~");// 2.進行監聽Socket socket = serverSocket.accept();// 開始監聽9999埠,並接收到此套接字的連接。// 3.拿到輸入流(客戶端發送的信息就在這裡)InputStream is = socket.getInputStream();// 4.解析數據InputStreamReader reader = new InputStreamReader(is);BufferedReader bufReader = new BufferedReader(reader);String s = null;StringBuffer sb = new StringBuffer();while ((s = bufReader.readLine()) != null) {sb.append(s);}System.out.println("伺服器:" + sb.toString());// 關閉輸入流socket.shutdownInput();OutputStream os = socket.getOutputStream();os.write(("我是服務端,客戶端發給我的數據就是:"+sb.toString()).getBytes());os.flush();// 關閉輸出流socket.shutdownOutput();os.close();// 關閉IO資源bufReader.close();reader.close();is.close();socket.close();// 關閉socketserverSocket.close();// 關閉ServerSocket} catch (IOException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}}}

注意:在使用TCP編程的時候,最後需要釋放資源,關閉socket(socket.close());關閉socket輸入輸出流(socket.shutdownInput()以及socket.shutdownOutput());關閉IO流(is.close() os.close())。需要注意的是:關閉socket的輸入輸出流需要放在關閉io流之前。因為, **關閉IO流會同時關閉socket,一旦關閉了socket的,就不能再進行socket的相關操作了。而,只關閉socket輸入輸出流(socket.shutdownInput()以及socket.shutdownOutput())不會完全關閉socket,此時任然可以進行socket方面的操作。**所以要先調用socket.shutdownXXX,然後再調用io.close();

客戶端:

頁面文件沒什麼好看的。然後就是點擊button的時候發送數據,收到數據展示出來。我們這裡主要看點擊按鈕時做的事情。

public void onClick(View view){new Thread(){@Overridepublic void run() {super.run();try {//1.創建監聽指定伺服器地址以及指定伺服器監聽的埠號Socket socket = new Socket("111.111.11.11", 12306);//111.111.11.11為我這個本機的IP地址,埠號為12306.//2.拿到客戶端的socket對象的輸出流發送給伺服器數據OutputStream os = socket.getOutputStream();//寫入要發送給伺服器的數據os.write(et.getText().toString().getBytes());os.flush();socket.shutdownOutput();//拿到socket的輸入流,這裡存儲的是伺服器返回的數據InputStream is = socket.getInputStream();//解析伺服器返回的數據InputStreamReader reader = new InputStreamReader(is);BufferedReader bufReader = new BufferedReader(reader);String s = null;final StringBuffer sb = new StringBuffer();while((s = bufReader.readLine()) != null){sb.append(s);}runOnUiThread(new Runnable() {@Overridepublic void run() {tv.setText(sb.toString());}});//3、關閉IO資源(註:實際開發中需要放到finally中)bufReader.close();reader.close();is.close();os.close();socket.close();} catch (UnknownHostException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}}.start();}

注意!實際開發中的關閉IO資源需要放到finally中。這裡主要是為了先理解TCP編程的socket通信。還有,上面講過的io.close()需要放到socket.showdownXX()後面。關於new Socket("111.111.11.11", 12306),如何查看本機地址,自己百度哦~~~

整體運行結果如下:

TCP的單線程編程

在上圖中,我們手機端發送完一個請求后,服務端(Server)拿到數據,解析數據,返回給客戶端數據,關閉所有資源,也就是伺服器關閉了。這時,如果另一個客戶端再想跟伺服器進行通信時,發現伺服器已經關閉了,無法與伺服器再次進行通信。換句話說,只能跟伺服器通信一次,服務端 只能支持單線程數據處理。也就是說,上面的伺服器的代碼無法實現多線程編程,只能進行一次通信。那麼如果我們想實現server的多線程數據處理,使得server處理完我這個請求后不會關閉,任然可以處理其他客戶端的請求,怎麼辦呢?

TCP的多線程編程

思路:在上面例子中,我們執行serversocket.accept()等待客戶端去連接,與客戶建立完連接后,拿到對應的socket,然後進行相應的處理。那麼多個客戶端的請求,我們就一直不關閉ServerSocket,一直等待客戶端連接,一旦建立連接拿到socket,就可以吧這個socket放到單獨的線程中,從而實現這個建立連接的端到端通信的socket在自己單獨的線程中處理。這樣就能實現Socket的多線程處理。

step1:創建ServerThread,單獨處理拿到的socket,使得客戶端到伺服器端的這個socket會話在一個單獨的線程中。package com.hui;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStream;import java.net.Socket;public class ServerThread extends Thread{private Socket socket;//在構造中得到要單獨會話的socketpublic ServerThread(Socket socket) {this.socket = socket;}@Overridepublic void run() {super.run();InputStreamReader reader = null;BufferedReader bufReader = null;OutputStream os = null; try {reader = new InputStreamReader(socket.getInputStream());bufReader = new BufferedReader(reader);String s = null;StringBuffer sb = new StringBuffer();while((s = bufReader.readLine()) != null){sb.append(s);}System.out.println("伺服器:"+sb.toString());//關閉輸入流socket.shutdownInput();//返回給客戶端數據os = socket.getOutputStream();os.write(("我是服務端,客戶端發給我的數據就是:"+sb.toString()).getBytes());os.flush();socket.shutdownOutput();} catch (IOException e2) {e2.printStackTrace();} finally{//關閉IO資源if(reader != null){try {reader.close();} catch (IOException e) {e.printStackTrace();}}if(bufReader != null){try {bufReader.close();} catch (IOException e) {e.printStackTrace();}}if(os != null){try {os.close();} catch (IOException e) {e.printStackTrace();}}} }}step2:創建MultiThreadServerpackage com.hui;import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;public class MultiThreadServer {public static void main(String[] args) {try {ServerSocket serverSocket = new ServerSocket(12306);//死循環while(true){System.out.println("MultiThreadServer~~~監聽~~~");//accept方法會阻塞,直到有客戶端與之建立連接Socket socket = serverSocket.accept();ServerThread serverThread = new ServerThread(socket);serverThread.start();}} catch (IOException e) {e.printStackTrace();} catch(Exception e){e.printStackTrace();}}}

下面我使用兩個手機,多次進行與伺服器的連接,演示如下:總體結果:

TCP 的多線程通信單獨看兩個手機

重要的事情說三遍!萬丈高樓平地起!萬丈高樓平地起!!萬丈高樓平地起!!!只有當我們明白了最底層的,知識才是最牢固的。上面的講解的是基於TCP協議的socket編程。而我們後來將要講的HTTP相關的大都是基於TCP/IP協議的。一個TCP/IP協議我們又不能直接使用,Socket可以說是TCP/IP協議的抽象與包裝,然後我們就可以做相對於TCP/IP的網路通信與信息傳輸了。

UDP編程

上面我們講解了基於TCP協議的Socket編程,現在開始我們就開始講解基於UDP協議的Socket編程了。UDP,是User Datagram Protocol,也就是用戶數據包協議。關鍵點在於「數據包」。主要就是把數據進行打包然後丟給目標,而不管目標是否接收到數據。主要的流程就是:發送者打包數據(DatagramPacket)然後通過DatagramSocket發送,接收者收到數據包解開數據。

主要API:DatagramPacket,用來包裝發送的數據構造方法

發送數據的構造DatagramPacket(byte[] buf, int length,SocketAddress address)DatagramPacket(byte[] buf, int length, InetAddress address, int port)用來將長度為 length 的包發送到指定主機上的指定埠號。length 參數必須小於等於 buf.length。

接收數據的構造:public DatagramPacket(byte[] buf, int length)用來接收長度為 length 的數據包。

DatagramSocket:

構造方法DatagramSocket()構造數據報套接字並將其綁定到本地主機上任何可用的埠 。套接字將被綁定到通配符地址,IP 地址由內核來選擇。

DatagramSocket(int port)創建數據報套接字並將其綁定到本地主機上的指定埠。套接字將被綁定到通配符地址,IP 地址由內核來選擇。

發送數據send(DatagramPacket p)從此套接字發送數據報包。DatagramPacket 包含的信息指示:將要發送的數據、其長度、遠程主機的 IP 地址和遠程主機的埠號。接收數據receive(DatagramPacket p)從此套接字接收數據報包。當此方法返回時,DatagramPacket 的緩衝區填充了接收的數據。數據報包也包含發送方的 IP 地址和發送方機器上的埠號。

下面開始代碼了

客戶端

主要頁面與上面的tcp一致,只不過是通訊時的方法改了。如下:

private void udp() {byte[] bytes = et.getText().toString().getBytes();try {/*******************發送數據***********************/InetAddress address = InetAddress.getByName("192.168.232.2");//1.構造數據包DatagramPacket packet = new DatagramPacket(bytes, bytes.length, address, 12306);//2.創建數據報套接字並將其綁定到本地主機上的指定埠。DatagramSocket socket = new DatagramSocket();//3.從此套接字發送數據報包。socket.send(packet);/*******************接收數據***********************///1.構造 DatagramPacket,用來接收長度為 length 的數據包。final byte[] bytes1 = new byte[1024];DatagramPacket receiverPacket = new DatagramPacket(bytes1, bytes1.length);socket.receive(receiverPacket);runOnUiThread(new Runnable() {@Overridepublic void run() {tv.setText(new String(bytes1, 0, bytes1.length));}});//socket.close();} catch (UnknownHostException e) {e.printStackTrace();} catch (SocketException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}服務端

UDPServer

package com.hui;import java.io.IOException;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetSocketAddress;public class UDPServer {public static void main(String[] args) throws IOException {byte[] buf = new byte[1024];// 一:接受數據// 1.創建接受數據的數據包DatagramPacket packet = new DatagramPacket(buf, buf.length);// 2.創建UPD 的 socketDatagramSocket socket = new DatagramSocket(12306);// 3.接收數據System.out.println("服務端開始監聽!~~~~");socket.receive(packet);// 4.處理數據System.out.println("服務端:" + new String(buf, 0, buf.length));// 二:返回數據DatagramPacket p = new DatagramPacket(buf, buf.length, packet.getAddress(), packet.getPort());socket.send(p);socket.close();}}UDP通信TCP與UDP區別與使用場景

至此,基於TCP、UDP協議的Socket通信已經講完了基礎部分。那麼這兩個協議在實際中有什麼區別,分別適用於什麼場景呢?

TCP

對於TCP的數據傳輸而言,傳輸數據之前需要進行三次握手建立穩定的連接。建立連接通道后,數據包會在這個通道中以位元組流的形式進行數據的傳輸。由於建立穩定連接后才開始傳輸數據,而同時還是以位元組流的形式發送數據,所以發送數據速度較慢,但是不會造成數據包丟失。即使數據包丟失了,會進行數據重發。同時,如果收到的數據包順序錯亂,會進行排序糾正。

三次握手??這個網路上的解釋太多了,想詳細了解的自行去百度上Google一下。簡單理解的就是這樣的:我家是農村的,記得小時后爺爺在田裡種地。到了晌午時間,奶奶快燒好飯後我都要去喊爺爺吃飯,因為干農活的地離家裡不遠不近的,我就跑到隔壁家裡的平頂房上喊爺爺吃飯。我先大喊一聲「爺爺,回家吃飯啦」。爺爺如果聽到我說的話就會給我一個應答「好的!知道了,馬上就回去,你們先吃吧!」我只有聽到了這句話,才知道爺爺這個時候能聽到我說的話,我然後就再次回答爺爺:「好的!那你快點!」這三句話說完,就確定了我能聽到爺爺的應答,爺爺也能聽到我的回復。這樣我就確定我跟爺爺之間的喊話通道是正常的,如果還想對爺爺說什麼話,直接說就好了。最後,爺爺聽到了我說的話,就不再回復我的話了,然後,拿起鋤頭回來了。

總結下來,就是面向連接、數據可靠,速度慢,有序的。適用於需要安全穩定傳輸數據的場景。例如後面要講解的HTTP、HTTPS網路協議,FTP文件傳輸協議以及POP、SMTP郵件傳輸協議。或者開發交易類、支付類等軟體時,都需要基於TCP協議的Socket連接進行安全可靠的數據傳輸等等

UDP

對於UDP的數據傳輸而言,UDP不會去建立連接。它不管目的地是否存在,直接將數據發送給目的地,同時不會過問發送的數據是否丟失,到達的數據是否順序錯亂。如果你想處理這些問題的話,需要自己在應用層自行處理。總結下來,不面向連接、數據不可靠、速度快、無序的。適用於需要實時性較高不較為關注數據結果的場景,例如:打電話、視頻會議、廣播電台,等。

_,最後的最後,歡迎拍磚。家裡要蓋房子了,上海的房價傷不起~~~~

我的GitHub

免責聲明:非本網註明原創的信息,皆為程序自動獲取互聯網,目的在於傳遞更多信息,並不代表本網贊同其觀點和對其真實性負責;如此頁面有侵犯到您的權益,請給站長發送郵件,並提供相關證明(版權證明、身份證正反面、侵權鏈接),站長將在收到郵件12小時內刪除。