Java TCP Socket编程:客户端和服务器在同一台计算机上通信良好,但无法通过局域网相互发送数据

Posted

技术标签:

【中文标题】Java TCP Socket编程:客户端和服务器在同一台计算机上通信良好,但无法通过局域网相互发送数据【英文标题】:Java TCP Socket Programming: Client and Server communicate well on the same computer, but fail to send data to each other over LAN 【发布时间】:2022-01-04 20:22:50 【问题描述】:

我正在尝试设置一个程序,使服务器可以与多个客户端进行通信。该程序是用 Java 编写的。我让这一切都在同一台机器上工作,所以我决定尝试局域网。我将程序转换为 JAR 文件,并尝试将笔记本电脑连接到我的 PC(两者都在同一个网络上)。连接成功,但不幸的是 只有 1 消息到达服务器。正如您在下面的代码中看到的,我通过DataOutputStream 发送了多条消息(意思是我写了多次)。一个定义数据类型(在下面的示例中,0 表示它是一个字符串),另一个发送实际的消息数据。我还以字节为单位打印数据包的大小,它始终与 DataOutputStream 实例的大小匹配。

DataOutputStream dOut = new DataOutputStream(clientSocket.getOutputStream());
String str = "Hello";
//Type
System.out.println("Type output size: 1");
dOut.writeByte(0);
//Message
System.out.println("Message output size: " + (str.getBytes(StandardCharsets.UTF_8).length + 2));
dOut.writeUTF(str);

System.out.println("Length of all: " + (dOut.size()));
dOut.flush();

所以现在当来自客户端的数据被发送时,我们需要在服务器上处理它,下面的代码就是这样做的。它从被调用的客户端Socket 中检索InputStream,并将其插入DataInputStream。这是因为它在 LAN 上变得很奇怪,因为流只包含 第一条消息

InputStream stream = client.getInputStream(); 
DataInputStream dIn = new DataInputStream(stream); 

while(dIn.available() > 0) 
   byte type = dIn.readByte();
    
  switch(type) 
                    
    case 0:
      System.out.println(dIn.readUTF());
      break;
    case 1:
      System.out.println(dIn.readInt());
      break;
    case 2:
      System.out.println(dIn.readByte());
      break;

    default:
      throw new IllegalStateException("Unexpected value: " + type);       
  

如果您在 IDE 中运行客户端,比如说连接到同一网络的笔记本电脑,然后您在连接到同一网络的 PC 上运行服务器它将工作。但是,如果程序在 JARS 中,则不是。

实际的堆栈跟踪如下:

java.net.SocketException:连接重置 在 java.base/sun.nio.ch.NiosocketImpl.implRead(NioSocketImpl.java:323) 在 java.base/sun.nio.ch.NioSocketImpl.read(NioSocketImpl.java:350) 在 java.base/sun.nio.ch.NioSocketImpl$1.read(NioSocketImpl.java:803) 在 java.base/java.net.Socket$SocketInputStream.read(Socket.java:966) 在 java.base/java.net.Socket$SocketInputStream.read(Socket.java:961) 在 java.base/java.io.DataInputStream.readInt(DataInputStream.java:393)

stacktrace 没有告诉我任何信息,但它指向switch case 中的case 0:。它无法读取字符串,因为DataInputStream 不包含任何数据(我猜?)。

我还要声明服务器是多线程的!我有一个线程在通过ServerSocket.accept() 接受套接字时添加它们,我使用第二个(主线程)读取从客户端发送的数据。

我选择了上面的代码,因为我认为问题出在其中,但是我是 Socket 编程的新手,我知道你们中的一些人希望看到代码的其他部分。当我被问到时,我会添加更多相关的代码。

我不知道为什么会这样,有人知道为什么吗?

我尝试了什么?

我已尝试等待数据包 - 但这只导致 服务器永远循环。等待数据包的意思是在DataInputStream 包含足够的字节之前不要继续前进。 我已禁用 Nagels AlgorithmsetTCPNoDelay(false)。 尝试发送不同的数据类型,但同样失败 我尝试将第一个数据包更改为String,这导致String 出现在DataInputStream 中。 我已尝试对使用的端口进行端口转发,并尝试在两台计算机上禁用防火墙。

更新 1

我一直在听取 cmets 的建议,这导致了一些发现:

关闭DataOutputStream 成功将所有包发送到客户端。 也可以建立自己的缓冲区并在服务器中对其进行解码。但是,在此之后仍然无法发送更多消息。 它作为 JAR 工作,因为 IntelliJ 非常好(Eclipse 在 IDE 中运行时抛出了同样的错误)

更新 2: 我认为这个post 是相关的。它指出当客户端“不优雅”地关闭它的套接字时会发送 SocketException。而且因为我的客户端关闭(因为它不在循环中)并且我没有正确关闭套接字 - 它会“不优雅地”关闭并且数据将会丢失。因此出现错误。

【问题讨论】:

您正在从输入流中读取数据可用时。因此,如果您在第二个 pkg 到达之前读取第一个 pkg,您的程序会认为没有更多数据。寻找另一种方式来终止数据流。 请不要发布到存储库的链接并期望人们下载和构建您的代码。请阅读How to create a Minimal, Reproducible Example,然后这样做。 我不知道这是违反规则的。 @AndrewHenle 如“我尝试过什么”中所述,我已尝试限制它。第二个包裹永远不会以某种方式到达。我用 available() 方法限制了它。 @BurakSerdar re 正如您在下面的代码中看到的,我通过 DataOutputStream 发送多条消息 -- 实际上,不,我看不到。我看到只发送了一条消息,它似乎为那条消息使用了一个新的 DataOutputStream。您是否每次都创建一个新流? 【参考方案1】:

问题现在已经解决了,而且解决方案也很合乎逻辑。我的客户端不是在循环中运行,而是发送数据并关闭程序。听起来不错,但我忘了正确关闭客户端的套接字。

第二个“数据包”从未到达的原因是我犯了这个小错误。数据包正在通过本地网络传输,但客户端套接字不正确在数据包到达服务器之前关闭了它的套接字,这就是我收到 SocketException 错误的原因。见this。

在我发送了所有我想发送的消息之后,我通过放置 socket.close() 解决了这个问题,其中 socket 是客户端的套接字。

【讨论】:

以上是关于Java TCP Socket编程:客户端和服务器在同一台计算机上通信良好,但无法通过局域网相互发送数据的主要内容,如果未能解决你的问题,请参考以下文章

java 网络编程 TCP协议 java 服务器和客户端 java socket编程

Java网络编程之TCP网络编程

JAVA TCP Socket编程 计算题

Java网络编程基础-- 基于TCP/IP的Socket编程

廖雪峰Java13网络编程-1Socket编程-3TCP多线程编程

Java Socket实现基于TCP和UDP多线程通信