Java socket中关闭IO流后,发生啥事

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java socket中关闭IO流后,发生啥事相关的知识,希望对你有一定的参考价值。

参考技术A 为了方便讲解,我们把DataOutputstream dout = new DataOutputStream(new BufferedOutputStream(mySocket.getOutputStream()));中的dout做为Socket输出流的代言。同 样的,din是输入流的代言。
可以造成dout被关闭的操作有:
1、调用dout.close();或din.close();因为使用这种流关闭,会造成socket被关闭,所以输入输出流都将不可再用。
2、调用socket.close();
3、调用socket.shutdownOutputStream();单方面关闭dout,此时din还可正常使用。

以下,我将对socket中关闭输出流进行3个测试:
输出流关闭测试一:socket关闭吗?
输出流关闭测试二:该流是否可以重新开启?
输出流关闭测试三:输出缓冲区里的数据是丢弃,还是发送?
测试结果如下:
测试一:dout.close();会造成socket被关闭,但socket.shutdownOutputStream()不会。
测试二:不可以,会抛出异常!
测试三:丢弃

转载

如何在 gradle 启动测试中关闭关闭挂钩的输出?

【中文标题】如何在 gradle 启动测试中关闭关闭挂钩的输出?【英文标题】:How to turn off output from shutdown hooks in gradle boot tests? 【发布时间】:2020-05-18 00:08:07 【问题描述】:

你可以从https://start.spring.io/starter.zip?type=gradle-project&language=java&bootVersion=2.2.5.RELEASE&baseDir=demo&groupId=com.example&artifactId=demo&name=demo&description=Demo%20project%20for%20Spring%20Boot&packageName=com.example.demo&packaging=jar&javaVersion=1.8&dependencies=h2,data-jpa,web这个issue从start.spring.io生成一个项目

我有一个用 gradle 构建的多模块 springBoot 应用程序,有一堆 SpringBoot 集成测试。当我进行构建时,我最终会得到一些从 SpringBoot 关闭到控制台的输出,如下所示。如何关闭此输出?

± |master ↑1 1 S:3 U:10 ✗| → ./gradlew build

> Task :core:test
2020-02-01 11:20:33.529  INFO 24114 --- [extShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2020-02-01 11:20:33.531  INFO 24114 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2020-02-01 11:20:33.538  INFO 24114 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

> Task :email:test
2020-02-01 11:20:43.820  INFO 24150 --- [extShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2020-02-01 11:20:43.820  INFO 24150 --- [extShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2020-02-01 11:20:43.822  INFO 24150 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-2 - Shutdown initiated...
2020-02-01 11:20:43.822  INFO 24150 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2020-02-01 11:20:43.830  INFO 24150 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.
2020-02-01 11:20:43.830  INFO 24150 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-2 - Shutdown completed.

> Task :security:test
2020-02-01 11:20:54.941  INFO 24188 --- [extShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2020-02-01 11:20:54.944  INFO 24188 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2020-02-01 11:20:54.952  INFO 24188 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/6.1.1/userguide/command_line_interface.html#sec:command_line_warnings

BUILD SUCCESSFUL in 46s
57 actionable tasks: 54 executed, 3 up-to-date

作为参考,使用 gradle 从 start.spring.io 创建的应用程序不会在屏幕上产生任何输出

./gradlew build

BUILD SUCCESSFUL in 779ms
5 actionable tasks: 5 up-to-date

而是将输出放在build/reports/

在我的情况下,我没有对引导附带的日志记录配置进行任何更改。没有 logback.xml,或对日志级别的 application.yml 进行更改。我期待 gradle 正在捕获系统输出和系统错误并将它们发送到build/reports/,但一些输出似乎正在逃逸到系统输出。

【问题讨论】:

将这些包或类的日志级别调整为低于INFO(或完全删除)。 这些是INFO 级别的日志行。如您所见,它们源自关闭挂钩,并且它们最终出现在配置日志记录的位置。我想从理论上讲,由于日志记录配置的更改以及随后异步执行的钩子,消息可能最终出现在与预期不同的位置。所以它会将这些行默认到控制台,因为之前的配置已被卸载。也许吧。 你能添加你的测试类,以及你的主应用程序类吗?以及任何与数据源配置相关的 application.properties/yml? 可能是关闭挂钩发生在 Gradle 测试工作进程在其输出重定向被拆除后被关闭时。这可能值得一个 gradle/gradle 问题来开启讨论。 理想情况下,spring boot 在您的测试中是关闭的,而不必依赖 jvm 关闭挂钩,这将是一个 spring 问题。 【参考方案1】:

@eskatos 是对的。在关闭工作进程之前执行测试用例后,日志管理器被拆除。当工作进程关闭并重定向回控制台时,将执行所有关闭挂钩。

由于这些消息是由 spring boot 生成的,最好的地方是使用 logback 测试配置 xml 过滤关闭消息。

src/test/resources 中类似于 logback-test.xml 的东西

<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
            <evaluator> <!-- defaults to type ch.qos.logback.classic.boolex.JaninoEventEvaluator -->
                <expression>return event.getThreadName().contains("ShutdownHook");</expression>
            </evaluator>
            <OnMismatch>NEUTRAL</OnMismatch>
            <OnMatch>DENY</OnMatch>
        </filter>
        <encoder>
            <pattern>%dHH:mm:ss.SSS [%thread] %-5level %logger36 - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="INFO">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

build.gradle

testCompile 'org.codehaus.janino:janino'

【讨论】:

【参考方案2】:

可以使用TestLoggingContainer testLogging.showStandardStreams = false 禁用输出 决定在Test 任务中记录onOutput 的内容,以控制测试JVM 的stdout/stderr:

apply plugin: 'java'

test 

    // show standard out and standard error of the test JVM on the console
    // can be used to disable the console output:
    testLogging.showStandardStreams = true

    // listen to standard out and standard error of the test JVM
    // can be used to make the logging optional:
    onOutput  descriptor, event ->
        logger.lifecycle("Test: " + descriptor + " produced standard out/err: " + event.message)
    

这些流是来自 JVM 的 TestLogEvent STANDARD_OUTSTANDARD_ERROR。当可以确定包含extShutdownHookevent.message 时,可以跳过日志记录。

【讨论】:

见start.spring.io/…可以从start.spring.io生成一个项目到这个issue来重现这个issue 这可能不是问题,因为INFO 是 Spring 的默认日志记录级别;可以设置其他日志级别,例如。 logging.level.org.springframework=TRACE 作为环境变量。 我相信关闭挂钩日志是在测试任务之外生成的。您能否更新您的答案以显示如何过滤关闭挂钩消息?我认为过滤这些消息的最佳位置是它们在 Spring Boot 中的生成位置。【参考方案3】:

我可以隐藏 spring-data 特定的测试记录(基于this spring-starter) 通过将以下application.properties 添加到 src/test/resources:

logging.level.root=ERROR

logging.level.org.springframework 不会影响例如com.zaxxer.hikari 记录器,但你在这里有 flexible options。

root=ERROR 就像“大锤方法”)。

src/main/resources 也是可能的,但不仅在测试时有效,而且在应用程序运行时也有效) (application.properties 只是此属性的 many possible "locations" 之一...另请参阅:https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html)

有了这个,我得到一个“静默”的 gradle 输出,也在 clean build:

$ ./gradlew clean build

BUILD SUCCESSFUL in 10s
7 actionable tasks: 7 executed

【讨论】:

【参考方案4】:

Gradle 有一个安静模式。

./gradlew build -q

但您仍然需要有关测试的信息。你可以使用 jacoco 和 sonarqube。它对我有用 here 和 here。

【讨论】:

【参考方案5】:

Spring Boot 2.5 更新

此解决方案不再有效。 有关详细信息,请参阅此 GitHub 问题:https://github.com/spring-projects/spring-boot/issues/28108


在最近的版本中,Spring Boot 具有在退出之前关闭日志系统的属性。将以下行添加到您的配置中:

src/test/resources/application.yaml

logging:
  register-shutdown-hook: true

src/test/resources/application.properties

logging.register-shutdown-hook=true

属性定义和文档可以在 GitHub 上的 Spring Boot 存储库中找到:https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json


  "name": "logging.register-shutdown-hook",
  "type": "java.lang.Boolean",
  "description": "Register a shutdown hook for the logging system when it is initialized. Disabled automatically when deployed as a war file.",
  "sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
  "defaultValue": true

运行测试时似乎默认未注册关闭挂钩。

【讨论】:

以上是关于Java socket中关闭IO流后,发生啥事的主要内容,如果未能解决你的问题,请参考以下文章

如何在 socket.io 1.0 中关闭服务器?

如何在 gradle 启动测试中关闭关闭挂钩的输出?

为什么我们应该在JDBC中关闭连接?如果我们不这样做,会发生什么

我是不是需要在 Java 中关闭 IO 流? [复制]

java中关闭窗口的方法

如何在java中关闭当前工作目录(不是完整路径)[关闭]