java - 有多个客户端连接到 Java Server

标签 java multithreading sockets

我正在使用 Java 创建一个简单的套接字服务器。我能够一次连接一个客户端,我尝试实现线程来处理多个客户端。在我的服务器构造函数中,我创建了一个处理 ServerSocket 的线程,并且应该继续监听新客户端。连接套接字后,我尝试创建另一个线程来处理客户端套接字。但我仍然无法连接多个客户端。我尝试连接的第二个客户端将无法获取 IO 流。

public class Server extends JFrame {

private JTextField enterField;
private JTextArea displayArea;
private ObjectOutputStream output;
private ObjectInputStream input;

private ServerSocket server;
private Socket connection;
private int counter = 1;

public Server() {
    super("Server");
    enterField = new JTextField();
    enterField.setEditable(false);
    enterField.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent event) {
            sendData(event.getActionCommand());
            enterField.setText("");

        }
    });

    add(enterField, BorderLayout.NORTH);
    displayArea = new JTextArea();
    add(new JScrollPane(displayArea));
    setSize(300, 150);
    setLocation(500, 500);
    setVisible(true);

    new Thread(new Runnable() {
        public void run() {
            try {
                server = new ServerSocket(50499, 100);
                displayMessage("Listening on Port: "
                        + server.getLocalPort() + "\n");
                for (;;) {
                    Socket nextClient = server.accept();
                    displayMessage("Client Connected");
                    new ClientThread(nextClient).start();
                    nextClient = null;
                }
            } catch (IOException exception) {
                exception.printStackTrace();
            }
        }
    }).start();
}

private void closeConnection() {
    displayMessage("\nTerminating connection\n");
    setTextFieldEditable(false);
    try {
        output.close();
        input.close();
        connection.close();
    } catch (IOException ioException) {
        ioException.printStackTrace();
    }
}

private void displayMessage(final String string) {
    SwingUtilities.invokeLater(new Runnable() {

        @Override
        public void run() {
            displayArea.append(string);
        }
    });
}

private void setTextFieldEditable(final boolean editable) {
    SwingUtilities.invokeLater(new Runnable() {

        @Override
        public void run() {
            enterField.setEditable(editable);

        }

    });

}

private void sendData(String message) {
    try {
        output.writeObject("SERVER>>> " + message);
        output.flush();
        displayMessage("\nSERVER>>> " + message);
    } catch (IOException ioException) {
        displayArea.append("\nError Writing Object");
    }
}

private class ClientThread extends Thread {

    public ClientThread(Socket socket) throws IOException {
        try {
            connection = socket;
            output = new ObjectOutputStream(socket.getOutputStream());
            output.flush();
            input = new ObjectInputStream(socket.getInputStream());
            displayMessage("Got I/O Stream\n");
            displayMessage("Connection " + counter + " received from: "
                    + 
                            connection.getInetAddress().getHostName());
            counter++;
            String message = "Connection Sucessful";
            sendData(message);
            setTextFieldEditable(true);
            do {

                message = (String) input.readObject();
                displayMessage("\n" + message);
            } while (!message.endsWith(">>> TERMINATE"));

        } catch (ClassNotFoundException classNotFoundException) {
            displayMessage("\nUnknown object type recieved");

        } finally {
            closeConnection();
        }

    }
  }
}

最佳答案

您正在 ClientThread 构造函数内执行连接操作。因此,在您发送 TERMINATE 命令之前,new ClientThread(...) 永远不会返回。将逻辑放入 run() 方法中。

private class ClientThread extends Thread {
  private Socket socket;

  // The queue, thread-safe for good measure
  private Queue<String> queue = new ConcurrentLinkedQueue<String>();

  public ClientThread(Socket socket) throws IOException {
    this.socket = socket;
  }

  public void send(String message) {
    if (message != null) {
      this.sendQueue.add(message);
    }
  }

  public void run() {
    try {
      connection = socket;
      output = new ObjectOutputStream(socket.getOutputStream());
      output.flush();
      input = new ObjectInputStream(socket.getInputStream());
      displayMessage("Got I/O Stream\n");
      displayMessage("Connection " + counter + " received from: " 
          + connection.getInetAddress().getHostName());
      counter++;
      String message = "Connection Sucessful";
      sendData(message);
      setTextFieldEditable(true);
      do {
        // Purge the queue and send all messages.
        while ((String msg = queue.poll()) != null) {
          sendData(msg);
        }
        message = (String) input.readObject();
        displayMessage("\n" + message);
      } while (!message.endsWith(">>> TERMINATE"));
    } catch (ClassNotFoundException classNotFoundException) {
      displayMessage("\nUnknown object type recieved");
    } finally {
      closeConnection();
    }
  }
}

通常,您将从其他线程向连接发送消息:

ClientThread client = new ClientThread(newClient);
client.start();
client.send("Hi there");

就我个人而言,我会使用非阻塞 (NIO) 网络库,例如 NettyMina来实现这类东西。使用它们需要一些学习,但我认为这是值得的。非阻塞意味着您不会为每个连接指定一个单独的线程,而是当套接字中收到某些内容时您会收到通知。

关于java - 有多个客户端连接到 Java Server,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16751078/

相关文章:

java - 如何使用php解码图像文件

java - 探查器查看方法所花费的时间

java - 我应该在虚拟机中使用多少个线程?

java - 将反斜杠替换为斜杠时出现异常

multithreading - Jackrabbit 和并发修改

c++ - 如何并行化将矩阵的行随机复制到内存中的另一个矩阵的过程?

c# - 为什么用空委托(delegate)调用 ThreadStart?

sockets - 支持 haproxy 中的 TCP_KEEPCNT 功能吗?

c++ - 如何通过Linux C++实现一台主机控制多台从机

delphi - 如何使用 IdTCPClient 等待来自服务器的字符串?