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

首頁綜合 正文
目錄

柚子快報邀請碼778899分享:【JavaEE初階】網(wǎng)絡編程

柚子快報邀請碼778899分享:【JavaEE初階】網(wǎng)絡編程

http://yzkb.51969.com/

??

歡迎關注個人主頁:逸狼

創(chuàng)造不易,可以點點贊嗎~

如有錯誤,歡迎指出~

?絡編程,指?絡上的主機,通過不同的進程,以編程的?式實現(xiàn)?絡通信(或稱為?絡數(shù)據(jù)傳 輸)。

socket api

Socket套接字,是由系統(tǒng)提供?于?絡通信的技術,是基于TCP/IP協(xié)議的?絡通信的基本操作單元。 基于Socket套接字的?絡程序開發(fā)就是?絡編程。

操作系統(tǒng)給應用程序(傳輸層給應用層)提供的api,就叫做socket api(這里學習Java版本的),有兩組不同的api,分別為UDP和TCP兩套版本

UDP 無連接 不可靠傳輸 面向數(shù)據(jù)報 全雙工TCP 有連接 可靠傳輸 面向字節(jié)流 全雙工

有/無連接: 通信雙方若保存了通信對端的信息(IP和端口),就是有連接,不保存就是無連接可靠/不可靠傳輸:盡可能考慮能夠到達對方就是可靠傳輸,完全不考慮就是不可靠傳輸.這個在代碼中沒辦法直接體現(xiàn),它是在內核中實現(xiàn)好的功能

TCP內置了一些機制(感知到對方是否收到; 重傳機制,在對方沒收到時進行重試)可以保證可靠傳輸( 但是可靠傳輸要付出代價 ,TCP協(xié)議設計要比UDP復雜很多,也會損失一些傳輸數(shù)據(jù)的效率)UDP沒有可靠性機制?面向字節(jié)流/數(shù)據(jù)報 :參數(shù)單位是字節(jié)的就是面向字節(jié)流,單位是數(shù)據(jù)包的就是面向數(shù)據(jù)報?

TCP是面向字節(jié)流的,傳輸過程和文件流/水流是一樣的特點UDP是面向數(shù)據(jù)報的,傳輸數(shù)據(jù)的基本單位就是UDP數(shù)據(jù)報,一次發(fā)送/接收必須是完整的數(shù)據(jù)報全/半雙工:一個通信鏈路,可以發(fā)送數(shù)據(jù),也可以接收數(shù)據(jù)就是全雙工; 只能發(fā)送或只能接收就是半雙工? 這里寫的代碼都是全雙工的,不考慮半雙工

UDP版本socket api

通過代碼不好直接操作網(wǎng)卡(網(wǎng)卡有很多不同的型號,之間提供的api都會有差別),操作系統(tǒng)就把網(wǎng)卡概念封裝成socket,應用程序員就不必關注硬件的差異和細節(jié),統(tǒng)一操作socket對象就能間接操作網(wǎng)卡;? socket 可以認為是操作系統(tǒng)中廣義文件下里的一種文件類型,這樣的文件就是網(wǎng)卡這種硬件設備的抽象表現(xiàn)形式

代碼部分需要實現(xiàn)兩個程序

socket相當于網(wǎng)卡的遙控器,網(wǎng)絡編程必須要操作網(wǎng)卡,就需要用到socket對象DatagramSocket

下面通過寫一個"回顯服務器(echo server)"(客戶端發(fā)的請求和服務器返回的響應一致)代碼示例加以理解

UDP服務器

import java.io.IOException;

import java.net.DatagramPacket;

import java.net.DatagramSocket;

import java.net.SocketException;

import java.nio.charset.StandardCharsets;

public class UdpEchoServer {

private DatagramSocket socket=null;

public UdpEchoServer(int port) throws SocketException {

socket = new DatagramSocket(port);

}

//通過start 啟動服務器的核心流程

public void start() throws IOException {

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

while(true){

//通過死循環(huán)不停的處理客戶端的請求

//1.讀取客戶端的請求并解析

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

socket.receive(requestPacket);

//上述收到的數(shù)據(jù),是二進制byte[]的形式體現(xiàn)的,后續(xù)代碼如果要進行打印之類的操作 需要將其轉成字符串

//構造string字符串 獲取字節(jié)報的數(shù)據(jù),從數(shù)組的0位置開始構造string,長度為字節(jié)報的長度

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

//2.根據(jù)請求計算響應,由于此處是回顯服務器,響應就是請求

String response = process(request);

//3.把響應寫回到客戶端

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

,requestPacket.getSocketAddress());//UDP是無連接的,所以要手動將客戶端的請求的IP和端口號取出并包裝到responsePacket里

socket.send(responsePacket);

//4.打印日志

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

,request,response);//獲取IP地址和端口號,請求和響應

}

}

private String process(String request){

return request;

}

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

UdpEchoServer server =new UdpEchoServer(9090);

server.start();

}

}

?

對于一個系統(tǒng)來說,同一時刻,同一個協(xié)議下,一個端口號,只能被一個進程綁定(端口號就是用來區(qū)分進程的,如果有多個進程嘗試綁定一個端口號,后來的進程就會綁定失敗),但是一個進程可以同時綁定多個端口號(通過創(chuàng)建多個socket對象來實現(xiàn))? ?

比如:9090端口在udp下被一個進程綁定了,還可以在TCP下被另一個進程綁定

receive

?

DatagramSocket 這個對象中,不持有對方(客戶端) 的 ip 和端口的. 所以進行 send 的時候,就需要在 send 的數(shù)據(jù)包里,把要發(fā)給誰這樣的信息,寫進去,才能夠正確的把數(shù)據(jù)進行返回

socket在使用完之后需要關閉,此處代碼中,socket生命周期整個進程一樣長,就算沒有close,進程關閉時也會釋放文件描述符表里的所有內容,相當于close了

UDP客戶端

import java.io.IOException;

import java.net.*;

import java.util.Scanner;

public class UdpEchoClient {

private DatagramSocket socket = null;

private String serverIP;

private int serverPort;

public UdpEchoClient(String serverIP ,int serverPort) throws SocketException {

socket =new DatagramSocket();

this.serverIP=serverIP;

this.serverPort =serverPort;

}

public void start() throws IOException {

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

Scanner scanner= new Scanner(System.in);

while(true){

//1.從控制臺讀取用戶的輸入

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

String request = scanner.next();

//2.構造出一個UDP請求,發(fā)送給服務器

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

InetAddress.getByName(this.serverIP),this.serverPort);//這里要將IP字符串轉換成int類型

socket.send(requestPacket);

//3.從服務器中讀取響應

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

socket.receive(responsePacket);

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

//4.把響應打印到控制臺上

System.out.println(response);

}

}

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

//127.0.0.1稱為環(huán)回IP,代表本機,如果服務器和客戶端在同一個主機上,就使用這個IP

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

udpEchoClient.start();

}

}

服務器這邊創(chuàng)建socket時一定要指定端口號,因為客戶端是通過端口號來找到服務器的,且客戶端是主動發(fā)起的一方.客戶端這邊創(chuàng)建socket時就最好不要指定端口號(不指定不代表沒有,客戶端的端口號是系統(tǒng)自動分配的一個端口,讓系統(tǒng)自動分配一個端口,就能確保分配的是一個無人使用的端口).如果在客戶端指定了端口號,由于客戶端是在用戶的電腦運行的,萬一代碼指定的端口和用戶電腦上運行的其他程序的端口沖突,就會產生bug

服務器和客戶端代碼執(zhí)行流程

TCP版本的socket api

TCP socket api核心類有兩個

ServerSocket? 專門給服務器使用的socket對象Socket? ? ? ? ? ? ?給客戶端使用,也可以給服務器使用

?TCP是有連接的,建立連接的過程類似于"打電話",ServerSocket類里的accept相當于"接電話"("客戶端打電話,服務器接電話")

下面通過編寫TCP回顯服務器來舉例展示

TCP服務器

package net;

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;

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("服務器啟動!");

while(true){

Socket clientSocket = serverSocket.accept();//serverSocket用于幫助clientSocket建立連接

//使用多線程來實現(xiàn)多個客戶端連接服務器

Thread t= new Thread(()->{

try {

processConnection(clientSocket);

} catch (IOException e) {

e.printStackTrace();

}

});

t.start();

}

}

//針對一個連接,提供處理邏輯

private void processConnection(Socket clientSocket) throws IOException {

//先打印一下客戶端的信息

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

//獲取到socket中持有的流對象

try(InputStream inputStream = clientSocket.getInputStream();//TCP是全雙工的通信,一個socket對象既可以讀也可以寫

OutputStream outputStream = clientSocket.getOutputStream()){

//使用Scanner包裝一下inputStream ,就可以更方便的讀取這里的請求數(shù)據(jù)了

Scanner scanner = new Scanner(inputStream);

PrintWriter printWriter = new PrintWriter(outputStream);

while(true){

//1.讀取請求并解析

if(!scanner.hasNext()){

//如果scanner無法讀取出數(shù)據(jù),說明客戶端關閉了連接,導致服務器讀到了"末尾"

break;

}

String request = scanner.next();

//2.根據(jù)請求計算響應

String response = process(request);

//3.把響應寫回客戶端

//此處可以按照字節(jié)數(shù)組直接寫,也可以有另一種寫法

// outputStream.write(response.getBytes());

printWriter.println(response);

printWriter.flush();//刷新緩沖區(qū)

//4.打印日志

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

}

}catch(IOException e){

e.printStackTrace();

}finally{//只要是方法執(zhí)行完畢了,就會執(zhí)行close代碼

//連接失敗打印日志

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

clientSocket.close();//客戶端socket需要手動關閉

}

}

private String process(String request) {

return request;

}

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

TcpEchoServer server = new TcpEchoServer(9090);

server.start();

}

}

accept?

約定換行符?

TCP客戶端

package net;

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 {

//這里寫入ip和端口號之后意味著new好對象之后 和服務器的連接就建立完成了

//如果建立連接失敗了,直接就會拋出異常

socket = new Socket(serverIp,serverPort);

}

public void start(){

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

try(InputStream inputStream = socket.getInputStream();

OutputStream outputStream = socket.getOutputStream()){

Scanner scanner = new Scanner(inputStream);

Scanner scannerIn = new Scanner(System.in);

PrintWriter printWriter = new PrintWriter(outputStream);

while(true){

//1.從控制臺讀取數(shù)據(jù)

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

String request = scannerIn.next();

//2.把請求發(fā)送給服務器

printWriter.println(request);

printWriter.flush();//刷新緩沖區(qū)

//3.從服務器讀取響應

if(!scanner.hasNext()){

break;

}

String response = scanner.next();

//4.打印響應結果

System.out.println(response);

}

}catch(Exception e){

throw new RuntimeException(e);

}

}

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

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

client.start();

}

}

容易產生bug的點?

刷新緩沖區(qū)

PrintWrite這樣的類以及很多IO流中的類都是"自帶緩沖區(qū)的",引入緩沖區(qū)后,進行寫數(shù)據(jù)操作不會立即觸發(fā)IO,而是放到內存緩沖區(qū)中,等到緩沖區(qū)贊了一波,在進行統(tǒng)一發(fā)送;

所以當要發(fā)送的數(shù)據(jù)較少時 ,沒辦法攢夠數(shù)據(jù)發(fā)送,停在了緩沖區(qū),所以要引入PrintWrite類里的flush操作 來主動 "刷新緩沖區(qū)"

?clientSocket要自動關閉close

像ServerSocket,DatagramSocket他們的生命周期都是跟隨整個進程的(進程結束,會自動關閉),而服務器代碼中clientSocket是"連接級別"的數(shù)據(jù),隨著客戶端斷開連接,這個socket就不再使用了(即使是同一個客戶端,斷開之后,重新連接,也和舊的socket不是同一個),這樣的socket應該主動關閉以防止 文件資源泄漏.

多個客戶端連接同一個服務器

此處單線程下無法處理多個客戶端本質是服務器代碼里 雙重while循環(huán)導致的,進入了里層的while循環(huán)時,外層while無法執(zhí)行了?,所以這里要比雙層while循環(huán)改成一異while,分別執(zhí)行-->使用多線程解決.主線程用于accept來獲取多個連接 ,每個連接都可以啟動一個新的線程

雖然創(chuàng)建線程比創(chuàng)建進程 更輕量,但是也架不住短時間內 ,創(chuàng)建銷毀大量的線程?

使用線程池 可以解決短時間客戶端涌入,并且每個客戶端請求都很快 的問題

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

ExecutorService service = Executors.newCachedThreadPool();

while(true){

Socket clientSocket = serverSocket.accept();//serverSocket用于幫助clientSocket建立連接

//使用多線程來實現(xiàn)多個客戶端連接服務器

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

// try {

// processConnection(clientSocket);

// } catch (IOException e) {

// e.printStackTrace();

// }

// });

// t.start();

//使用線程池

service.submit(()->{

try {

processConnection(clientSocket);

} catch (IOException e) {

e.printStackTrace();

}

});

長短連接

長連接: 客戶端連上服務器后,一個連接中會多次發(fā)出請求,接收多個響應(當前回顯服務器就屬于這種模式)短連接: 客戶端連上服務器后,一個連接只能發(fā)一個請求,接受一個響應,然后就斷開連接了(可能會頻繁和服務器建立/斷開連接)

柚子快報邀請碼778899分享:【JavaEE初階】網(wǎng)絡編程

http://yzkb.51969.com/

文章來源

評論可見,查看隱藏內容

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

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

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

發(fā)布評論

您暫未設置收款碼

請在主題配置——文章設置里上傳

掃描二維碼手機訪問

文章目錄