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为什么要
- 下一篇: TCP的拥塞