欧美free性护士vide0shd,老熟女,一区二区三区,久久久久夜夜夜精品国产,久久久久久综合网天天,欧美成人护士h版

首頁綜合 正文
目錄

柚子快報激活碼778899分享:10.JAVAEE之網(wǎng)絡(luò)編程

柚子快報激活碼778899分享:10.JAVAEE之網(wǎng)絡(luò)編程

http://yzkb.51969.com/

1.網(wǎng)絡(luò)編程

通過網(wǎng)絡(luò),讓兩個主機之間能夠進行通信 =>基于這樣的通信來完成一定的功能進行網(wǎng)絡(luò)編程的時候,需要操作系統(tǒng)給咱們提供一組 AP1, 通過這些 API才能完成編程(API 可以認為是 應(yīng)用層 和 傳輸層 之間交互的路徑)(API:Socket API相當于一個插座:通過這一套 Socket AP| 可以完成不同主機之間,不同系統(tǒng)之間的網(wǎng)絡(luò)通信)傳輸層,提供的網(wǎng)絡(luò)協(xié)議,主要是兩個:TCP UDP這倆協(xié)議的特性(工作原理) 差異很大.導(dǎo)致,使用這兩種協(xié)議進行網(wǎng)絡(luò)編程,也存在一定差別系統(tǒng)就分別提供了兩套 APITCP 和 UDP 的區(qū)別.(后面網(wǎng)絡(luò)原理章節(jié), 學(xué)習的重點)

? ? ? ?1.TCP 是有連接的, UDP 是無連接的

? ? ? ?2.TCP 是可靠傳輸?shù)?UDP 是不可靠傳輸?shù)?/p>

? ? ? ?3.TCP 是面向字節(jié)流的,UDP 是面向數(shù)據(jù)報

? ? ? ?4.TCP 和 UDP 都是全雙工的

?1.TCP 是有連接的, UDP 是無連接的 (連接 是 抽象 的概念) 計算機中,這種 抽象 的連接是很常見的,此處的連接本質(zhì)上就是建立連接的雙方,各自保存對方的信息兩臺計算機建立連接,就是雙方彼此保存了對方的關(guān)鍵信息~~ TCP 要想通信, 就需要先建立連接 (剛才說的, 保存對方信息),做完之后,才能后續(xù)通信(如果 A 想和 B 建立連接, 但是 B 拒絕了! 通信就無法完成!!!)

UDP 想要通信,就直接發(fā)送數(shù)據(jù)即可~~不需要征得對方的同意,UDP 自身也不會保存對方的信息(UDP 不知道,但是寫程序的人得知道.UDP 自己不保存,但是你調(diào)用 UDP 的 socket api的時候要把對方的位置啥的給傳過去)

2.TCP 是可靠傳輸?shù)?UDP 是不可靠傳輸?shù)?網(wǎng)絡(luò)上進行通信, A ->B 發(fā)送一個消息,這個消息是不可能做到 100% 送達的!!?

可靠傳輸,退而求其次. A ->B 發(fā)消息,消息是不是到達 B 這一方,A 自己能感知到.(A 心里有數(shù))進一步的,就可以在發(fā)送失敗的時候采取一定的措施(嘗試重傳之類的)

TCP 就內(nèi)置了可靠傳輸機制;UDP 就沒有內(nèi)置可靠傳輸

【tips】可靠傳輸,聽起來挺美好的呀, 為啥不讓 UDP 也搞個可靠傳輸呢??

想要可靠傳輸,你就是要付出代價的(需要去交換) 可靠傳輸要付出什么代價? 1)機制更復(fù)雜 2)傳輸效率會降低

3.TCP 是面向字節(jié)流的,UDP 是面向數(shù)據(jù)報 此處說的 字節(jié)流 和 文件 操作這里的 字節(jié)流 是一個意思!!! TCP 也是和文件操作一樣,以字節(jié)為單位來進行傳輸. UDP 則是按照數(shù)據(jù)報為單位,來進行傳輸?shù)?UDP 數(shù)據(jù)報是有嚴格的格式的?

網(wǎng)絡(luò)通信數(shù)據(jù)的基本單位,涉及到多種說法~~ 1.數(shù)據(jù)報(Datagram) 2.數(shù)據(jù)包(Packet)

3.數(shù)據(jù)幀(Frame) 4.數(shù)據(jù)段 (Segment)?

4. TCP 和 UDP 都是全雙工的 一個信道,允許雙向通信, 就是全雙工 一個信道,只能單向通信,就是半雙工 代碼中使用一個 Socket 對象, 就可以發(fā)送數(shù)據(jù)也能接受數(shù)據(jù)~~?

2.UDP 的 socket api 如何使用

Datagramsocket?

Datagrampacket?

【回顯服務(wù)器:(echo server)】

寫一個簡單的 UDP 的客戶端/服務(wù)器 通信的程序. 這個程序沒有啥業(yè)務(wù)邏輯,只是單純的調(diào)用 socket api. 讓客戶端給服務(wù)器發(fā)送一個請求,請求就是一個從控制臺輸入的字符串. 服務(wù)器收到字符串之后,也就會把這個字符串原封不動的返回給客戶端,客戶端再顯示出來.?

import java.io.IOException;

import java.net.DatagramPacket;

import java.net.DatagramSocket;

import java.net.SocketException;

public class UdpEchoServer {

// 創(chuàng)建一個 DatagramSocket 對象. 后續(xù)操作網(wǎng)卡的基礎(chǔ).

private DatagramSocket socket = null;

public UdpEchoServer(int port) throws SocketException {

// 這么寫就是手動指定端口

socket = new DatagramSocket(port);

// 這么寫就是讓系統(tǒng)自動分配端口

// socket = new DatagramSocket();

}

public void start() throws IOException {

// 通過這個方法來啟動服務(wù)器.

System.out.println("服務(wù)器啟動!");

// 一個服務(wù)器程序中, 經(jīng)常能看到 while true 這樣的代碼.

while (true) {

// 1. 讀取請求并解析.

DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);

socket.receive(requestPacket);

// 當前完成 receive 之后, 數(shù)據(jù)是以 二進制 的形式存儲到 DatagramPacket 中了.

// 要想能夠把這里的數(shù)據(jù)給顯示出來, 還需要把這個二進制數(shù)據(jù)給轉(zhuǎn)成字符串.

String request = new String(requestPacket.getData(), 0, requestPacket.getLength());

// 2. 根據(jù)請求計算響應(yīng)(一般的服務(wù)器都會經(jīng)歷的過程)

// 由于此處是回顯服務(wù)器, 請求是啥樣, 響應(yīng)就是啥樣.

String response = process(request);

// 3. 把響應(yīng)寫回到客戶端.

// 搞一個響應(yīng)對象, DatagramPacket

// 往 DatagramPacket 里構(gòu)造剛才的數(shù)據(jù), 再通過 send 返回.

DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,

requestPacket.getSocketAddress());

socket.send(responsePacket);

// 4. 打印一個日志, 把這次數(shù)據(jù)交互的詳情打印出來.

System.out.printf("[%s:%d] req=%s, resp=%s\n", requestPacket.getAddress().toString(),

requestPacket.getPort(), request, response);

}

}

public String process(String request) {

return request;

}

public static void main(String[] args) throws IOException {

UdpEchoServer server = new UdpEchoServer(9090);

server.start();

}

}

import java.io.IOException;

import java.net.*;

import java.util.Scanner;

public class UdpEchoClient {

private DatagramSocket socket = null;

private String serverIp = "";

private int serverPort = 0;

public UdpEchoClient(String ip, int port) throws SocketException {

// 創(chuàng)建這個對象, 不能手動指定端口.

socket = new DatagramSocket();

// 由于 UDP 自身不會持有對端的信息. 就需要在應(yīng)用程序里, 把對端的情況給記錄下來.

// 這里咱們主要記錄對端的 ip 和 端口 .

serverIp = ip;

serverPort = port;

}

public void start() throws IOException {

System.out.println("客戶端啟動!");

Scanner scanner = new Scanner(System.in);

while (true) {

// 1. 從控制臺讀取數(shù)據(jù), 作為請求

System.out.print("-> ");

String request = scanner.next();

// 2. 把請求內(nèi)容構(gòu)造成 DatagramPacket 對象, 發(fā)給服務(wù)器.

DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,

InetAddress.getByName(serverIp), serverPort);

socket.send(requestPacket);

// 3. 嘗試讀取服務(wù)器返回的響應(yīng)了.

DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);

socket.receive(responsePacket);

// 4. 把響應(yīng), 轉(zhuǎn)換成字符串, 并顯示出來.

String response = new String(responsePacket.getData(), 0, responsePacket.getLength());

System.out.println(response);

}

}

public static void main(String[] args) throws IOException {

UdpEchoClient client = new UdpEchoClient("127.0.0.1", 9090);

// UdpEchoClient client = new UdpEchoClient("42.192.83.143", 9090);

client.start();

}

}

【客戶端】

對于服務(wù)器來說,也就需要把端口號給明確下來了~~客戶端的端口號是不需要確定的.交給系統(tǒng)進行分配即可,如果你手動指定確定的端口,就可能和別人的程序的端口號沖突【tips】服務(wù)器這邊手動指定端口,就不會出現(xiàn)沖突嘛?? 為啥客戶端在意這個沖突,而服務(wù)器不在意呢?? 服務(wù)器是在程序猿手里的,一個服務(wù)器上都有哪些程序, 都使用哪些端口,程序猿都是可控的!!程序猿寫代碼的時候,就可以指定一個空閑的端口,給當前的服務(wù)器使用即可 但是客戶端就不可控,客戶端是在用戶的電腦上;一方面,用戶千千萬~~ 每個用戶電腦上裝的程序都不一樣,占用的端口也不一樣;交給系統(tǒng)分配比較穩(wěn)妥.系統(tǒng)能保證肯定分配一個空閑的端口

服務(wù)器一旦啟動,就會立即執(zhí)行到這里的 receive 方法此時,客戶端的請求可能還沒來呢~~ 這種情況也沒關(guān)系.receive 就會直接阻塞, 就會一直阻塞到真正客戶端把請求發(fā)過來為止,(類似于阻塞隊列)

【question】

//根據(jù)請求計算響應(yīng)(核心步驟)

這個步驟是一個服務(wù)器程序,最核心的步驟!!! 咱們當前是 echo server 不涉及到這些流程,也不必考慮響應(yīng)怎么計算,只要請求過來,就把請求當做響應(yīng)

【question】

【question】上述寫的代碼中,為啥沒寫 close?? socket 也是文件,不關(guān)閉不就出問題了,不就文件資源泄露了么,為啥這里咱們可以不寫 close?為啥不寫 close 也不會出現(xiàn)文件資源泄露??

private DatagramSocket socket = null; 這個 socket 在整個程序運行過程中都是需要使用的(不能提前關(guān)閉)當 socket 不需要使用的時候, 意味著程序就要結(jié)束了

進程結(jié)束,此時隨之文件描述符表就會銷毀了(PCB 都銷毀了).談何泄露?? 隨著銷毀的過程,被系統(tǒng)自動回收了~~

啥時候才會出現(xiàn)泄露?代碼中頻繁的打開文件,但是不關(guān)閉在一個進程的運行過程中,不斷積累打開的文件,逐漸消耗掉文件描述符表里的內(nèi)容最終就消耗殆盡了 但是如果進程的生命周期很短,打開一下沒多久就關(guān)閉了.談不上泄露文件資源泄露這樣的問題,在服務(wù)器這邊是比較 嚴重的, 在客戶端這邊一般來說影響不大.

?【服務(wù)器】

【交互】

1.服務(wù)器先啟動.服務(wù)器啟動之后,就會進入循環(huán),執(zhí)行到 receive 這里并阻塞 (此時還沒有客戶端過來呢) 2.客戶端開始啟動,也會先進入 while 循環(huán),執(zhí)行 scanner.next.并且也在這里阻塞當用戶在控制臺輸入字符串之后,next 就會返回,從而構(gòu)造請求數(shù)據(jù)并發(fā)送出來~~

3.客戶端發(fā)送出數(shù)據(jù)之后, 服務(wù)器: 就會從 receive 中返回,進一步的執(zhí)行解析請求為字符串,執(zhí)行 process 操作,執(zhí)行 send 操作 客戶端: 繼續(xù)往下執(zhí)行,執(zhí)行到 receive,等待服務(wù)器的響應(yīng)

4.客戶端收到從服務(wù)器返回的數(shù)據(jù)之后,就會從 receive 中返回執(zhí)行這里的打印操作,也就把響應(yīng)給顯示出來了 5.服務(wù)器這邊完成一次循環(huán)之后, 又執(zhí)行到 receive 這里,客戶端這邊完成一次循環(huán)之后,又執(zhí)行到 scanner.next 這里雙雙進入阻塞

?【翻譯服務(wù)器】

import java.io.IOException;

import java.net.SocketException;

import java.util.HashMap;

import java.util.Map;

public class UdpDictServer extends UdpEchoServer {

private Map dict = new HashMap<>();

public UdpDictServer(int port) throws SocketException {

super(port);

// 此處可以往這個表里插入幾千幾萬個這樣的英文單詞.

dict.put("dog", "小狗");

dict.put("cat", "小貓");

dict.put("pig", "小豬");

}

// 重寫 process 方法, 在重寫的方法中完成翻譯的過程.

// 翻譯本質(zhì)上就是 "查表"

@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();

}

}

上述重寫 process 方法,就可以在子類中組織你想要的"業(yè)務(wù)邏輯",(你要寫代碼解決一些實際的問題)

3.TCP 的 socket api 如何使用?

TCP 的 socket api 和 UDP 的 socket api 差異又很大~,

但是和前面講的 文件操作,有密切聯(lián)系的

兩個關(guān)鍵的類

1.ServerSocket(給服務(wù)器使用的類,使用這個類來綁定端口號)

2.Socket(既會給服務(wù)器用,又會給客戶端用)

這倆類都是用來表示 socket 文件的,(抽象了網(wǎng)卡這樣的硬件設(shè)備)

TCP 是字節(jié)流的.傳輸?shù)幕締挝?是 byte

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.io.PrintWriter;

import java.net.Socket;

import java.util.Scanner;

public class TcpEchoClient {

private Socket socket = null;

public TcpEchoClient(String serverIp, int serverPort) throws IOException {

// 需要在創(chuàng)建 Socket 的同時, 和服務(wù)器 "建立連接", 此時就得告訴 Socket 服務(wù)器在哪里~~

// 具體建立連接的細節(jié), 不需要咱們代碼手動干預(yù). 是內(nèi)核自動負責的.

// 當我們 new 這個對象的時候, 操作系統(tǒng)內(nèi)核, 就開始進行 三次握手 具體細節(jié), 完成建立連接的過程了.

socket = new Socket(serverIp, serverPort);

}

public void start() {

// tcp 的客戶端行為和 udp 的客戶端差不多.

// 都是:

// 3. 從服務(wù)器讀取響應(yīng).

// 4. 把響應(yīng)顯示到界面上.

Scanner scanner = new Scanner(System.in);

try (InputStream inputStream = socket.getInputStream();

OutputStream outputStream = socket.getOutputStream()) {

PrintWriter writer = new PrintWriter(outputStream);

Scanner scannerNetwork = new Scanner(inputStream);

while (true) {

// 1. 從控制臺讀取用戶輸入的內(nèi)容

System.out.print("-> ");

String request = scanner.next();

// 2. 把字符串作為請求, 發(fā)送給服務(wù)器

// 這里使用 println, 是為了讓請求后面帶上換行.

// 也就是和服務(wù)器讀取請求, scanner.next 呼應(yīng)

writer.println(request);

writer.flush();

// 3. 讀取服務(wù)器返回的響應(yīng).

String response = scannerNetwork.next();

// 4. 在界面上顯示內(nèi)容了.

System.out.println(response);

}

} catch (IOException e) {

e.printStackTrace();

}

}

public static void main(String[] args) throws IOException {

TcpEchoClient client = new TcpEchoClient("127.0.0.1", 9090);

client.start();

}

}

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.io.PrintWriter;

import java.net.ServerSocket;

import java.net.Socket;

import java.util.Scanner;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

public class TcpEchoServer {

private ServerSocket serverSocket = null;

public TcpEchoServer(int port) throws IOException {

serverSocket = new ServerSocket(port);

}

public void start() throws IOException {

System.out.println("服務(wù)器啟動!");

ExecutorService service = Executors.newCachedThreadPool();

while (true) {

// 通過 accept 方法, 把內(nèi)核中已經(jīng)建立好的連接拿到應(yīng)用程序中.

// 建立連接的細節(jié)流程都是內(nèi)核自動完成的. 應(yīng)用程序只需要 "撿現(xiàn)成" 的.

Socket clientSocket = serverSocket.accept();

// 此處不應(yīng)該直接調(diào)用 processConnection, 會導(dǎo)致服務(wù)器不能處理多個客戶端.

// 創(chuàng)建新的線程來調(diào)用更合理的做法.

// 這種做法可行, 不夠好

// Thread t = new Thread(() -> {

// processConnection(clientSocket);

// });

// t.start();

// 更好一點的辦法, 是使用線程池.

service.submit(new Runnable() {

@Override

public void run() {

processConnection(clientSocket);

}

});

}

}

// 通過這個方法, 來處理當前的連接.

public void processConnection(Socket clientSocket) {

// 進入方法, 先打印一個日志, 表示當前有客戶端連上了.

System.out.printf("[%s:%d] 客戶端上線!\n", clientSocket.getInetAddress(), clientSocket.getPort());

// 接下來進行數(shù)據(jù)的交互.

try (InputStream inputStream = clientSocket.getInputStream();

OutputStream outputStream = clientSocket.getOutputStream()) {

// 使用 try ( ) 方式, 避免后續(xù)用完了流對象, 忘記關(guān)閉.

// 由于客戶端發(fā)來的數(shù)據(jù), 可能是 "多條數(shù)據(jù)", 針對多條數(shù)據(jù), 就循環(huán)的處理.

while (true) {

Scanner scanner = new Scanner(inputStream);

if (!scanner.hasNext()) {

// 連接斷開了. 此時循環(huán)就應(yīng)該結(jié)束

System.out.printf("[%s:%d] 客戶端下線!\n", clientSocket.getInetAddress(), clientSocket.getPort());

break;

}

// 1. 讀取請求并解析. 此處就以 next 來作為讀取請求的方式. next 的規(guī)則是, 讀到 "空白符" 就返回.

String request = scanner.next();

// 2. 根據(jù)請求, 計算響應(yīng).

String response = process(request);

// 3. 把響應(yīng)寫回到客戶端.

// 可以把 String 轉(zhuǎn)成字節(jié)數(shù)組, 寫入到 OutputStream

// 也可以使用 PrintWriter 把 OutputStream 包裹一下, 來寫入字符串.

PrintWriter printWriter = new PrintWriter(outputStream);

// 此處的 println 不是打印到控制臺了, 而是寫入到 outputStream 對應(yīng)的流對象中, 也就是寫入到 clientSocket 里面.

// 自然這個數(shù)據(jù)也就通過網(wǎng)絡(luò)發(fā)送出去了. (發(fā)給當前這個連接的另外一端)

// 此處使用 println 帶有 \n 也是為了后續(xù) 客戶端這邊 可以使用 scanner.next 來讀取數(shù)據(jù).

printWriter.println(response);

// 此處還要記得有個操作, 刷新緩沖區(qū). 如果沒有刷新操作, 可能數(shù)據(jù)仍然是在內(nèi)存中, 沒有被寫入網(wǎng)卡.

printWriter.flush();

// 4. 打印一下這次請求交互過程的內(nèi)容

System.out.printf("[%s:%d] req=%s, resp=%s\n", clientSocket.getInetAddress(), clientSocket.getPort(),

request, response);

}

} catch (IOException e) {

e.printStackTrace();

} finally {

try {

// 在這個地方, 進行 clientSocket 的關(guān)閉.

// processConnection 就是在處理一個連接. 這個方法執(zhí)行完畢, 這個連接也就處理完了.

clientSocket.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

public String process(String request) {

// 此處也是寫的回顯服務(wù)器. 響應(yīng)和請求是一樣的.

return request;

}

public static void main(String[] args) throws IOException {

TcpEchoServer server = new TcpEchoServer(9090);

server.start();

}

}

【服務(wù)器】

內(nèi)核中有一個“隊列”(可以視為阻塞隊列)

如果有客戶端,和服務(wù)器建立連接,這個時候服務(wù)器的應(yīng)用程序是不需要做出任何操作(也沒有任何感知的),內(nèi)核直接就完成了連接建立的流程(三次握手).

完成流程之后,就會在內(nèi)核的隊列中(這個隊列是每個 serverSocket 都有一個這樣的隊列)。

排隊應(yīng)用程序要想和這個客戶端進行通信,就需要通過一個 accept 方法把內(nèi)核隊列里已經(jīng)建立好的連接對象,拿到應(yīng)用程序中。

【question】

前面寫過的 DatagramSocket, ServerSocket 都沒寫 close, 但是我們說這個東西都沒關(guān)系但是 clientSocket 如果不關(guān)閉,就會真的泄露了!!! DatagramSocket 和 ServerSocket,都是在程序中,只有這么一個對象.申明周期, 都是貫穿整個程序的.

而ClientSocket 則是在循環(huán)中,每次有一個新的客戶端來建立連接,都會創(chuàng)建出新的clientSocket

每次執(zhí)行這個,都會創(chuàng)建新的 clientSocket,并且這個 socket 最多使用到 該客戶端退出(斷開連接) 此時,如果有很多客戶端都來建立連接~~此時,就意味著每個連接都會創(chuàng)建 clientSocket.當連接斷開clientSocket 就失去作用了,但是如果沒有手動 close此時這個 socket 對象就會占據(jù)著文件描述符表的位置

【客戶端】

【question】出現(xiàn)一個bug

當前啟動兩個客戶端,同時連接服務(wù)器. 其中一個客戶端(先啟動的客戶端) 一切正常. 另一個客戶端 (后啟動的客戶端)則沒法和服務(wù)器進行任何交互,(服務(wù)器不會提示"建立連接”,也不會針對 請求 做出任何響應(yīng))

上述bug和代碼結(jié)構(gòu)密切相關(guān)

確實如剛才推理的現(xiàn)象一樣,第一個客戶端結(jié)束的時候,就從 processConnection 返回了就可以執(zhí)行到第二次 accept 了,也就可以處理第二個客戶端了~~ 很明顯,如果啟動第三個客戶端,第三個客戶端也會僵硬住,又會需要第二個客戶端結(jié)束才能活過來...

如何解決上述問題?讓一個服務(wù)器可以同時接待多個客戶端呢??

關(guān)鍵就是,在處理第一個客戶端的請求的過程中,要讓代碼能夠快速的第二次執(zhí)行到 accept ~~~【多線程】

上述這里的關(guān)鍵,就是讓這兩個循環(huán)能夠"并發(fā)"執(zhí)行. 各自執(zhí)行各自的,不會因為進入一個循環(huán)影響到另一個~~

【剛才出現(xiàn)這個問題的關(guān)鍵在于兩重循環(huán)在一個線程里進入第二重循環(huán)的時候,無法繼續(xù)執(zhí)行第一個循環(huán). Udp 版本的服務(wù)器,當時是只有一個循環(huán),不存在類似的問題~~(前面部署到云服務(wù)器的時候)】

柚子快報激活碼778899分享:10.JAVAEE之網(wǎng)絡(luò)編程

http://yzkb.51969.com/

相關(guān)閱讀

評論可見,查看隱藏內(nèi)容

本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。

轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。

本文鏈接:http://gantiao.com.cn/post/18822651.html

發(fā)布評論

您暫未設(shè)置收款碼

請在主題配置——文章設(shè)置里上傳

掃描二維碼手機訪問

文章目錄