为啥当未接受的套接字发送消息时,套接字不会抛出错误?
Posted
技术标签:
【中文标题】为啥当未接受的套接字发送消息时,套接字不会抛出错误?【英文标题】:Why Socket does not throw error when unaccepted socket send a message?为什么当未接受的套接字发送消息时,套接字不会抛出错误? 【发布时间】:2021-10-29 08:56:01 【问题描述】:我的目标是创建一个接受 1 个连接的服务器客户端,然后服务器以代码 0 退出。
我的预期结果:第二个客户端应该无法发送消息,因为ServerSocket
要么已关闭,要么它不接受带有serverSocket.accept()
的第二个客户端。
我的实际结果:第二个客户端可以发送消息而不会抛出java.net.ConnectException: Connection refused: connect
的错误
我的实际结果EXTRA
:如果你用不同的方法创建第二个客户端,它会失败。
java/framework/Client.java
package framework;
import java.io.IOException;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
public class Client
private final Socket socket;
public Client() throws IOException
socket = new Socket("localhost", 80);
public void send(String message) throws IOException
socket.getOutputStream().write(message.getBytes(StandardCharsets.US_ASCII));
java/framework/Server.java
package framework;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
public class Server implements Runnable
private static final Logger logger = LoggerFactory.getLogger(Server.class);
private final ServerSocket serverSocket;
public Server() throws IOException
serverSocket = new ServerSocket(80);
@Override
public void run()
try (serverSocket)
logger.debug("Sever socket started at port " + serverSocket.getLocalPort());
final Socket socket = serverSocket.accept();
logger.debug("Socket accepted: " + socket.getInetAddress());
final BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
final String line = reader.readLine();
logger.debug("Input received: " + line);
catch (IOException e)
logger.warn("something went wrong" + e);
logger.debug("Server Socket closed.");
test/java/ServerClientTest.java
import framework.Client;
import framework.Server;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
public class ServerClientTest
private static final Logger logger = LoggerFactory.getLogger(ServerClientTest.class);
@BeforeAll
static void if_server_can_start_then_success()
logger.debug("beforeAll");
try
Server server = new Server();
Thread thread = new Thread(server);
thread.start();
catch (IOException ignored)
Assertions.fail("failed to start the server");
@Test
void if_1st_client_can_send_message_to_server_then_success()
try
Client client = new Client();
client.send("Hello from 1st client\r\n");
Client client1 = new Client();
client1.send("Hello from 2nd client\r\n");
catch (IOException e)
Assertions.fail("1st client unable to send message to server. " + e);
编辑:
-
在Client类
send
方法中做logger.debug("Socket: " + socket.getInputStream())
,神奇的是,socket会抛出java.net.SocketException. Connection reset by peer: socket write error
。同时,调用 socket.isConnected()
将返回 true。是的,我知道,打电话给getInputStream
并记录它很奇怪,但它使测试按预期工作。
原代码日志:
23:37:02.418 [main] DEBUG ServerClientTest - beforeAll
23:37:02.442 [Thread-0] DEBUG framework.Server - Sever socket started at port 80
23:37:02.491 [Thread-0] DEBUG framework.Server - Socket accepted: /127.0.0.1
23:37:02.492 [Thread-0] DEBUG framework.Server - Input received: Hello from 1st client
23:37:02.493 [Thread-0] DEBUG framework.Server - Server Socket closed.
Process finished with exit code 0
在客户端类send
方法中调用socket.getInputStream()
的日志。
23:39:48.098 [main] DEBUG ServerClientTest - beforeAll
23:39:48.153 [Thread-0] DEBUG framework.Server - Sever socket started at port 80
23:39:48.188 [Thread-0] DEBUG framework.Server - Socket accepted: /127.0.0.1
23:39:48.188 [main] DEBUG framework.Client - Socket: java.net.SocketInputStream@6bf0219d
23:39:48.190 [Thread-0] DEBUG framework.Server - Input received: Hello from 1st client
23:39:48.191 [main] DEBUG framework.Client - Socket: java.net.SocketInputStream@36d585c
23:39:48.191 [Thread-0] DEBUG framework.Server - Server Socket closed.
org.opentest4j.AssertionFailedError: clients failed to send message.
at org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:43)
at org.junit.jupiter.api.Assertions.fail(Assertions.java:129)
at ServerClientTest.if_clients_can_send_message_to_server_then_success(ServerClientTest.java:35)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:688)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:210)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:206)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:131)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:65)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)
Caused by: java.net.SocketException: Connection reset by peer: socket write error
at java.base/java.net.SocketOutputStream.socketWrite0(Native Method)
at java.base/java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:110)
at java.base/java.net.SocketOutputStream.write(SocketOutputStream.java:138)
at framework.Client.send(Client.java:20)
at ServerClientTest.if_clients_can_send_message_to_server_then_success(ServerClientTest.java:33)
... 65 more
Process finished with exit code -1
【问题讨论】:
为什么不 1) 为 backlog 参数指定 1,即serverSocket = new ServerSocket(80, 1);
,以及 2) 在第一次 accept()
调用后关闭服务器套接字?
@PresidentJamesK.Polk 1)我试图将积压工作设置为 1,正如你所说,它什么都不做 2)try-with-resources
将立即关闭它,检查 isClosed()
也返回 true。 , closing
接受后的服务器会杀死我想要理解的测试。 @user16320675 带来了一个很好的讨论点。
@user16320675 你能澄清一下standard options
是什么意思吗?
@user16320675 你的意思是backlog
来自ServerSocket
吗?我尝试使用 backlog 并将其设置为 1。我无法从 Socket
找到 backlog。
docs 确实暗示backlog
参数是一个建议而不是硬限制。我很惊讶服务器套接字关闭后第二个连接成功。我仍然会在接受后立即关闭服务器套接字。
【参考方案1】:
问题在于您的客户端创建。创建两个客户端之间没有延迟,这会导致排除连接错误。添加延迟总是会导致连接错误。
static void if_1st_client_can_send_message_to_server_then_success()
try
Client client = new Client();
client.send("Hello from 1st client\r\n");
Thread.sleep(1000);
Client client1 = new Client();
client1.send("Hello from 2nd client\r\n");
catch (Exception e)
Assertions.fail("1st client unable to send message to server. " + e);
【讨论】:
以上是关于为啥当未接受的套接字发送消息时,套接字不会抛出错误?的主要内容,如果未能解决你的问题,请参考以下文章
ZeroMQ 套接字 Recv() 抛出“上下文已终止”异常 - 为啥以及如何恢复?
为啥当客户端断开连接时这个简单的 websocket 代码会抛出?
socket 错误之:OSError: [WinError 10057] 由于套接字没有连接并且(当使用一个 sendto 调用发送数据报套接字时)没有提供地址,发送或接收数据的请求没有被接受。