繁体   English   中英

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

我正在尝试设置一个服务器可以与多个客户端通信的程序。 程序编写在 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();

所以现在当从客户端发送数据时,我们需要在服务器上处理它,下面的代码就是这样做的。 它从名为 client 的Socket中检索InputStream并将其插入DataInputStream 这是因为 stream 仅包含第一条消息,因此在 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: Connection reset at java.base/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:323) at java.base/sun.nio.ch.NioSocketImpl.read(NioSocketImpl.java:350) at java.base/sun.nio.ch.NioSocketImpl$1.read(NioSocketImpl.java:803) at java.base/java.net.Socket$SocketInputStream.read(Socket.java:966) at java.base/java. net.Socket$SocketInputStream.read(Socket.java:961) at java.base/java.io.DataInputStream.readInt(DataInputStream.java:393)

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

我还想state说Server是多线程的! 我有一个线程在通过ServerSocket.accept()接受 sockets 时添加它们,并且我使用第二个(主线程)读取从客户端发送的数据。

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

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

我试过什么?

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

更新 1

我一直在从导致一些发现的评论中获得建议:

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

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM