?計(jì)算機(jī)網(wǎng)絡(luò)—網(wǎng)絡(luò)編程套接字之UDP數(shù)據(jù)報(bào)套接字編程
作者介紹:
??作者:偷偷敲代碼的青花瓷?????
??作者的Gitee:代碼倉(cāng)庫(kù)
??系列文章推薦:計(jì)算機(jī)網(wǎng)絡(luò) ——網(wǎng)絡(luò)原理之初識(shí)
??我和大家一樣都是熱愛編程?,很高興能在此和大家分享知識(shí),希望在分享知識(shí)的同時(shí),能和大家一起共同進(jìn)步,取得好成績(jī)??,今天大家進(jìn)入網(wǎng)絡(luò)編程的新章節(jié),如果有錯(cuò)誤?,歡迎指正喲??,咋們廢話不多說,跟緊步伐,開始學(xué)習(xí)吧~??
Socket套接字
概念
Socket套接字,是由系統(tǒng)提供用于網(wǎng)絡(luò)通信的技術(shù),是基于TCP/IP協(xié)議的網(wǎng)絡(luò)通信的基本操作單元。基于Socket套接字的網(wǎng)絡(luò)程序開發(fā)就是網(wǎng)絡(luò)編程。
分類
Socket套接字主要針對(duì)傳輸層協(xié)議劃分為如下三類:
第一類:流套接字:使用傳輸層TCP協(xié)議 TCP,即Transmission Control Protocol(傳輸控制協(xié)議),傳輸層協(xié)議
。
TCP的特點(diǎn):
1.有連接
2.可靠傳輸
3.面向字節(jié)流
4.有接收緩沖區(qū),也有發(fā)送緩沖區(qū)
5.大小不限
對(duì)于字節(jié)流來說,可以簡(jiǎn)單的理解為,傳輸數(shù)據(jù)是基于IO流,流式數(shù)據(jù)的特征就是在IO流沒有關(guān)閉的情況下,是無邊界的數(shù)據(jù),可以多次發(fā)送,也可以分開多次接收
。
第二類:數(shù)據(jù)報(bào)套接字:使用傳輸層UDP協(xié)議 , UDP,即User Datagram Protocol(用戶數(shù)據(jù)報(bào)協(xié)議),傳輸層協(xié)議
。
UDP特點(diǎn):
1.無連接
2.不可靠傳輸
3.面向數(shù)據(jù)報(bào)
4.有接受緩沖區(qū),無法送緩沖區(qū)
5.大小受限:一次最多傳輸64k
對(duì)于數(shù)據(jù)報(bào)來說,可以簡(jiǎn)單的理解為,傳輸數(shù)據(jù)是一塊一塊的
,發(fā)送一塊數(shù)據(jù)假如100個(gè)字節(jié),必須一
次發(fā)送,接收也必須一次接收100個(gè)字節(jié),而不能分100次,每次接收1個(gè)字節(jié)。
第三類:原始套接字
原始套接字用于自定義傳輸層協(xié)議,用于讀寫內(nèi)核沒有處理的IP協(xié)議數(shù)據(jù)。(簡(jiǎn)單了解即可)
今天主要講解Socket套接字的第二類:UDP
UDP數(shù)據(jù)報(bào)套接字編程
在 TCP/IP 協(xié)議的傳輸層除了一個(gè) TCP 協(xié)議之外,還有一個(gè) UDP 協(xié)議。UDP 協(xié)議是用戶數(shù)據(jù)報(bào)協(xié)議的簡(jiǎn)稱,也用于網(wǎng)絡(luò)數(shù)據(jù)的傳輸。雖然 UDP 協(xié)議是一種不太可靠的協(xié)議,但有時(shí)在需要較快地接收數(shù)據(jù)并且可以忍受較小錯(cuò)誤的情況下,UDP 就會(huì)表現(xiàn)出更大的優(yōu)勢(shì)。
Java中UDP套接字編程步驟
下面是在 Java 中使用 UDP 協(xié)議發(fā)送數(shù)據(jù)的步驟。
1.使用 DatagramSocket() 創(chuàng)建一個(gè)數(shù)據(jù)包套接字。
2.使用 DatagramPacket() 創(chuàng)建要發(fā)送的數(shù)據(jù)包
3.使用 DatagramSocket 類的 send() 方法發(fā)送數(shù)據(jù)包。
接收 UDP 數(shù)據(jù)包的步驟如下:
1.使用 DatagramSocket 創(chuàng)建數(shù)據(jù)包套接字,并將其綁定到指定的端口。
2.使用 DatagramPacket 創(chuàng)建字節(jié)數(shù)組來接收數(shù)據(jù)包。
3.使用 DatagramPacket 類的 receive() 方法接收 UDP 包。
UDP數(shù)據(jù)報(bào)套接字有
兩個(gè)核心類
:DatagramSocket 和 DatagramPacket
DatagramSocket API
DatagramSocket 是 UDP Socket,用于發(fā)送和接收UDP 數(shù)據(jù)報(bào)
DatagramSocket 的構(gòu)造方法:
DatagramSocket 的常用方法:
DatagramPacket API
DatagramPacket 的構(gòu)造方法
DatagramPacket 的常用方法
代碼實(shí)例以及詳細(xì)圖文解析
實(shí)例1:回顯服務(wù)
進(jìn)行網(wǎng)絡(luò)編程,第一步就需要
先準(zhǔn)備好 socket 實(shí)例
,這是進(jìn)行網(wǎng)絡(luò)編程的大前提
注意:
多個(gè)進(jìn)程不能綁定同一個(gè)端口,一個(gè)進(jìn)程能夠綁定多個(gè)端口。
啟動(dòng)服務(wù)器,分三個(gè)步驟:
1.讀取客服端發(fā)來的請(qǐng)求
2.根據(jù)請(qǐng)求計(jì)算響應(yīng)
3.把響應(yīng)寫回到客戶端
客戶端:
輸出結(jié)果:
代碼實(shí)現(xiàn):
服務(wù)器端:
// 服務(wù)器端:
package network;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
// 站在服務(wù)器的角度:
// 1. 源 IP: 服務(wù)器程序本機(jī)的 IP
// 2. 源端口: 服務(wù)器綁定的端口 (此處手動(dòng)指定了 9090)
// 3. 目的 IP: 包含在收到的數(shù)據(jù)報(bào)中. (客戶端的IP)
// 4. 目的端口: 包含在收到的數(shù)據(jù)報(bào)中. (客戶端的端口)
// 5. 協(xié)議類型: UDP
public class UdpEchoServer {
// 進(jìn)行網(wǎng)絡(luò)編程, 第一步就需要先準(zhǔn)備好 socket 實(shí)例~ 這是進(jìn)行網(wǎng)絡(luò)編程的大前提.
private DatagramSocket socket = null;
public UdpEchoServer(int port) throws SocketException {
socket = new DatagramSocket(port);
}
// 啟動(dòng)服務(wù)器.
public void start() throws IOException {
System.out.println("啟動(dòng)服務(wù)器!");
// UDP 不需要建立連接, 直接接收從客戶端來的數(shù)據(jù)即可
while (true) {
// 1. 讀取客戶端發(fā)來的請(qǐng)求
DatagramPacket requestPacket = new DatagramPacket(new byte[1024], 1024);
socket.receive(requestPacket); // 為了接受數(shù)據(jù), 需要先準(zhǔn)備好一個(gè)空的 DatagramPacket 對(duì)象, 由 receive 來進(jìn)行填充數(shù)據(jù)
// 把 DatagramPacket 解析成一個(gè) String
String request = new String(requestPacket.getData(), 0, requestPacket.getLength(), "UTF-8");
// 2. 根據(jù)請(qǐng)求計(jì)算響應(yīng)(由于咱們這是一個(gè)回顯服務(wù), 2 省略)
String response = process(request);
// 3. 把響應(yīng)寫回到客戶端
DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,
requestPacket.getSocketAddress());
socket.send(responsePacket);
System.out.printf("[%s:%d] req: %s, resp: %s\n",
requestPacket.getAddress().toString(), requestPacket.getPort(), request, response);
}
}
// 由于是回顯服務(wù), 響應(yīng)就和請(qǐng)求一樣了.
// 實(shí)際上對(duì)于一個(gè)真實(shí)的服務(wù)器來說, 這個(gè)過程是最復(fù)雜的. 為了實(shí)現(xiàn)這個(gè)過程, 可能需要幾萬行, 幾十萬行代碼....
public String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
UdpEchoServer server = new UdpEchoServer(9090);
server.start();
}
}
客戶端:
package network;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Scanner;
public class UdpEchoClient {
private DatagramSocket socket = null;
private String serverIP;
private int serverPort;
// 站在客戶端的角度:
// 源 IP: 本機(jī) IP
// 源端口: 系統(tǒng)分配的端口
// 目的 IP: 服務(wù)器的 IP
// 目的端口: 服務(wù)器的端口
// 協(xié)議類型: UDP
public UdpEchoClient(String ip, int port) throws SocketException {
// 此處的 port 是服務(wù)器的端口.
// 客戶端啟動(dòng)的時(shí)候, 不需要給 socket 指定端口. 客戶端自己的端口是系統(tǒng)隨機(jī)分配的~~
socket = new DatagramSocket();
serverIP = ip;
serverPort = port;
}
public void start() throws IOException {
Scanner scanner = new Scanner(System.in);
while (true) {
// 1. 先從控制臺(tái)讀取用戶輸入的字符串
System.out.print("-> ");
String request = scanner.next();
// 2. 把這個(gè)用戶輸入的內(nèi)容, 構(gòu)造成一個(gè) UDP 請(qǐng)求, 并發(fā)送.
// 構(gòu)造的請(qǐng)求里包含兩部分信息:
// 1) 數(shù)據(jù)的內(nèi)容. request 字符串
// 2) 數(shù)據(jù)要發(fā)給誰~ 服務(wù)器的 IP + 端口
DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,
InetAddress.getByName(serverIP), serverPort);
socket.send(requestPacket);
// 3. 從服務(wù)器讀取響應(yīng)數(shù)據(jù), 并解析
DatagramPacket responsePacket = new DatagramPacket(new byte[1024], 1024);
socket.receive(responsePacket);
String response = new String(responsePacket.getData(), 0, responsePacket.getLength(), "UTF-8");
// 4. 把響應(yīng)結(jié)果顯示到控制臺(tái)上.
System.out.printf("req: %s, resp: %s\n", request, response);
}
}
public static void main(String[] args) throws IOException {
// 由于服務(wù)器和客戶端在同一個(gè)機(jī)器上, 使用的 IP 仍然是 127.0.0.1 . 如果是在不同的機(jī)器上, 當(dāng)然就需要更改這里的 IP 了
UdpEchoClient client = new UdpEchoClient("127.0.0.1", 9090);
client.start();
}
}
實(shí)例2:程序翻譯(英譯漢 )
請(qǐng)求:一些簡(jiǎn)單的英語單詞 響應(yīng):英語單詞對(duì)應(yīng)的翻譯
這個(gè)簡(jiǎn)單的程序我們需要知道,客戶端不變
,把服務(wù)器代碼進(jìn)行調(diào)整,主要是調(diào)整 process 方法,讀取請(qǐng)求并分析,把響應(yīng)寫會(huì)給客戶端,這倆步驟都一樣,關(guān)鍵的邏輯就是"根據(jù)請(qǐng)求處理響應(yīng)"
代碼實(shí)現(xiàn):
package network;
import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
public class UdpDictServer extends UdpEchoServer {
private HashMap<String, String> dict = new HashMap<>();
public UdpDictServer(int port) throws SocketException {
super(port);
// 簡(jiǎn)單構(gòu)造幾個(gè)詞
dict.put("cat", "小貓");
dict.put("dog", "小狗");
dict.put("fuck", "臥槽");
dict.put("pig", "小豬");
}
@Override
public String process(String request) {
return dict.getOrDefault(request, "該詞無法被翻譯!");
}
public static void main(String[] args) throws IOException {
UdpDictServer server = new UdpDictServer(9090);
server.start();
}
}
輸出結(jié)果:
總結(jié)
“種一顆樹最好的是十年前,其次就是現(xiàn)在”
所以,
“讓我們一起努力吧,去奔赴更高更遠(yuǎn)的山海”
如果有錯(cuò)誤?,歡迎指正喲??文章來源:http://www.zghlxwxcb.cn/news/detail-406732.html
??如果覺得收獲滿滿,可以動(dòng)動(dòng)小手,點(diǎn)點(diǎn)贊??,支持一下喲??文章來源地址http://www.zghlxwxcb.cn/news/detail-406732.html
到了這里,關(guān)于計(jì)算機(jī)網(wǎng)絡(luò)---網(wǎng)絡(luò)編程套接字(一)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!