IO-阻塞IO

IO-阻塞IO,第1张

目录

一、原理

二、BIO

1、三次握手

 2、测试用例

2.1、服务端测试代码

2.2、客户端代码

2.3、开启服务端

2.4、启动客户端


一、原理

IO即Input/Output 输入输出,诺依曼结构将计算机分成5个基础部分:运算器、控制器、存储器、输入、输出。

应用程序发起一次IO *** 作,包含两个阶段:

IO调用:应用程序进程向 *** 作系统内核发起调用。

IO执行: *** 作系统内核完成IO *** 作。

IO又分成BIO(阻塞IO)、NIO(非阻塞IO)。下面我们先梳理下BIO。

二、BIO

BIO是在Jdk1.4及以前有的,它的主要问题就是存在阻塞的情况。当然鉴于真正的IO执行是在OS中,所以阻塞不阻塞还是 *** 作系统内核提供的API决定的。

BIO:blocking IO主要问题就是阻塞,取决于系统内核提供的API。

客户端三次握手完成后,服务端的accept系统调用,会clone一个专用线程。然后再去接收其他客户端的连接。

1、三次握手

TCP协议建立IO连接都会经历三次握手阶段。

客户端发送一个带SYN标志的TCP报文到服务器。

服务器回应回应客户端,带上ACK标志和SYN标志,响应客户端。

客户端再次回应服务端一个ACK标志的TCP报文,建立连接。

 2、测试用例 2.1、服务端测试代码
public class BIOServerTest {

    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;  //如果有很多连接过来的时候,线程不够了,等待不处理的个数。
    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; // TCP下的优化算法,在发送数据比较小的情况下,可以利用缓冲的概念

    public static void main(String[] args){
        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket();
            serverSocket.bind(new InetSocketAddress(9090), BACK_LOG);
            serverSocket.setReuseAddress(REUSE_ADDR);
            serverSocket.setSoTimeout(SO_TIMEOUT);
        }
        catch (IOException e){
            e.printStackTrace();
        }

        System.out.println("server up use 9090");
        while (true){
            try{
                System.in.read();

                Socket socket = serverSocket.accept();
                System.out.println("client port:"+ socket.getPort());

                socket.setKeepAlive(CLI_KEEPALIVE);
                socket.setOOBInline(CLI_OOB);
                socket.setReceiveBufferSize(CLI_REC_BUF);
                socket.setReuseAddress(CLI_REUSE_ADDR);
                socket.setSendBufferSize(CLI_SEND_BUF);
                socket.setSoLinger(CLI_LINGER, CLI_LINGER_N);
                socket.setSoTimeout(CLI_TIMEOUT);
                socket.setTcpNoDelay(CLI_NO_DELAY);

            }
            catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}
2.2、客户端代码
public class BIOClientTest {
    public static void main(String[] args){
        try
        {
            Socket client = new Socket("192.168.150.11", 9090);
            client.setSendBufferSize(20); //发送的缓冲区
            client.setTcpNoDelay(true); //优化方式发送数据
            client.setOOBInline(false); //首字节快速发送,关闭
            OutputStream out = client.getOutputStream();

            InputStream inputStream = System.in;
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));

            while (true){
                String line = reader.readLine();
                if(line != null){
                    byte[] bb = line.getBytes();
                    for (byte b : bb){
                        out.write(b);
                    }
                }
            }
        }
        catch (Exception e)
        {

        }
    }
}
  • setSendBufferSize

        发送数据缓冲区的大小,例如:客户端发送22个字节数据,那么服务端会先写入前20个,再写入后面两个。

  • setTcpNoDelay         

        优化方式是否开启。

        false是开启,有delay的现象,并且delay的数据可能大于setSendBufferSize设置的数值。

  • setOOBInline

        true是开启,客户端每次传数据,首个字节不缓存,快速先传递给服务端。

        false是不开启,不首发第一个字节给服务端。

2.3、开启服务端

服务端开启数据包抓取

[root@ywxtdb ~]# tcpdump -nn -i ens33 port 9090

启动服务端程序

[root@ywxtdb IO]# javac BIOServerTest.java 
[root@ywxtdb IO]# java BIOServerTest
server up use 9090

查看内核里面的socket建立的过程,此时9090端口已经开始监听

[root@ywxtdb ~]# netstat -natp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp6       0      0 ::1:631                 :::*                    LISTEN      1542/cupsd          
tcp6       0      0 ::1:25                  :::*                    LISTEN      1844/master         
tcp6       0      0 ::1:6010                :::*                    LISTEN      2442/sshd: root@pts 
tcp6       0      0 ::1:6011                :::*                    LISTEN      2442/sshd: root@pts 
tcp6       0      0 ::1:6012                :::*                    LISTEN      2442/sshd: root@pts 
tcp6       0      0 :::9090                 :::*                    LISTEN      5013/java

得到服务端jsp进程号

[root@ywxtdb ~]# jps
5013 BIOServerTest
5434 Jps

查看服务端进程,他里面的文件描述符是否已经有了,以及分配情况

[root@ywxtdb ~]# lsof -p 5013

2.4、启动客户端

登录客户端服务器,执行如下命令

[root@localhost IO]# javac BIOClientTest.java 
[root@localhost IO]# java BIOClientTest
  • 查看服务器端抓包情况。
[root@ywxtdb ~]# tcpdump -nn -i ens33 port 9090

如下Flags三次握手

        第一次130连接上128服务端

        第二次128响应了130客户端

        第三次130回复了128服务端

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
11:08:42.643253 IP 192.168.1.130.40504 > 192.168.1.128.9090: Flags [S], seq 1639954253, win 29200, options [mss 1460,sackOK,TS val 2438787 ecr 0,nop,wscale 7], length 0

11:08:42.643375 IP 192.168.1.128.9090 > 192.168.1.130.40504: Flags [S.], seq 1333909161, ack 1639954254, win 28960, options [mss 1460,sackOK,TS val 2879177 ecr 2438787,nop,wscale 7], length 0

11:08:42.643666 IP 192.168.1.130.40504 > 192.168.1.128.9090: Flags [.], ack 1, win 229, options [nop,nop,TS val 2438788 ecr 2879177], length 0

        握手数据分析

        mss:数据内容大小,数据包大小 - ip-端口(40k) =1460。ens33网卡定义的MTU(数据包大小)为1500。

        win:定义一个窗口大小,协商服务端、客户端网卡,开一个窗口。大小根据实际值而定。

        注意:如果客户端请求的数据量超过1500和窗口的阈值,内核缓存区会开始丢失后面的数据。

  • 查看服务端网络情况

        此时多了一条客户端的socket,但还没被java进程接收。

[root@ywxtdb ~]# netstat -natp

  • 执行客户端输入

        客户端输入1111

[root@localhost IO]# java BIOClientTest
1111

        查看服务端抓包情况,可以看见通讯都能正常交互。

        查看网络情况,虽然这个socket没有被接受,但内核中已经记录了有4个字节的请求。

         查看java程序的文件描述符是否已经有了,可以看见。目前还没分配,还是listen状态。

        可以知道,程序不是映射socket的,而是在等文件描述符。

  •   执行服务端输入
[root@ywxtdb IO]# java BIOServerTest
server up use 9090

client port:40502

        查看网路情况,发现这个socket已经被接受。

         查看java进程情况,7u文件描述符已经取到。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/924708.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-16
下一篇 2022-05-16

发表评论

登录后才能评论

评论列表(0条)

保存