UDP網(wǎng)絡(luò)通信的收包過程:
使用DatagramSocket()創(chuàng)建一個數(shù)據(jù)包套接字,綁定到指定的端口唆姐。
使用DatagramPacket(byte[]buf,int length)創(chuàng)建字節(jié)數(shù)組來接收數(shù)據(jù)包.
使用DatagramPacket類的receive()方法接收UDP。
TCP網(wǎng)絡(luò)程序
=====================================================================
ServerSocket類
Java.net包中的ServerSocket類用于表示服務(wù)器套接字廓八,其主要功能是監(jiān)聽客戶端的請求奉芦,然后將客戶端的請求連接存入隊(duì)列中,默認(rèn)請求隊(duì)列大小是50剧蹂。
構(gòu)造方法主要有以下幾種形式:
ServerSocket():創(chuàng)建非綁定服務(wù)器套接字声功。
ServerSocket(int port):創(chuàng)建綁定到特定端口的服務(wù)器套接字。
ServerSocket(int port,int backlog):利用指定的backlog創(chuàng)建服務(wù)器套接字并將其綁定到指定的本地端口號宠叼。
ServerSocket(int port,int backlog,InetAdress bindAddress):使用指定的端口亡哄、監(jiān)聽backlog和要綁定到本地IP地址創(chuàng)建服務(wù)器丈莺,適用于計算機(jī)有多個網(wǎng)卡、多個IP的情景。
實(shí)例:
//服務(wù)端程序
import java.io.*;
import java.net.*;
public class MyTcpServer {
private BufferedReader reader;
private PrintWriter writer;
private ServerSocket server;
private Socket socket;
void getserver(){
try{
server = new ServerSocket(8866);
System.out.println(“服務(wù)器套接字已經(jīng)創(chuàng)建成功”);
while(true){
System.out.println(“等待客戶機(jī)的連接”);
socket = server.accept(); //阻塞的
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
writer = new PrintWriter(socket.getOutputStream(),true);
getClientMessage(); }
}catch(Exception e){
e.printStackTrace();
}
}
private void getClientMessage(){
try{
while(true){
System.out.println(“客戶端信息接收:”+ reader.readLine());
writer.println(“歡迎您連接服務(wù)端”);
}
}catch(Exception e){
e.printStackTrace();
}
try{
if(reader != null){
reader.close();
}
if(writer != null){
writer.close();
}
if(socket != null){
socket.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
public class Main {
public static void main(String[] args) {
MyTcpServer tcpserv = new MyTcpServer();
tcpserv.getserver();
}
}
//客戶端程序
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class MyTcpClient {
private PrintWriter writer;
private BufferedReader reader;
Socket socket;
public void connect(){
System.out.println(“嘗試連接”);
try{
socket = new Socket(“127.0.0.1”,8866);
writer = new PrintWriter(socket.getOutputStream(),true);
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println(“連接成功”);
writer.println(“你好发钝,來自客戶端的連接”);
getserverMessage();
}catch(Exception e){
e.printStackTrace();
}
}
private void getserverMessage(){
try{
while(true){
System.out.println(":"+ reader.readLine());
}
}catch(Exception e){
e.printStackTrace();
}
try{
if(reader != null){
reader.close();
}
if(writer != null){
writer.close();
}
if(socket != null){
socket.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
public class Main {
public static void main(String[] args) {
MyTcpClient tcpclient;
tcpclient = new MyTcpClient();
tcpclient.connect();
}
}
服務(wù)端控制臺:
客戶端控制臺:
總結(jié):網(wǎng)絡(luò)編程是程序?qū)崿F(xiàn)網(wǎng)絡(luò)通信的基石,在互聯(lián)網(wǎng)高度繁榮的當(dāng)下版保,幾乎沒有什么應(yīng)用不需要網(wǎng)絡(luò)支持博烂。網(wǎng)絡(luò)通信基于TCP/IP,基于此乐埠,網(wǎng)絡(luò)編程可以選擇使用TCP傳輸或UDP傳輸抗斤,它們兩個是比較底層的通信協(xié)議,TCP提供可靠的連接丈咐,UDP則不提供可靠的連接瑞眼,在實(shí)際應(yīng)用中大多數(shù)選用TCP,而UDP主要用于音視頻棵逊、NTP對時這種對數(shù)據(jù)可靠性要求不是那么高的場合伤疙。
IO模型
==================================================================
對于一次IO訪問(以read舉例),數(shù)據(jù)會先被拷貝到操作系統(tǒng)內(nèi)核的緩沖區(qū)page cache中,然后才會從操作系統(tǒng)內(nèi)核的緩沖區(qū)拷貝到應(yīng)用程序的地址空間徒像。所以說黍特,當(dāng)一個read操作發(fā)生時,它會經(jīng)歷兩個階段:
等待數(shù)據(jù)準(zhǔn)備
將數(shù)據(jù)從內(nèi)核拷貝到進(jìn)程中
IO模型的分類有下:
阻塞 I/O(blocking IO)
非阻塞 I/O(nonblocking IO)
I/O 多路復(fù)用( IO multiplexing)
異步 I/O(asynchronous IO)
BIO 阻塞 I/O
缺點(diǎn):一個請求一個線程锯蛀,浪費(fèi)線程灭衷,且上下文切換開銷大;
當(dāng)用戶進(jìn)程調(diào)用了recvfrom這個系統(tǒng)調(diào)用旁涤,kernel就開始了IO的第一個階段:準(zhǔn)備數(shù)據(jù)(對于網(wǎng)絡(luò)IO來說翔曲,很多時候數(shù)據(jù)在一開始還沒有到達(dá)。比如劈愚,還沒有收到一個完整的UDP包瞳遍。這個時候kernel就要等待足夠的數(shù)據(jù)到來)。這個過程需要等待菌羽,也就是說數(shù)據(jù)被拷貝到操作系統(tǒng)內(nèi)核的緩沖區(qū)中是需要一個過程的掠械。而在用戶進(jìn)程這邊,整個進(jìn)程會被阻塞(當(dāng)然注祖,是進(jìn)程自己選擇的阻塞)猾蒂。當(dāng)kernel一直等到數(shù)據(jù)準(zhǔn)備好了,它就會將數(shù)據(jù)從kernel中拷貝到用戶內(nèi)存氓轰,然后kernel返回結(jié)果婚夫,用戶進(jìn)程才解除block的狀態(tài),重新運(yùn)行起來署鸡。
NIO 非阻塞 I/O
當(dāng)用戶進(jìn)程發(fā)出read操作時案糙,如果kernel中的數(shù)據(jù)還沒有準(zhǔn)備好,那么它并不會block用戶進(jìn)程靴庆,而是立刻返回一個error 时捌。從用戶進(jìn)程角度講 ,它發(fā)起一個read操作后炉抒,并不需要等待奢讨,而是馬上就得到了一個結(jié)果。用戶進(jìn)程判斷結(jié)果是一個error時焰薄,它就知道數(shù)據(jù)還沒有準(zhǔn)備好拿诸,于是它可以再次發(fā)送read操作。一旦kernel中的數(shù)據(jù)準(zhǔn)備好了塞茅,并且又再次收到了用戶進(jìn)程的system call亩码,那么它馬上就將數(shù)據(jù)拷貝到了用戶內(nèi)存,然后返回野瘦。
nonblocking IO的特點(diǎn)是用戶進(jìn)程需要不斷的主動詢問kernel數(shù)據(jù)好了沒有描沟。
I/O 多路復(fù)用
《一線大廠Java面試題解析+后端開發(fā)學(xué)習(xí)筆記+最新架構(gòu)講解視頻+實(shí)戰(zhàn)項(xiàng)目源碼講義》
【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整內(nèi)容開源分享
IO multiplexing就是我們說的select飒泻,poll,epoll吏廉,有些地方也稱這種IO方式為event driven IO泞遗。select/epoll的好處就在于單個process就可以同時處理多個網(wǎng)絡(luò)連接的IO。它的基本原理就是select席覆,poll史辙,epoll這個function會不斷的輪詢所負(fù)責(zé)的所有socket,當(dāng)某個socket有數(shù)據(jù)到達(dá)了娜睛,就通知用戶進(jìn)程髓霞。
機(jī)制:一個線程以阻塞的方式監(jiān)聽客戶端請求卦睹;另一個線程采用NIO的形式select已經(jīng)接收到數(shù)據(jù)的channel信道畦戒,處理請求;Linux的多種IO模型以及select结序,poll障斋,epoll模型 - 處理更多的連接。