【问题标题】:Java sockets: multiple client threads on same port on same machine?Java套接字:同一台机器上同一端口上的多个客户端线程?
【发布时间】:2023-03-03 07:35:28
【问题描述】:

我是 Java Socket 编程的新手,并试图了解以下代码是否正确。我的问题是:

我能否在每个线程上有多个客户端尝试连接到同一程序中的服务器实例,并期望服务器在客户端之间隔离的情况下读写数据?

public class Client extends Thread
{
    ...
    void run()
    {
        Socket socket = new Socket("localhost", 1234);
        doIO(socket);  
    }
}

public class Server extends Thread
{
    ...
    void run()
    {
        // serverSocket on "localhost", 1234
        Socket clientSock = serverSocket.accept();
        executor.execute(new ClientWorker(clientSock));
    }
}

现在我可以让不同线程上的多个客户端实例尝试连接到当前机器的同一个端口吗?

例如,

   Server s = new Server("localhost", 1234);
   s.start();
   Client[] c = new Client[10];
   for (int i = 0; i < c.length; ++i)
   {
        c.start();
   }

【问题讨论】:

    标签: java sockets port relationship


    【解决方案1】:

    是的,但是按照所写的每个线程执行只有一个客户端能够连接。

    你可以把你的服务器 run() 放在一个 while true 循环中,让多个客户端连接。 根据执行者的不同,它们将串行或并行执行。

       public class Server extends Thread  
       {  
           ...  
           void run()  
           {  
               while(true){
                  // serverSocket on "localhost", 1234  
                  Socket clientSock = serverSocket.accept();  
                  executor.execute(new ClientWorker(clientSock));  
               }
           }  
       } 
    

    【讨论】:

      【解决方案2】:

      只要你只有一个对象试图绑定端口进行监听,那么多个客户端连接就没有问题。

      【讨论】:

      • 谢谢大家,我认为端口是一个单一的物理实体(如电线),因为它只有一个编号。所以我的想法是它只能由一个客户端套接字使用,否则多个客户端套接字可以一次写入同一条线路。但从你的回答来看,我认为端口本身是由多个资源组成的(比如内存块),但套接字将绑定到可能由某个绑定键索引的这些块之一。
      • 端口只是一个数字。它不对应于任何物理。 connection 由元组 {protocol, source address, source port, target address, target port} 定义。客户端操作系统将负责确保每个传出连接的不同出站端口号。所以有多个入站连接到同一个目标主机/端口是没有问题的,即使它们都来自同一个客户端源主机。
      【解决方案3】:

      在此示例中,您的 Server 一次接受并处理一个客户端连接。您可以尝试连接任意数量的Clients,但一次只能处理一个。

      您的执行器逻辑是否是多线程的并不明显,因为您没有提供实现。如果执行程序委托给线程池或类似的东西,您需要确保您的 ClientWorker 是线程安全的,因为您将有多个实例并行执行。

      我当然假设您的 Client 也是线程安全的,因为您的问题仅与 Server 有关。

      【讨论】:

        【解决方案4】:

        是的,您的客户端是本地还是远程都没有关系。在您的示例中,重要的是 ClientWorker 是线程安全的,因为您的服务器将具有该类的多个实例(每个客户端连接一个实例)。

        【讨论】:

          【解决方案5】:

          所以。开始:

          您可以使用一个 serversocket 接受更多客户端,因为您在 run 方法中只接受一个。您只需再次致电accept()

          然后,您在 for 循环中:首先您必须每次创建一个新的 Client 对象。然后你可以打电话给c[i].start(); 而不是c.start()

          现在我可以拥有多个客户端吗 不同线程上的实例尝试 连接到同一端口 当前机器?

          是的,你可以。只需创建新线程并运行它们。这应该可以完美运行。

          期望服务器读写 客户端之间的数据隔离

          您可以使用您对基本 IO 技术的经验,例如使用 file-io:

          OutputStream os = socket.getOutputStream();
          PrintStream pw = new PrintStream(os, true); // Or PrintWriter, I don't know what the best one is.
          pw.println("Hello, other side of the connection!");
          

          对于阅读使用 BufferedReader。

          【讨论】:

            【解决方案6】:

            你可以在这些线路上尝试一些东西

            public class MultiThreadServer extends Application {
              // Text area for displaying contents
              private TextArea ta = new TextArea();
            
              // Number a client
              private int clientNo = 0;
            
              @Override // Override the start method in the Application class
              public void start(Stage primaryStage) {
                // Create a scene and place it in the stage
                Scene scene = new Scene(new ScrollPane(ta), 450, 200);
                primaryStage.setTitle("MultiThreadServer"); // Set the stage title
                primaryStage.setScene(scene); // Place the scene in the stage
                primaryStage.show(); // Display the stage
            
                new Thread( () -> {
                  try {
                    // Create a server socket
                    ServerSocket serverSocket = new ServerSocket(8000);
                    ta.appendText("MultiThreadServer started at " 
                      + new Date() + '\n');
            
                    while (true) {
                      // Listen for a new connection request
                      Socket socket = serverSocket.accept();
            
                      // Increment clientNo
                      clientNo++;
            
                      Platform.runLater( () -> {
                        // Display the client number
                        ta.appendText("Starting thread for client " + clientNo +
                          " at " + new Date() + '\n');
            
                        // Find the client's host name, and IP address
                        InetAddress inetAddress = socket.getInetAddress();
                        ta.appendText("Client " + clientNo + "'s host name is "
                          + inetAddress.getHostName() + "\n");
                        ta.appendText("Client " + clientNo + "'s IP Address is "
                          + inetAddress.getHostAddress() + "\n");
                      });
            
                      // Create and start a new thread for the connection
                      new Thread(new HandleAClient(socket)).start();
                    }
                  }
                  catch(IOException ex) {
                    System.err.println(ex);
                  }
                }).start();
              }
            
              // Define the thread class for handling new connection
              class HandleAClient implements Runnable {
                private Socket socket; // A connected socket
            
                /** Construct a thread */
                public HandleAClient(Socket socket) {
                  this.socket = socket;
                }
            
                /** Run a thread */
                public void run() {
                  try {
                    // Create data input and output streams
                    DataInputStream inputFromClient = new DataInputStream(
                      socket.getInputStream());
                    DataOutputStream outputToClient = new DataOutputStream(
                      socket.getOutputStream());
            
                    // Continuously serve the client
                    while (true) {
                      // Receive radius from the client
                      double radius = inputFromClient.readDouble();
            
                      // Compute area
                      double area = radius * radius * Math.PI;
            
                      // Send area back to the client
                      outputToClient.writeDouble(area);
            
                      Platform.runLater(() -> {
                        ta.appendText("radius received from client: " +
                          radius + '\n');
                        ta.appendText("Area found: " + area + '\n');
                      });
                    }
                  }
                  catch(IOException e) {
                    ex.printStackTrace();
                  }
                }
              }
            
              /**
               * The main method is only needed for the IDE with limited
               * JavaFX support. Not needed for running from the command line.
               */
              public static void main(String[] args) {
                launch(args);
              }
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-08-18
              • 1970-01-01
              • 1970-01-01
              • 2011-08-16
              相关资源
              最近更新 更多