日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

网络与IO知识扫盲(三):从系统调用的角度,剖析 Socket 的连接过程、BIO 的连接过程

發布時間:2024/2/28 windows 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 网络与IO知识扫盲(三):从系统调用的角度,剖析 Socket 的连接过程、BIO 的连接过程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Socket的連接過程、TCP的一些參數

前置知識

用到的命令
netstat -natp 查看網絡連接和占用的端口
tcpdump -nn -i eth0 port 9090 開監聽抓取數據包
lsof -p <進程號>查看某個進程已經打開的文件狀態

Socket

服務端代碼

package com.bjmashibing.system.io;import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket;public class SocketIOPropertites {//server socket listen property: 這些配置不是JVM層級的,是關聯到內核的TCP協議棧的一些選項參數。private static final int RECEIVE_BUFFER = 10;private static final int SO_TIMEOUT = 0; // 服務端的超時時間private static final boolean REUSE_ADDR = false;private static final int BACK_LOG = 2; // 多少個連接可以被積壓//client socket listen property on server endpoint:private static final boolean CLI_KEEPALIVE = false;private static final boolean CLI_OOB = false;private static final int CLI_REC_BUF = 20;private static final boolean CLI_REUSE_ADDR = false;private static final int CLI_SEND_BUF = 20;private static final boolean CLI_LINGER = true;private static final int CLI_LINGER_N = 0;private static final int CLI_TIMEOUT = 0; // 客戶端的超時時間private static final boolean CLI_NO_DELAY = false; /*StandardSocketOptions.TCP_NODELAYStandardSocketOptions.SO_KEEPALIVEStandardSocketOptions.SO_LINGERStandardSocketOptions.SO_RCVBUFStandardSocketOptions.SO_SNDBUFStandardSocketOptions.SO_REUSEADDR*/public static void main(String[] args) {ServerSocket server = null;try {server = new ServerSocket();server.bind(new InetSocketAddress(9090), BACK_LOG);server.setReceiveBufferSize(RECEIVE_BUFFER);server.setReuseAddress(REUSE_ADDR);server.setSoTimeout(SO_TIMEOUT);} catch (IOException e) {e.printStackTrace();}System.out.println("server up use 9090!");try {while (true) {// System.in.read(); //分水嶺:Socket client = server.accept(); //阻塞的,沒有 -1 一直卡著不動 accept(4,System.out.println("client port: " + client.getPort());client.setKeepAlive(CLI_KEEPALIVE);client.setOOBInline(CLI_OOB);client.setReceiveBufferSize(CLI_REC_BUF);client.setReuseAddress(CLI_REUSE_ADDR);client.setSendBufferSize(CLI_SEND_BUF);client.setSoLinger(CLI_LINGER, CLI_LINGER_N);client.setSoTimeout(CLI_TIMEOUT);client.setTcpNoDelay(CLI_NO_DELAY);//client.read //阻塞 沒有 -1 0new Thread(() -> {try {InputStream in = client.getInputStream();BufferedReader reader = new BufferedReader(new InputStreamReader(in));char[] data = new char[1024];while (true) {int num = reader.read(data);if (num > 0) {System.out.println("client read some data is :" + num + " val :" + new String(data, 0, num));} else if (num == 0) {System.out.println("client readed nothing!");continue;} else {System.out.println("client readed -1...");System.in.read();client.close();break;}}} catch (IOException e) {e.printStackTrace();}}).start();}} catch (IOException e) {e.printStackTrace();} finally {try {server.close();} catch (IOException e) {e.printStackTrace();}}} }

客戶端代碼

package com.bjmashibing.system.io;import java.io.*; import java.net.Socket;public class SocketClient {public static void main(String[] args) {try {Socket client = new Socket("192.168.150.11",9090);client.setSendBufferSize(20);client.setTcpNoDelay(true); // 如果數據量比較小,會不會積攢起來再發,默認是trueclient.setOOBInLine(true);OutputStream out = client.getOutputStream();InputStream in = System.in;BufferedReader reader = new BufferedReader(new InputStreamReader(in));while(true){String line = reader.readLine();if(line != null ){byte[] bb = line.getBytes();for (byte b : bb) {out.write(b);}}}} catch (IOException e) {e.printStackTrace();}} }

下面詳細跟蹤建立連接的過程

啟動服務端

開啟服務端后,出現了一個對于 9090 的 listen 狀態。
TCP 三次握手是走 listen 的,建立連接之后,后面走文件描述符,那就是另外一個環節了,我們后面再講。

使用jps得到服務端的進程id號:7932

使用lsof -p 7932查看7932端口的文件描述符的分配情況。

啟動客戶端
客戶端啟動,進入代碼的阻塞等待用戶輸入邏輯

在服務端抓到了三次握手的包

在服務端看到建立了連接,雖然連接還未被使用。

在客戶端進行用戶輸入之后(服務端也有的阻塞的邏輯,需要回車才能接收client的數據)

繼續查看服務端抓包監聽

查看服務端的連接狀態:雙方開辟了資源。即便你程序不要我,我也在內核里有資源用來接收或者等待一類的。

服務端輸入回車之后
接受到了客戶端發過來的數據

剛才的socket連接已經被分配給7932了

lsof 得到了新的文件描述符 6

總結一下

TCP:面向連接的,可靠的傳輸協議

Socket:是一個四元組。ip:port ip:port四元組的任何一個元的不同,都可以區分不同的連接。

面試題 1:服務端80端口接收客戶端連接之后,是否需要為客戶端的連接分配一個隨機端口號?
:不需要。

面試題 2:現在,有一個客戶端,有一個服務端,
客戶端的ip地址是AIP,程序使用端口號CPORT想要建立連接。
服務端的IP地址是XIP,端口號是XPORT。
現在假設某一個客戶端A開了很多連接占滿了自己的65535個端口號,那客戶端A是否還能與另一個服務端建立建立連接?
:可以,因為只要能保證四元組唯一即可

注:一臺服務器是可以與超過65535個客戶端保持長連接的,調優到超過百萬連接都沒問題,只要四元組唯一就可以了。客戶端來了之后,服務端是不需要單獨給它開辟一個端口號的。

下面這個圖可以說明,無論再多的連接,服務端始終是使用的同一個<ip:端口>

那么,我們常見的報錯“端口號被占用”是什么原因?

我們常見的報錯“端口號被占用”實際上是在啟動SocketSocket的時候,而不是Socket,兩者不是一個概念。如果兩個服務使用了相同的端口號,這時如果來了一個數據包,內核無法區分是哪一個服務在LISTEN,不知道要發給哪一個服務了,如下圖例子

每一個獨立的進程只要維護它自己的文件描述符唯一即可。

keepalive

三個不同層級的 keepalive

  • TCP協議中規定,如果雙方建立的連接(虛無的,并不是物理的連接),如果雙方很久都不說話,你能確定對方還活著嗎?不能,因為可能突然斷電。所以規定了這么一種機制,哪怕是周期性的消耗一些網絡資源,也要及時把無效的連接踢掉,節省內存。
  • HTTP級別
  • 負載均衡keepalived

網絡IO的變化 演進模型(BIO)

一句話概括BIO?

BIO就是,客戶端來一個連接,拋出一個線程,來一個連接,拋出一個線程…

幾個維度

同步、異步、阻塞、非阻塞

用到的命令:

strace -ff -o out /usr/java TestSocket
用來追蹤Java程序和內核進行了哪些交互(進行了哪些系統調用)

詳細追蹤 BIO 的連接過程

TestSocket.java


用JDK1.4跑起來

在服務端用jps找到進程的id號是8384

在服務端使用tail監控out.8384文件的輸出(8384是main線程的輸出,其他的out可能是一些垃圾回收線程等其他線程的輸出)
(這里注意一下一共有8個線程,待會兒建立連接之后再看)

可以看到JVM用到了內核系統調用的accept,main線程正在阻塞

在一個客戶端上建立一個連接

在服務端我們看到,剛才阻塞 accept(3, 的位置繼續執行。34178是客戶端連接進來的隨機端口號,192.1618.150.12是來自于客戶端的ip地址

clone是linux的一個系統調用。Java當中的一個線程,就是操作系統的一個子線程。下圖我們看到,(客戶端連接進來之后),服務端調用clone函數,開啟了一個線程號為8447的新線程。flags里面記錄的是子線程共享的文件系統、打開的文件等父線程的系統資源。
下面又開始阻塞的accept

查看用strace輸出的out文件,也可以證明8447這個新線程的存在。

在服務端可以看到,多了一個文件描述符5,表示的是從node01(服務端機器名稱)到node02(客戶端機器名稱)的已連通的狀態(socket四元組)

服務端 8447.out 正在recv阻塞接收

想學好Linux,去學習文檔中這些man幫助手冊,有時候比網絡上的博客文章更準確(也可以 man man 查看幫助文檔本身的幫助文檔)
使用man 2 socket,你會發現所謂socket系統調用,其實就是調用了一個有返回值(文件描述符)的函數(用于LISTEN)

稍稍總結一下

BIO 模型的整個連接過程

無論哪種IO模型,application想要和外界通信,都要進行上面所展示的一系列的(3步)系統調用,都是不可缺少的。
之后服務端進入阻塞狀態accept(3,等待客戶端的連接。此次阻塞被成功地連接之后,又進入一的新的阻塞,等待新的客戶端連接。
一旦連接成功之后,會為這個連接拋出去一個新的線程,新的線程中又進入一個阻塞狀態recv(5,等待接收消息。

總結

以上是生活随笔為你收集整理的网络与IO知识扫盲(三):从系统调用的角度,剖析 Socket 的连接过程、BIO 的连接过程的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。