• 网络编程-关闭连接(2)-Java的NIO在关闭socket时,究竟用了哪个系统调用函数?


    背景

    在上一讲网络编程-关闭连接-C/C++相关系统调用中,提到过,目前项目使用Netty框架来实现的网络编程,查看netty源码可以得知,netty最终是调用了java Nio的close接口做的关闭操作,那么想研究清楚这个close操作究竟做了什么,可以从两个方向入手,这两个方向也是从下至上的。

    1. 搞清楚如果使用C/C++编程,应该调用哪个系统调用函数?函数内部做了什么,涉及到什么TCP/IP的协议参数,这些已经在上一讲中研究明白了。
    2. 搞清楚java nio在调用close方法时,究竟使用了哪个系统调用?

    这一讲,主要研究解决第二个问题,搞清楚java nio在调用close方法时,究竟使用了哪个系统调用?

    实验

    在实验中,我们会使用linux下的strace工具,来跟踪系统调用。

    使用我们的测试代码,用netty框架搭建了一个客户端,其中通过web接口传入ip端口进行连接,连接成功以后,在channelActive回调方法中先调用一个写文件方法,然后关闭channel。

    测试代码上传到github上:

    部分关键代码如图:

    @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            //写入本地文件测试字符,然后关闭channel
            FileWriter fileWriter = new FileWriter("/root/test.txt");
            fileWriter.write("test test hold on");
            fileWriter.flush();
            fileWriter.close();
    
            //调用同步方法关闭
            ChannelFuture sync = ctx.channel().close().sync();
            if(sync.isSuccess()){
                System.out.println("关闭成功!");
            }else{
                System.out.println("关闭失败!");
            }
    }
    

    在linux虚拟机上,使用下面指令运行该程序

    [root@localhost Downloads]# java -jar pro-test-demo-0.0.1-SNAPSHOT.jar 
    

    然后使用ps -ef | grep "java"得到该进程的pid。

    [root@localhost Downloads]# ps -ef | grep "java"
    root       3029   2940 31 13:48 pts/0    00:00:13 java -jar pro-test-demo-0.0.1-SNAPSHOT.jar
    root       3136   3101  0 13:49 pts/2    00:00:00 grep --color=auto java
    

    使用strace跟踪系统调用,命令如下:

    strace -tt -T -f -v -e trace=all -p 3029 -o ouput.log
    

    java程序运行起来如下,我们使用postman传递了ip和端口,看到日志输出为连接成功,并且输出关闭成功!

    [root@localhost Downloads]# java -jar pro-test-demo-0.0.1-SNAPSHOT.jar 
    
      .   ____          _            __ _ _
     /\ / ___'_ __ _ _(_)_ __  __ _    
    ( ( )\___ | '_ | '_| | '_ / _` |    
     \/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::        (v2.1.4.RELEASE)
    
    2020-07-14 13:48:52.225  INFO 3029 --- [           main] c.n.icomp.protestdemo.jfdj.NettyClient   : Starting NettyClient v0.0.1-SNAPSHOT on localhost.localdomain with PID 3029 (/root/Downloads/pro-test-demo-0.0.1-SNAPSHOT.jar started by root in /root/Downloads)
    2020-07-14 13:48:52.229  INFO 3029 --- [           main] c.n.icomp.protestdemo.jfdj.NettyClient   : No active profile set, falling back to default profiles: default
    2020-07-14 13:48:53.608  INFO 3029 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!
    2020-07-14 13:48:53.618  INFO 3029 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data repositories in DEFAULT mode.
    2020-07-14 13:48:53.694  INFO 3029 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 55ms. Found 0 repository interfaces.
    2020-07-14 13:48:54.374  INFO 3029 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 9876 (http)
    2020-07-14 13:48:54.410  INFO 3029 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
    2020-07-14 13:48:54.410  INFO 3029 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.17]
    2020-07-14 13:48:54.504  INFO 3029 --- [           main] o.a.c.c.C.[.[localhost].[/protest]       : Initializing Spring embedded WebApplicationContext
    2020-07-14 13:48:54.504  INFO 3029 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 2187 ms
    2020-07-14 13:48:55.144  INFO 3029 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
    2020-07-14 13:48:55.434  INFO 3029 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 9876 (http) with context path '/protest'
    2020-07-14 13:48:55.436  INFO 3029 --- [           main] c.n.icomp.protestdemo.jfdj.NettyClient   : Started NettyClient in 3.968 seconds (JVM running for 4.79)
    2020-07-14 13:50:10.685  INFO 3029 --- [nio-9876-exec-1] o.a.c.c.C.[.[localhost].[/protest]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
    2020-07-14 13:50:10.685  INFO 3029 --- [nio-9876-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
    2020-07-14 13:50:10.691  INFO 3029 --- [nio-9876-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 6 ms
    接收到参数:{"ip":"10.43.11.240","port":"20108"}
    连接成功!
    关闭成功!
    

    最后打开output.log查看系统调用
    为了便于我搜索,我提前在代码中加入了关键的一行

    fileWriter.write("test test hold on");
    

    所以搜索的时候,可以先搜索test test hold on,找到对应写入的系统调用,附近应该就是close相关的系统调用了。

    3494  13:53:04.722628 write(103, "test test hold on", 17) = 17 <0.000060>
    3494  13:53:04.722755 close(103)        = 0 <0.000251>
    3494  13:53:04.723141 getsockopt(102, SOL_SOCKET, SO_LINGER, {l_onoff=0, l_linger=0}, [8]) = 0 <0.000080>
    
    3499  13:53:17.206962 close(102)        = 0 <0.000012>
    

    可以发现,fd为102的socket就是我们需要检测的socket,在调用close真正关闭之前,还进行了获取SO_LINGER的系统调用getsockopt,并且l_onoff=0, l_linger=0,会关闭读写两方向连接,并且在后台继续发送发送缓冲区中的内容,后面调用close返回0,代表成功。

    总结

    JAVA nio的close操作,使用的是close系统调用。关于close系统调用,可以参考上一讲中的内容。网络编程-关闭连接-C/C++相关系统调用

  • 相关阅读:
    linux下ipc
    c各种打印集合
    linux环境下,利用gsoap生成webservice客户端进行应用程序开发的小结 (轉)
    Linux C函数之错误处理函数
    Linux操作系统文件系统基础知识详解
    常用字符串操作函数
    ASP.NET偷懒大法二
    ASP.NET偷懒大法三 (利用Attribute特性简化多查询条件拼接sql语句的麻烦)
    用JavaScript获取Asp.net服务器端控件CheckBoxList的选中值数组
    ASP.NET偷懒大法四(动态生成表单对象)
  • 原文地址:https://www.cnblogs.com/ging/p/13467859.html
Copyright © 2020-2023  润新知