【问题标题】:Java server socket with NAT and Dynamic DNS带有 NAT 和动态 DNS 的 Java 服务器套接字
【发布时间】:2013-11-23 17:02:40
【问题描述】:

我正在尝试用 Java 为数据收集项目开发服务器端程序。

架构如下:

我有一个远程电子设备,它定期收集数据并将其发送回服务器。该设备配备了一张激活了 GPRS 连接的 SIM 卡,因此可以将其视为一台可以上网的电脑。在将数据发送回服务器之前,它必须知道服务器的 IP 地址。

由于通过我的 ISP 获得静态 IP 太贵,所以我决定尝试动态 DNS(DDNS)。我已经在我的 PC 上安装了 DDNS 客户端,并在我的路由器中配置了端口转发,如下所示:

External Port Start: 33333
External Port End: 33333
Protocol: TCP
Internal Port Start: 33333
Internal Port End: 33333
Server IP Address: 192.168.0.100 // My PC's IP Adress.

然后我尝试在我的 PC 上运行一个简单的服务器/客户端程序(是的,它们都在同一台 PC 上)。他们没有做任何复杂的事情,只是使用DataInputStreamDataOutputStream 类发送和接收一些字符串。

客户端可以连接到服务器,服务器可以接受连接(端口转发有效?!)。但是当他们试图读取或发送数据时,他们都会抛出一个 IOException。服务器说Connection reset,几秒钟后,客户端得到timeout

似乎当服务器接受新连接时,他们会选择一个在端口转发设置中未指定的任意新端口号,是不是有问题?

我想知道 IOException 的实际原因是什么,为什么他们无法读取或写入数据,以及解决此问题的任何建议?

非常感谢。

服务器代码:

import java.net.*;
import java.io.*;

public class GreetingServer extends Thread
{
   private ServerSocket serverSocket;

   public GreetingServer(int port) throws IOException
   {
      serverSocket = new ServerSocket(port);
   }

   public void run()
   {
      while(true)
      {
         try
         {
            System.out.println("Waiting for client on port " +
            serverSocket.getLocalPort() + "...");
            Socket server = serverSocket.accept();
            System.out.println("Just connected to "
                  + server.getRemoteSocketAddress());
            DataInputStream in =
                  new DataInputStream(server.getInputStream());
            System.out.println(in.readUTF());
            DataOutputStream out =
                 new DataOutputStream(server.getOutputStream());
            out.writeUTF("Thank you for connecting to "
              + server.getLocalSocketAddress() + "\nGoodbye!");
            server.close();
         }catch(SocketTimeoutException s)
         {
            System.out.println("Socket timed out!");
            break;
         }catch(IOException e)
         {
            e.printStackTrace();
            break;
         }
      }
   }
   public static void main(String [] args)
   {
      int port = 33333;
      try
      {
         Thread t = new GreetingServer(port);
         t.start();
      }catch(IOException e)
      {
         e.printStackTrace();
      }
   }
}

客户代码: xxxxxxx.xicp.net是我的免费域名,服务器监听33333端口。

import java.net.*;
import java.io.*;

public class GreetingClient
{
   public static void main(String [] args)
   {
      InetAddress ip;
      String serverName;
      int port = 33333;
      try
      {
         ip = InetAddress.getByName("xxxxxxx.xicp.net");
         serverName = ip.getHostAddress();
         System.out.println("Connecting to " + serverName
                             + " on port " + port);
         Socket client = new Socket(ip, port);
         System.out.println("Just connected to "
                      + client.getRemoteSocketAddress());
         OutputStream outToServer = client.getOutputStream();
         DataOutputStream out =
                       new DataOutputStream(outToServer);

         out.writeUTF("Hello from "
                      + client.getLocalSocketAddress());
         InputStream inFromServer = client.getInputStream();
         DataInputStream in =
                        new DataInputStream(inFromServer);
         System.out.println("Server says " + in.readUTF());
         client.close();
      }catch(IOException e)
      {
         e.printStackTrace();
      }
   }
}

【问题讨论】:

  • 我认为外部端口开始应该是0,外部端口结束应该是65535。通常客户端的操作系统会随机选择一个端口来建立传出连接。您告诉客户端连接到您路由器的公共 IP 的 33333 端口,但它选择用作源的端口可以是任何东西。
  • 客户端中的close()关闭连接。您是否尝试过在同一个 JVM 中同时运行服务器和客户端并添加一些 println 语句来查看事件发生的顺序?
  • @NormR 是的,这两个程序都在同一台 PC 上运行,因此在同一个 JVM 中。在关闭套接字之前,客户端必须向服务器发送一个 Hello 消息,然后等待服务器的回复。但是程序在消息传输之前就崩溃了。
  • @mti2935 试过1-65535,还是不行。服务器仍然重置连接,客户端总是超时。客户端打印Connecting to 174.128.255.227 on port 33333,IP 地址始终相同。然而服务器打印Just connected to /114.43.16.250:6000,IP地址和端口每次都改变。我就是不明白。
  • @vesintio 你说:在同一台PC上,所以在同一个JVM中,你是怎么做到的?当我这样做时,我有另一个 main() 方法,它从线程中调用 Server 和 Client 类的 main() 方法。然后,所有打印输出在运行我的特殊主程序的一个 JVM 中按时间顺序排列。

标签: java sockets tcp dns nat


【解决方案1】:

我知道这是不久前的事了,但一开始我的连接也有/遇到过同样的问题。

您的问题源于关闭 DataOutputStream/DataInputStream 流。基本上你想让它们保持打开状态,并在编写完成后调用刷新。

但是,如果您尝试读取的字节数多于已发送的字节数,这对于任一连接都可能存在问题。我建议在您的代码(也称为哨兵)中添加一个消息“标头”,以告诉接收方它将读取多少字节。

对于快速数据遍历也可以试试 BufferedOutputStream/BufferedInputStream,速度提升是惊人的。

【讨论】:

    【解决方案2】:

    我的猜测:我在路由器中配置了 NAT,因此所有传入连接都转发到 我电脑的 33333 端口。一旦客户端连接到服务器,它将选择另一个端口, 例如5000,与服务器通信。当服务器尝试向 客户端,它将它发送到端口 5000。在 NAT 中设置 5000 端口的 AS 不转发, 传输失败。我不确定,需要测试。

    好的,已确认。我尝试使用手机的 3G 连接在笔记本电脑上运行客户端,而台式 PC 上的服务器连接到路由器。端口 33333 转发给台式机。现在它完美地工作了。

    【讨论】:

      猜你喜欢
      • 2011-10-17
      • 1970-01-01
      • 1970-01-01
      • 2016-05-17
      • 2017-08-18
      • 2016-04-19
      • 2012-01-07
      • 1970-01-01
      • 2013-02-11
      相关资源
      最近更新 更多