日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

基于Java的TCP Socket通信详解(计算机端/Android手机端)

發布時間:2023/12/15 Android 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于Java的TCP Socket通信详解(计算机端/Android手机端) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

TCP Socket通信是一種比較常用的基于連接的網絡通信方式。本文通過Java實現TCP Socket通信,并將其用于計算機端、Android手機端,同時做到代碼規范化,實現代碼最大化復用。

本文代碼可在GitHub下載,建議對照源碼閱讀文章?https://github.com/jzj1993/JavaTcpSocket

TCP連接的建立

客戶端和服務器間通過三次握手建立TCP連接。在Java中,連接建立完成后,服務器端和客戶端分別獲取到一個Socket實例,之后就可以通過這個Socket實例進行通信。服務器端和客戶端使用不同的方法獲取Socket實例。

服務器端

在服務器端,通過ServerSocket實現對指定端口的監聽,代碼如下。其中port為int型端口數值,取值0~65535,0~1024為系統保留端口,這里取值1234。如果發生錯誤將會拋出異常。

  • int port = 1234;
  • ServerSocket server = new ServerSocket(port);
  • 通過ServerSocket.accept()方法接受客戶端連接。這個方法是阻塞的,從調用時開始監聽端口,直到客戶端連接建立時,執行結束并返回Socket實例。連接建立失敗會拋出異常。

  • Socket socket = server.accept();
  • 客戶端

    客戶端直接通過實例化的形式,產生Socket實例。實例化的過程中,嘗試連接指定的服務器主機。連接成功則實例化完成,連接失敗則拋出異常。hostIP為主機的IP地址,port為端口號,和服務器主機監聽的端口號保持一致。

  • String hostIP = "127.0.0.1";
  • int port = 1234;
  • Socket socket = new Socket(hostIP, port);
  • 連接的建立過程

    以上代碼的執行順序是:

  • 服務器端實例化ServerSocket:new ServerSocket(port);
  • 服務器端執行accept(),監聽指定端口,此方法阻塞等待客戶端連接:server.accept();
  • 客戶端實例化Socket實例,嘗試連接服務器:new Socket(hostIP, port);
  • TCP三次握手成功,服務器端的accept()返回Socket實例,同時客戶端的Socket實例化成功。
  • Socket的讀寫

    以收發字符串為例來說明Socket的讀寫。

    向Socket對象寫入數據,則會發送至TCP連接的另一方。這個操作在服務器端和客戶端是一樣的??赏ㄟ^獲取Socket的輸出流來寫入UTF8格式編碼的字符串,代碼如下。寫入完成后,就會被發送到連接的另一端。

  • private DataOutputStream out;
  • out = new DataOutputStream(socket.getOutputStream());
  • String s = "Test";
  • out.writeUTF(s);
  • out.flush();
  • 在接收端,通過獲取Socket的輸入流,就可以讀取字符串數據,代碼如下。readUTF()方法是阻塞的,直到對方發送完一個字符串,該方法才會執行結束并返回收到的字符串。如果連接中斷,或強制關閉Socket的輸入流,即執行socket.shutdownInput(),該方法會拋出異常。

  • private DataInputStream in;
  • in = new DataInputStream(socket.getInputStream());
  • String s = in.readUTF();
  • 在建立了TCP連接后,由于無法確定對方的數據發送時間,為了保證及時接收到數據,通過一個新線程不斷調用in.readUTF()方法讀取數據(相當于輪詢法);并在接收到數據后回調相關函數,對數據進行處理。

    TCP連接的斷開

    TCP Socket連接是雙向的,通過四次揮手的方式斷開,雙方分別調用Socket.close()方法斷開連接。連接斷開的過程中,一般一方A先斷開連接,另一方B發現A斷開連接后,也斷開連接。為方便表述,將先斷開連接的一方A稱為“主動斷開連接”;后斷開的一方B,則為“被動斷開連接”。

    在一方B阻塞執行in.readUTF()方法時,如果對方A主動斷開Socket連接,這個方法會拋出異常。從而在B處理異常時,可以被動的斷開這邊的連接。

    為保證主動斷開連接的一方不會阻塞在in.readUTF()方法中,需要先執行socket.shutdownInput()。所以主動斷開連接的代碼如下。

  • socket.shutdownInput();
  • in.close();
  • socket.close();
  • 被動斷開連接的一方,在捕獲到in.readUTF()的異常后,斷開Socket連接。

  • try {
  • String s = in.readUTF();
  • } catch (IOException e) {
  • // 連接被斷開(被動)
  • try {
  • in.close();
  • socket.close();
  • in = null;
  • socket = null;
  • } catch (IOException e) {
  • e.printStackTrace();
  • }
  • }
  • SocketTransceiver的實現

    考慮到在服務器端和客戶端,Socket對象的操作是完全一樣的,所以實現了一個SocketTransceiver(收發器),實現對Socket的直接操作,其他代碼則通過SocketTransceiver間接操作Socket對象實現數據收發、斷開連接等。

    SocketTransceiver實現的功能有:

    • 開啟新線程不斷查詢Socket是否收到數據;
    • 將字符串、文件等類型的數據進行打包,并通過Socket發送;
    • 從Socket接收數據,并自動解析出數據(字符串、文件等),接收完成后回調相應的方法;
    • 在發生錯誤、連接被動斷開時,自動斷開連接并進行相關處理,并回調相應方法。

    SocketTransceiver.class使用抽象類實現,回調方法是抽象的,實例化時對抽象方法進行實現,處理回調。完整代碼見附件。

    TcpServer的實現

    TcpServer為TCP Socket服務器端程序。為了讓服務器能同時接受并處理來自多個客戶端的TCP連接請求:

    • TcpServer中用一個監聽線程對端口進行監聽,即阻塞執行server.accept()方法,等待接受客戶端連接;
    • 服務器端每次與一個客戶端建立連接,即accept()方法執行結束并返回一個Socket對象,就會用一個SocketTransceiver對這個Socket進行操作;
    • 連接建立后,監聽線程再次執行server.accept()方法,繼續監聽端口并等待下一個連接;
    • 服務器端有一個List<SocketTransceiver>,保存當前連接的每個客戶端對應的SocketTransceiver對象,在需要時可取出并進行操作。

    TcpServer.class的完整代碼見附件。

    TcpClient的實現

    TcpClient為TCP Socket客戶端程序。主要工作是進行Socket的連接,并利用SocketTransceiver對Socket進行操作。TcpClient.class的完整代碼見附件。

    桌面服務器端的測試代碼

    完成了上面這三個主要的類,服務器端的實現就非常容易了。服務器端需要使用SocketTransceiver.class、TcpServer.class兩個類。

    服務器端編寫了一個桌面版本,測試代碼ClsMainServer.class在工程SocketServer-Desktop中,詳見附件。在測試代碼中,實現了一個服務器程序,監聽端口1234并接受客戶端連接,每次接收到客戶端主動發送的數據,就將數據原樣返回給這個客戶端。

    桌面客戶端的測試代碼

    客戶端需要使用SocketTransceiver.class、TcpClient.class兩個類。

    客戶端的桌面版本測試代碼ClsMainClient.class在工程SocketClient-Desktop中,詳見附件。在測試代碼中,實現了兩個客戶端,并交替向服務器發送數據。客戶端直接連接本機服務器端,IP地址為127.0.0.1。

    安卓客戶端的實現

    同樣的代碼還可以直接用于安卓客戶端。安卓客戶端編寫了一個簡單的界面,輸入IP和端口可進行連接,同時可以輸入字符串發送至服務器端,另外會將接收到的數據顯示出來。

    安卓端程序的實現,要注意幾點:

    • 網絡操作代碼不能在主線程(即UI線程)中執行
    • 界面操作不能在非UI線程執行,回調中不能直接執行刷新界面的代碼,可以通過向UI線程的Handler發送Runnable對象進行操作,即Handler.post(Runnable r)方法。
    • 需要在AndroidManifest.xml中添加網絡訪問權限<uses-permission android:name="android.permission.INTERNET" />

    測試可用的代碼詳見附件中的工程SocketClient-Android。

    網絡連接相關問題

    如果代碼編譯通過,但是測試不能正常運行,則有可能為網絡連接配置相關問題。

  • 電腦和手機之間的網絡連接方式

    • 電腦共享Wifi,用手機連接
    • 手機電腦連在一個局域網
  • 確保IP地址與端口設置正確
    確保程序中的IP地址和端口設置正確。服務器端代碼只需設置端口,最好不要處于0~1024之間,并避免端口被系統其它程序占用;在客戶端,使用的端口和服務器相同,IP地址設置如下:

    • 如果客戶端和服務器是在同一個設備上,IP地址可以直接用127.0.0.1,也可以用服務器任意網卡的IP地址
    • 如果服務器和客戶端處于同一個局域網,IP地址用服務器連接到該局域網的網卡的IP地址
    • 如果客戶端連接到服務器共享的Wifi網絡,IP地址用服務器共享Wifi的無線網卡的IP地址
  • 防火墻設置
    在Windows中,需要設置防火墻允許程序訪問網絡。通常在軟件第一次訪問網絡時,會彈出窗口提示是否允許程序訪問網絡,勾選允許并點擊確定即可。也可以在控制面板設置:控制面板 --> 系統和安全 --> Windows 防火墻 --> 允許程序或功能通過Windows防火墻 --> 更改設置 --> 勾選Java后面的選框并確認即可,如圖。

  • 嘗試用管理員權限運行Eclipse
    如果仍然不能連接,可以嘗試用管理員權限運行Eclipse。

  • 代碼測試

    Eclipse窗口操作技巧

    • 默認情況下,多個程序同時在命令行輸出時,Eclipse的Console會自動切換到最后輸出字符串的程序,點擊圖釘形狀的按鈕如圖,可以讓Console不再切換。
    • 點擊右側的加號按鈕,下拉菜單中選擇New Console View,可以新建一個Console命令行窗口,設置每個窗口顯示不同程序輸出的內容。
    • 點擊Console中的紅色方形按鈕可以停止程序的運行。

    測試

    將工程SocketServer-Desktop和SocketClient-Desktop導入Eclipse中,先執行服務器端,再執行客戶端。如果先執行客戶端,會提示連接失敗。

    桌面客戶端程序啟動后會創建兩個TcpClient客戶端實例同時連接服務器,并交替發送數據??蛻舳诉B接和發送數據時,服務器端會輸出相應的提示信息。

    停止服務器端程序,客戶端會提示連接中斷,并中止程序執行;停止客戶端程序,服務器端會提示客戶端斷開連接。

    將安卓版的Client編譯并安裝到手機,連接網絡讓安卓端和計算機端在同一個局域網中。讓服務器端程序啟動,再點擊安卓端的“連接”按鈕進行連接,如果網絡錯誤、服務器未啟動等原因,安卓端會提示“連接失敗”。如果連接成功,服務端會輸出客戶端連接的提示信息。

    同樣,可以在安卓客戶端向服務器發送數據,服務器會將數據原樣返回。

    執行結果如圖。

    本文由jzj1993原創,轉載請注明來源:http://www.hainter.com/java-tcp-socket

    總結

    以上是生活随笔為你收集整理的基于Java的TCP Socket通信详解(计算机端/Android手机端)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。