Java Socket

来源:http://www.cnblogs.com/xrq730/archive/2016/01/05/5087354.html
-Advertisement-
Play Games

什麼是SocketSocket的概念很簡單,它是網路上運行的兩個程式間雙向通訊的一端,既可以接收請求,也可以發送請求,利用它可以較為方便地編寫網路上數據的傳遞。所以簡而言之,Socket就是進程通信的端點,Socket之間的連接過程可以分為幾步:1、伺服器監聽伺服器端Socket並不定位具體的客戶端...


什麼是Socket

Socket的概念很簡單,它是網路上運行的兩個程式間雙向通訊的一端,既可以接收請求,也可以發送請求,利用它可以較為方便地編寫網路上數據的傳遞

所以簡而言之,Socket就是進程通信的端點,Socket之間的連接過程可以分為幾步:

1、伺服器監聽

伺服器端Socket並不定位具體的客戶端Socket,而是處於等待連接的狀態,實時監控網路狀態

2、客戶端請求

客戶端Socket發出連接請求,要連接的目標是服務端Socket。為此,客戶端Socket必須首先描述它要連接的服務端Socket,指出服務端Socket的地址和埠號,然後就向服務端Socket提出連接請求

3、連接確認

當服務端Socket監聽到或者說是接收到客戶端Socket的連接請求,它就響應客戶端Socket的請求,建立一個新的線程,把服務端Socket的描述發給客戶端,一旦客戶端確認了此描述,連接就好了。而服務端Socket繼續處於監聽狀態,繼續接收其他客戶端套接字的連接請求

 

TCP/IP、HTTP、Socket的區別

這三個概念是比較容易混淆的概念,這裡儘量解釋一下三者之間的區別。

隨著電腦網路體繫結構的發展,OSI七層網路模型誕生了,這個模型把開放系統的通信功能劃分為七個層次,一次完整的通信如下圖:

每一層都是相互獨立的,它利用其下一層提供的服務併為其上一層提供服務,而與其它層的具體實現無關,所謂"服務"就是下一層向上一層提供的通信功能和層之間的會話約定,一般用通信原語實現。上圖中,從下至上分別給層編號為1~7,其中1~4層為下層協議,5~7層為上層協議,接著回到我們的概念:

1、TCP/IP講的其實是兩個東西:TCP和IP。IP是一種網路層的協議,用於路由選擇、網路互連

2、TCP是一種傳輸層協議,用於建立、維護和拆除傳送連接,在系統之間提供可靠的透明的數據傳送

3、HTTP是一種應用層協議,提供OSI用戶服務,例如事物處理程式、文件傳送協議和網路管理等,其目的最終是為了實現應用進程之間的信息交換

至於Socket,它只是TCP/IP網路的API而已,Socket介面定義了許多函數,用以開發TCP/IP網路上的應用程式,組織數據,以符合指定的協議。

 

Socket的兩種模式

Socket有兩種主要的操作方式:面向連接和無連接的。面向連接的Socket操作就像一部電話,必須建立一個連接和一人呼叫,所有事情在達到時的順序與它們出發時的順序一樣,無連接的Socket操作就像是一個郵件投遞,沒有什麼保證,多個郵件可能在達到時的順序與出發時的順序不一樣。

到底使用哪種模式是由應用程式的需要決定的。如果可靠性更重要的話,用面向連接的操作會好一些,比如文件伺服器需要數據的正確性和有序性,如果一些數據丟失了,系統的有效性將會失去;比如一些伺服器間歇性地發送一些數據塊,如果數據丟失了的話,伺服器並不想要再重新發送一次,因為當數據到達的時候,它可能已經過時了。確保數據的有序性和正確性需要額外的操作的記憶體消耗,額外的消耗將會降低系統的回應速率。

無連接的操作使用數據報協議。一個數據報是一個獨立的單元,它包含了所有這次投遞的信息,就像一個信封,它有目的地址和要發送的內容,這個模式下的Socket並不需要連接一個目的Socket,它只是簡單地透出數據報,無連接的操作是快速、高效的,但是數據安全性不佳。

面向連接的操作使用TCP協議。一個這個模式下的Socket必須在發送數據之前與目的地的Socket取得一個連接,一旦連接建立了,Socket就可以使用一個流介面:打開-->讀-->寫-->關閉,所有發送的信息都會在另一端以同樣的順序被接收。面向連接的操作比無連接的操作效率更低,但是數據的安全性更高。

 

利用Java開發Socket

在Java中面向連接的類有兩種形式,它們分別是客戶端和伺服器端,先看一下伺服器端:

public class HelloServer
{
    public static void main(String[] args) throws IOException
    {
        ServerSocket serverSocket = null;
        
        try
        {
            // 實例化一個伺服器端的Socket連接
            serverSocket = new ServerSocket(9999);
        } 
        catch (IOException e)
        {
            System.err.print("Could not listen on port:9999");
            System.exit(1);
        }
        
        Socket clientSocket = null;
        try
        {
            // 用於接收來自客戶端的連接
            clientSocket = serverSocket.accept();
        } 
        catch (IOException e)
        {
            System.err.println("Accept failed");
            System.exit(1);
        }
        
        // 客戶端有數據了就向屏幕列印Hello World
        System.out.print("Hello World");
        clientSocket.close();
        serverSocket.close();
    }
}

此代碼的作用就是構造出服務端Socket,並等待來自客戶端的消息。當然,此時運行代碼是沒有任何反應的,因為服務端在等待客戶端的連接。下麵看一下客戶端代碼如何寫:

 1 public class HelloClient
 2 {
 3     public static void main(String[] args) throws IOException
 4     {
 5         Socket socket = null;
 6         BufferedReader br = null;
 7         
 8         // 下麵這段程式,用於將輸入輸出流和Socket相關聯
 9         try
10         {
11             socket = new Socket("localhost", 9999);
12             br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
13         } 
14         catch (UnknownHostException e)
15         {
16             System.err.println("Don't know about host:localhost");
17             System.exit(1);
18         }
19         catch (IOException e)
20         {
21             System.err.println("Could not get I/O for the connection");
22             System.exit(1);
23         }
24         
25         System.out.print(br.readLine());
26         br.close();
27         socket.close();
28     }
29 }

此時只需要先運行HelloServer,再運行HelloClient,保證伺服器先監聽,客戶端後發送,就可以在控制臺上看到"Hello World"了。

 

改進版本的Socket

上面的Socket演示的效果是,伺服器端Socket收到了來自客戶端Socket的數據,但是並沒有真正地體現伺服器端Socket和客戶端Socket的交互,下麵演示一下利用Socket進行伺服器端和客戶端的交互,首先是伺服器端的:

 1 public class EchoServer
 2 {
 3     public static void main(String[] args) throws IOException
 4     {
 5         ServerSocket ss = null;
 6         PrintWriter pw = null;
 7         BufferedReader br = null;
 8         
 9         try
10         {
11             // 實例化監聽埠
12             ss = new ServerSocket(1111);
13         } 
14         catch (IOException e)
15         {
16             System.err.println("Could not listen on port:1111");
17             System.exit(1);
18         }
19         Socket incoming = null;
20         while (true)
21         {
22             incoming = ss.accept();
23             pw = new PrintWriter(incoming.getOutputStream(), true);
24             // 先將位元組流通過InputStreamReader轉換為字元流,之後將字元流放入緩衝之中
25             br = new BufferedReader(new InputStreamReader(incoming.getInputStream()));
26             // 提示信息
27             pw.println("Hello!...");
28             pw.println("Enter BYE to exit");
29             pw.flush(); 
30             // 沒有異常則不斷迴圈
31             while (true)
32             {
33                 // 只有當用戶輸入時才返回數據
34                 String str = br.readLine();
35                 // 當用戶連接斷掉時會返回空值null
36                 if (str == null)
37                 {
38                     // 退出迴圈
39                     break;
40                 }
41                 else
42                 {
43                     // 對用戶輸入字元串加首碼Echo並將此信息列印到客戶端
44                     pw.println("Echo:" + str);
45                     pw.flush();
46                     // 退出命令,equalsIgnoreCase()是不區分大小寫的
47                     if ("BYE".equalsIgnoreCase(str.trim()))
48                     {
49                         break;
50                     }
51                 }
52             }
53             // 該close的資源都close掉
54             pw.close();
55             br.close();
56             incoming.close();
57             ss.close();
58         }
59     }
60 }

接著是客戶端的:

 1 public class EchoClient
 2 {
 3     public static void main(String[] args) throws IOException
 4     {
 5         Socket socket = null;
 6         PrintWriter pw = null;
 7         BufferedReader br = null;
 8         
 9         try
10         {
11             socket = new Socket("localhost", 1111);
12             pw = new PrintWriter(socket.getOutputStream(), true);
13             br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
14         }
15         catch (UnknownHostException e)
16         {
17             System.err.println("Don't know abount host:localhost");
18             System.exit(1);
19         }
20         System.out.println(br.readLine());
21         System.out.println(br.readLine());
22         BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
23         String userInput;
24         // 將客戶端Socket輸入流(即伺服器端Socket的輸出流)輸出到標準輸出上
25         while ((userInput = stdIn.readLine()) != null)
26         {
27             pw.println(userInput);
28             System.out.println(br.readLine());
29         }
30         // 同樣的,將該關閉的資源給關閉掉
31         pw.close();
32         br.close();
33         socket.close();
34     }
35 }

看一下運行結果:

這正是我們程式要達到的效果,客戶端不管輸入什麼,伺服器端都給輸入拼接上"Echo:"返還給客戶端並列印在屏幕上。

 

服務端多監聽

程式寫到上面,已經基本成型了,不過還有一個問題:現實情況中,一個伺服器端的Socket不可能只對應一個客戶端的Socket,必然一個伺服器端的Socket可以接收來自多個客戶端的Socket的請求。

解決上述問題的辦法就是多線程。大致代碼是這樣的:

public class HandleThread extends Thread
{
    private Socket socket;
    
    public HandleThread(Socket socket)
    {
        this.socket = socket;
    }
    
    public void run()
    {
        // Socket處理代碼
    }
}
public static void main(String[] args) throws IOException
{
    ServerSocket serverSocket = null;
    
    try
    {
        // 實例化一個伺服器端的Socket連接
        serverSocket = new ServerSocket(9999);
    } 
    catch (IOException e)
    {
        System.err.print("Could not listen on port:9999");
        System.exit(1);
    }
        
    Socket clientSocket = null;
    try
    {
                while (true)
                {
            // 用於接收來自客戶端的連接
            clientSocket = serverSocket.accept();
                    new HandleThread(clientSocket).start();
            }
        } 
    catch (IOException e)
    {
        System.err.println("Accept failed");
        System.exit(1);
    }
}    

即,伺服器端啟動一個永遠運行的線程,監聽來自客戶端的Socket,一旦客戶端有Socket到來,即開啟一個新的線程將Socket交給線程處理。

值得一提的是,這種Socket IO的處理方式,是一種阻塞性的IO操作,也就是說一個客戶端的Socket必然對應一條伺服器端的Socket線程,且Socket未處理完無法釋放該線程,這並不是一種好的辦法。進一步改進,可以使用NIO,NIO採用了Reactor模式,利用事件驅動機制,可以使用幾條較少的線程就處理多客戶端的請求。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • inten常見動作:MAIN_ACTION(主視圖)、 VIEW_ACTION(查看)、 EDIT_ACTION(修改)、 PICK_ACTION 、GET_CONTENT_ACTION(獲取內容)、 DIAL_ACTION 、CALL_ACTION 、SENDTO_ACTION、 ANSWER_A...
  • 這個特性是andorid4.4支持的,最少要api19才可以使用,也就是說如果Android的機子是低於4.4,沉浸通知欄是沒有效果的。下麵介紹一下使用的方法,非常得簡單。/** * 設置通知欄 這個方法在onCreate()實現,如果是在父類的onCreate()中添加,即使所有繼承了該父類都會....
  • //1、創建主線程(串列) dispatch_async(dispatch_get_main_queue(), ^{ //刷新界面代碼 }); //2、創建非同步線程(並行) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORIT...
  • 一,效果圖。二,工程圖。三,代碼。RootViewController.h#import #import "SVSegmentedControl.h"@interface RootViewController : UIViewController{ UIScrollView *scrollVi...
  • //1.設置背景 //tf.backgroundColor = [[UIColor blueColor] colorWithAlphaComponent:0.7]; //2.設置輸入框的樣式 /* UITextBorderStyleNone, 預設樣式,無樣式 UITextBorder...
  • 前言:之前公司app在騰訊開放平臺認領應用時,涉及了一個問題:就是給空白包簽名。然後再上傳上去審核。騰訊開放平臺的官方說明如下,如何簽名:jarsgner-verbose-keystore[keystorePath]-singnedjar [apkOut] [apkln] [alias]jarsgn...
  • 前些時間空閑,寫了個簡單的小小工具類,即圖片的裁剪,動畫改變frame隱藏;本類直接提供一個類方法調用,傳入3個參數即可,代碼非常簡單,誰都可以看懂;參數說明:第一個參數:你要將裁剪後的圖片添加到哪個視圖上執行動畫,傳入當前view即可;第二個參數:傳入一張你要進行裁剪的圖片;第三個參數:背景圖,可...
  • hi剛看了唐人街探案,5星好評啊親。由於是早就約好的,也不好推辭(雖然是和男的..),但該寫的還是得擠時間寫。明天早上老師的項目結題,雖然和我關係不大,但不要添亂就好!!1、PHP一、PHP基礎(三)1.3.2 Int整型。幾個點:進位問題(2,8,16);溢出——溢出後自動轉為float型;整.....
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...