linux网络IO
Socket
服務端代碼
package io.unittest;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; import java.net.SocketException;/*** Author: ljf* CreatedAt: 2021/3/28 上午10:05*/ public class SocketIOProperties {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; // 連接的等待隊列滿了之后還能拍幾個,linux一切皆文件嘛,意味著等著不分配文件描述符的有兩個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;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 (SocketException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}System.out.println("server up use 9090 !");jibeitry {while (true) {System.in.read(); // 這里阻塞住,看沒有客戶端的情況下,server端怎么處理連接Socket client = server.accept();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);jibei// 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();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 ..."); // 客戶端斷開,就是-1System.in.read();break;}}} catch (IOException e) {e.printStackTrace();}}).start();}} catch (IOException e) {e.printStackTrace();} finally {try {server.close();} catch (IOException e) {e.printStackTrace();}}} }客戶端代碼
package io.unittest;import java.io.*; import java.net.Socket;/*** Author: ljf* CreatedAt: 2021/3/28 上午11:07*/ public class SocketClient {public static void main(String[] args) throws IOException {Socket client = new Socket("192.168.150.11", 9080);client.setSendBufferSize(20);client.setTcpNoDelay(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);}}}} }開兩臺虛擬機,總共開4個控制臺界面,一臺(1號)服務器運行界面,一臺(2號)客戶端運行界面,一臺監控(3號) netstat -natp 查看端口和連接情況,一臺 (4號)tcpdump -nn -i ens33 port 9090 監控tcp連接情況,操作步驟:
1.4號運行 tcpdump -nn -i ens33 port 9090 ,啥也沒有
2.1號運行 java io.unittest.SocketIOProperties,服務開啟,因為有System.in.read()阻塞住了,所以沒有取接收客戶端
3.3號 netstat -natp ,發現服務端在監控9090端口 ,這時候4號的抓包沒有任何反應,說明沒有建立連接
lsof -p 2960,發現內核已經分配資源,文件描述符6,在監聽狀態
4.2號客戶端 java io.unittest.SocketClient,沒有報錯,說明客戶端啟動ok
6.看抓包的4號機器,有了客戶端與服務端的3次tcp握手
7.3號機 netstat -natp ,發現已經建立了連接,但是沒有進程產生,因為服務端還在阻塞呢
8.2號客戶端輸入 1111,回車,
3號機看 netstat -natp,發現服務端已經接收到4個字節了,雖然沒有應用程序接受,說明在網絡連接層已經完成通信了
且抓包也抓到了正常通信的網絡狀態,即 tcp 是面向連接的,走完三次握手,內核就已經分配資源(緩沖區等),雖然應用程序沒有接收,但是tcp連接照樣開啟了,占用系統資源了。
9.1號機回車,跳過阻塞,發現接收到了客戶端的數據
這時候 netstat -natp ,發現連接已經分配給2960進程了
且文件描述符也得到了,即7u
總結
1.tcp是面向連接的,可靠的傳輸協議,三次握手之后,雙方內核就開啟資源,即代表就有了連接。
2.socket是4元組,內核級的,服務端IP+服務端端口+客戶端IP+客戶端端口,即這4個元只要有一元不相同,即能辨識出來是唯一的資源,就能建立連接。意味著服務器雖然默認最多開啟65535個端口,但是一個端口可以listen多個socket,只要內存夠分配文件描述符,理論上連接可以是端口號數量的幾倍。
參數 BACK_LOG
表示最多有幾個不接收的連接在隊列里等待,操作步驟:
1.服務端開啟,阻塞住;
2.客戶端瘋狂連接
設置back_log是2,服務端接收到第4個連接的時候狀態就是 SYN_RECV了,可以理解為服務端不處理這個連接了。這個參數可以用來控制負載均衡的服務器訪問量,netty尤為有用。
參數 SO_TIMEOUT
1.server在listen狀態的超時時長,超過這個時間沒有連接進來,就拋異常,打斷這次等待,重新等待新的連接。
2.連進來的客戶端,即server.accept()過來的客戶端,這個參數表示連接進來了在時長內沒有收到數據包,就斷開這個連接,防止客戶端一直在消耗資源。比如你連接百度,但是就一直不發請求頭,過一會再發就連接失效了。
參數NODELAY
tcp連接有發包的優化策略,如果包太小了就攢在一起大點了再發,可以通過setNoDelay(true),讓每個包無論大小都發。
參數keepalive
如果建立了連接了,雙方都不通話,那么誰都不知道對方是否還存活,設置keepalive是true,客戶端會網服務端發確認包,服務端也給客戶端發確認包,保持連接存活。
總結
- 上一篇: 指令级别解释对象创建过程和DCL为什么要
- 下一篇: linux内核管理pagecache的一