Java TCP編程之Scoket
一、什么是Scoket
Socket 是一個抽象概念,一個應用程序通過一個 Socket 來建立一個遠程連接,而 Socket 內部通 過 TCP/IP 協(xié)議把數(shù)據(jù)傳輸?shù)骄W(wǎng)絡。
┌───────────┐ ┌───────────┐
│Application│ │Application│
├───────────┤ ├───────────┤
│ Socket │ │ Socket │
├───────────┤ ├───────────┤
│ TCP │ │ TCP │
├───────────┤ ┌──────┐ ┌──────┐ ├───────────┤
│ IP │<────>│Router│<─────>│Router│<────>│ IP │
└───────────┘ └──────┘ └──────┘ └───────────┘
Socket 、 TCP 和部分 IP 的功能都是由操作系統(tǒng)提供的,不同的編程語言只是提供了對操作系統(tǒng)調 用的簡單的封裝。例如, Java 提供的幾個 Socket 相關的類就封裝了操作系統(tǒng)提供的接口: Server Socket 類、 Socket 類。
為什么需要 Socket 進行網(wǎng)絡通信?因為僅僅通過 IP 地址進行通信是不夠的,同 一臺計算機同一時間會運行多個網(wǎng)絡應用程序,例如瀏覽器、QQ、郵件客戶端等。當 操作系統(tǒng)接收到一個數(shù)據(jù)包的時候,如果只有 IP 地址,它沒法判斷應該發(fā)給哪個應用 程序,所以,操作系統(tǒng)抽象出 Socket 接口,每個應用程序需要各自對應到不同的 S ocket ,數(shù)據(jù)包才能根據(jù) Socket 正確地發(fā)到對應的應用程序。
一個 Socket 就是由IP地址和端口號(范圍是0~65535)組成,可以把 Socket 簡單理解為 IP 地址加端口號。端口號總是由操作系統(tǒng)分配,它是一個 0 ~ 65535 之間的數(shù)字,其中,小于 1024 的端口屬于特權端口,需要管理員權限,大于 1024 的端口可以由任意用戶的應用程序打開。
使用 Socket 進行網(wǎng)絡編程時,本質上就是兩個進程之間的網(wǎng)絡通信。其中一個進 程必須充當服務器端,它會主動監(jiān)聽某個指定的端口,另一個進程必須充當客戶端,它 必須主動連接服務器的 IP 地址和指定端口,如果連接成功,服務器端和客戶端就成功 地建立了一個 TCP 連接,雙方后續(xù)就可以隨時發(fā)送和接收數(shù)據(jù)。
當 Socket 連接成功地在服務器端和客戶端之間建立后:
- 對服務器端來說,它的 Socket 是指定的 IP 地址和指定的端口號;
- 對客戶端來說,它的 Socket 是它所在計算機的 IP 地址和一個由操作系統(tǒng)分配的隨機端口號。
二、服務器端
package com.ljl.tcp.demo2; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.Writer; import java.net.ServerSocket; import java.net.Socket; import java.util.HashMap; import java.util.Map; public class ChatServer { public static void main(String[] args) { Map<String, String> chatMap = new HashMap<String, String>(){ { put("你好", "你好呀"); put("hi", "hi~"); put("hello", "哈嘍"); put("吃了嗎", "沒呢,你呢"); put("孤勇者", "愛你孤身走暗巷"); put("有請潘周聃", "潘周聃,今年29歲,蘇黎世理工大學....."); put("很高興認識你", "我也是哦"); } }; try (ServerSocket server = new ServerSocket(9966)) { while(true) { //客戶端連接 Socket client = server.accept(); //獲取客戶端IP地址 String clientIP = client.getInetAddress().getHostAddress(); try( BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream())); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));){ //獲取客戶端提問 String question = reader.readLine(); System.out.println("服務器來自客戶端"+clientIP+"的提問:"+question); //獲取問題答案 String answer = chatMap.get(question); answer = answer == null?"我不知道你在說啥":answer; //發(fā)送答案至客戶端 writer.write(answer); } } } catch (IOException e) { e.printStackTrace(); } } }
三、客戶端
package com.ljl.tcp.demo2; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.Socket; import java.util.Scanner; public class ChatClient { public static void main(String[] args) { Scanner input = new Scanner(System.in); while(true) { try (//創(chuàng)建Socket Socket client = new Socket("192.168.254.163",9966); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(client.getOutputStream())); BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream())); ){ //獲取控制臺的輸入問題 String question = input.nextLine(); if(question.equals("退下")) { break; } //發(fā)送問題至服務器 writer.write(question); writer.flush(); //暫時結束本次輸入 client.shutdownOutput(); //獲取來自服務器的答案 String answer = reader.readLine(); System.out.println("服務器回答:"+answer); } catch (IOException e) { e.printStackTrace(); } } System.out.println("over"); } }
四、Socket流
當 Socket 連接創(chuàng)建成功后,無論是服務器端,還是客戶端,我們都使用 Socket 實例進行網(wǎng)絡通信。因為 T CP 是一種基于流的協(xié)議,因此, Java 標準庫使用 InputStream 和 OutputStream 來封裝 Socket 的數(shù)據(jù)流,這 樣我們使用 Socket 的流,和普通 IO 流類似:
// 用于讀取網(wǎng)絡數(shù)據(jù): InputStream in = sock.getInputStream(); // 用于寫入網(wǎng)絡數(shù)據(jù): OutputStream out = sock.getOutputStream();
寫入網(wǎng)絡數(shù)據(jù)時,必須要調用 flush() 方法。如果不調用 flush() ,我們很可能會發(fā)現(xiàn),客戶端和服務器都 收不到數(shù)據(jù),這并不是 Java 標準庫的設計問題,而是我們以流的形式寫入數(shù)據(jù)的時候,并不是一寫入就立刻發(fā)送 到網(wǎng)絡,而是先寫入內存緩沖區(qū),直到緩沖區(qū)滿了以后,才會一次性真正發(fā)送到網(wǎng)絡,這樣設計的目的是為了提高 傳輸效率。如果緩沖區(qū)的數(shù)據(jù)很少,而我們又想強制把這些數(shù)據(jù)發(fā)送到網(wǎng)絡,就必須調用 flush() 強制把緩沖區(qū)數(shù) 據(jù)發(fā)送出去。
五、總結
使用Java進行TCP編程時,需要使用 Socket 模型:
- 服務器端用 ServerSocket 監(jiān)聽指定端口;
- 客戶端使用 Socket(InetAddress, port) 連接服務器;
- 服務器端用 accept() 接收連接并返回 Socket 實例;
- 雙方通過 Socket 打開 InputStream / OutputStream 讀寫數(shù)據(jù);
- 服務器端通常使用多線程同時處理多個客戶端連接,利用線程池可大幅提升效率;
- flush() 方法用于強制輸出緩沖區(qū)到網(wǎng)絡。
到此這篇關于Java TCP編程之Scoket的文章就介紹到這了,更多相關Java Scoket內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
MyBatis實現(xiàn)兩種查詢樹形數(shù)據(jù)的方法詳解(嵌套結果集和遞歸查詢)
樹形結構數(shù)據(jù)在開發(fā)中十分常見,比如:菜單數(shù)、組織樹, 利用 MyBatis 提供嵌套查詢功能可以很方便地實現(xiàn)這個功能需求。本文主要介紹了兩種方法,感興趣的可以了解一下2021-09-09Java服務調用RestTemplate與HttpClient的使用詳解
無論是微服務還是SOA,都面臨著服務間的遠程調用,這篇文章主要介紹了服務調用RestTemplate與HttpClient的使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-06-06