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