Java 如果我同时打开两个客户端,客户端会挂起
Java client hangs if I open two clients at once
我正在尝试构建一个类似聊天的东西,我的想法是每当有人输入时,它就会被发送并打印在每个连接的客户端上。
如果我只打开服务器和一个客户端,一切都会顺利进行——每当我从客户端发送一些东西时,它都会到达服务器,然后返回并在客户端上打印。
问题是:当我打开第二个客户端时,只有随后发送的第一条消息被打印(在第二个客户端上),然后挂起。它没有错误消息或崩溃,它只是挂起,就好像卡在一个什么都不做的无限循环中一样。同时,第一个客户端按预期工作。
客户:
package main;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Client extends javax.swing.JFrame
{
private Socket client;
public Client() throws IOException
{
initComponents();
initClient();
Chat();
}
private void initClient()
{
try
{
client = new Socket("127.0.0.1",25565);
}
catch (IOException ex)
{
Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
}
}
private void Chat()
{
new Thread()
{
@Override
public void run()
{
while (true)
{
try
{
DataInputStream msg = new DataInputStream(client.getInputStream());
if (chat.getText().equals("")) chat.setText(msg.readUTF());
else chat.setText(chat.getText() + "\n" + msg.readUTF());
}
catch (IOException ex)
{
Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}.start();
}
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
jScrollPane2 = new javax.swing.JScrollPane();
msgArea= new javax.swing.JTextArea();
sendMsgButton = new javax.swing.JButton();
jScrollPane1 = new javax.swing.JScrollPane();
chat = new javax.swing.JTextArea();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
msgArea.setColumns(20);
msgArea.setRows(10);
jScrollPane2.setViewportView(msgArea);
sendMsgButton.setText("Send message");
sendMsgButton.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1));
sendMsgButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
sendMsgButtonActionPerformed(evt);
}
});
chat.setColumns(20);
chat.setRows(5);
chat.setToolTipText("");
chat.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createCompoundBorder(), "Conversas:", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.ABOVE_TOP));
chat.setCursor(new java.awt.Cursor(java.awt.Cursor.TEXT_CURSOR));
jScrollPane1.setViewportView(chat);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(jScrollPane1)
.addComponent(jScrollPane2))
.addContainerGap())
.addGroup(layout.createSequentialGroup()
.addGap(142, 142, 142)
.addComponent(sendMsgButton, javax.swing.GroupLayout.PREFERRED_SIZE, 121, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(147, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 312, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(18, 18, 18)
.addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 86, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(18, 18, 18)
.addComponent(sendMsgButton, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(18, 18, 18))
);
pack();
}// </editor-fold>//GEN-END:initComponents
private void sendMsgButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sendMsgButtonActionPerformed
try
{
PrintStream out = new PrintStream(client.getOutputStream());
out.println("User: " + msgArea.getText());
msgArea.setText("");
}
catch (IOException ex)
{
Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
}
}//GEN-LAST:event_sendMsgButtonActionPerformed
public static void main(String args[])
{
try
{
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels())
{
if ("Nimbus".equals(info.getName()))
{
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
}
catch (ClassNotFoundException ex)
{
java.util.logging.Logger.getLogger(Client.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
catch (InstantiationException ex)
{
java.util.logging.Logger.getLogger(Client.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
catch (IllegalAccessException ex)
{
java.util.logging.Logger.getLogger(Client.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
catch (javax.swing.UnsupportedLookAndFeelException ex)
{
java.util.logging.Logger.getLogger(Client.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
java.awt.EventQueue.invokeLater(() -> {
try {
new Client().setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
}
});
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTextArea msgArea;
private javax.swing.JButton sendMsgButton;
private javax.swing.JTextArea chat;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JScrollPane jScrollPane2;
// End of variables declaration//GEN-END:variables
}
服务器:
package main;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Server
{
private static ArrayList<ClientHandler> clients = new ArrayList<>();
private static ExecutorService pool = Executors.newFixedThreadPool(5);
public static void main(String args[])
{
int door = 25565;
try
{
ServerSocket server = new ServerSocket(porta, 0, InetAddress.getByName("127.0.0.1"));
System.out.println("Server running on " + server.getInetAddress().getHostAddress() + ":" + door);
while (true)
{
Socket client = server.accept();
System.out.println("Client conectado do IP " + client.getInetAddress().getHostAddress());
ClientHandler clientThread = new ClientHandler(client, clients);
clients.add(clientThread);
pool.execute(clientThread);
}
}
catch (IOException ex)
{
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
ClientHandler:(服务器使用)
package main;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ClientHandler implements Runnable
{
private Socket client;
private Scanner in ;
private DataOutputStream out;
private ArrayList<ClientHandler> clients;
public ClientHandler(Socket clientSocket, ArrayList<ClientHandler> clients) throws IOException
{
this.client = clientSocket;
this.clients = clients;
in = new Scanner(cliente.getInputStream());
out = new DataOutputStream(cliente.getOutputStream());
}
private void sendToAll(String message) throws IOException
{
for (ClientHandler Client : clients)
{
Client.out.writeUTF(message);
}
}
@Override
public void run()
{
while(in.hasNextLine())
{
try
{
String msg = in.nextLine();
System.out.println(msg);
sendToAll(msg);
}
catch (IOException ex)
{
Logger.getLogger(ClientHandler.class.getName()).log(Level.SEVERE, null, ex);
}
}
try
{
out.close();
in.close();
}
catch (IOException ex)
{
Logger.getLogger(ClientHandler.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
回答我自己的问题:问题在于使用 JTextArea
,它与长字符串或其他东西有一些奇怪的交互。
我把它换成了 JTextPane
,现在一切正常。
我正在尝试构建一个类似聊天的东西,我的想法是每当有人输入时,它就会被发送并打印在每个连接的客户端上。
如果我只打开服务器和一个客户端,一切都会顺利进行——每当我从客户端发送一些东西时,它都会到达服务器,然后返回并在客户端上打印。
问题是:当我打开第二个客户端时,只有随后发送的第一条消息被打印(在第二个客户端上),然后挂起。它没有错误消息或崩溃,它只是挂起,就好像卡在一个什么都不做的无限循环中一样。同时,第一个客户端按预期工作。
客户:
package main;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Client extends javax.swing.JFrame
{
private Socket client;
public Client() throws IOException
{
initComponents();
initClient();
Chat();
}
private void initClient()
{
try
{
client = new Socket("127.0.0.1",25565);
}
catch (IOException ex)
{
Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
}
}
private void Chat()
{
new Thread()
{
@Override
public void run()
{
while (true)
{
try
{
DataInputStream msg = new DataInputStream(client.getInputStream());
if (chat.getText().equals("")) chat.setText(msg.readUTF());
else chat.setText(chat.getText() + "\n" + msg.readUTF());
}
catch (IOException ex)
{
Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}.start();
}
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
jScrollPane2 = new javax.swing.JScrollPane();
msgArea= new javax.swing.JTextArea();
sendMsgButton = new javax.swing.JButton();
jScrollPane1 = new javax.swing.JScrollPane();
chat = new javax.swing.JTextArea();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
msgArea.setColumns(20);
msgArea.setRows(10);
jScrollPane2.setViewportView(msgArea);
sendMsgButton.setText("Send message");
sendMsgButton.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1));
sendMsgButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
sendMsgButtonActionPerformed(evt);
}
});
chat.setColumns(20);
chat.setRows(5);
chat.setToolTipText("");
chat.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createCompoundBorder(), "Conversas:", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.ABOVE_TOP));
chat.setCursor(new java.awt.Cursor(java.awt.Cursor.TEXT_CURSOR));
jScrollPane1.setViewportView(chat);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(jScrollPane1)
.addComponent(jScrollPane2))
.addContainerGap())
.addGroup(layout.createSequentialGroup()
.addGap(142, 142, 142)
.addComponent(sendMsgButton, javax.swing.GroupLayout.PREFERRED_SIZE, 121, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(147, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 312, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(18, 18, 18)
.addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 86, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(18, 18, 18)
.addComponent(sendMsgButton, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(18, 18, 18))
);
pack();
}// </editor-fold>//GEN-END:initComponents
private void sendMsgButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sendMsgButtonActionPerformed
try
{
PrintStream out = new PrintStream(client.getOutputStream());
out.println("User: " + msgArea.getText());
msgArea.setText("");
}
catch (IOException ex)
{
Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
}
}//GEN-LAST:event_sendMsgButtonActionPerformed
public static void main(String args[])
{
try
{
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels())
{
if ("Nimbus".equals(info.getName()))
{
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
}
catch (ClassNotFoundException ex)
{
java.util.logging.Logger.getLogger(Client.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
catch (InstantiationException ex)
{
java.util.logging.Logger.getLogger(Client.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
catch (IllegalAccessException ex)
{
java.util.logging.Logger.getLogger(Client.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
catch (javax.swing.UnsupportedLookAndFeelException ex)
{
java.util.logging.Logger.getLogger(Client.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
java.awt.EventQueue.invokeLater(() -> {
try {
new Client().setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
}
});
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTextArea msgArea;
private javax.swing.JButton sendMsgButton;
private javax.swing.JTextArea chat;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JScrollPane jScrollPane2;
// End of variables declaration//GEN-END:variables
}
服务器:
package main;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Server
{
private static ArrayList<ClientHandler> clients = new ArrayList<>();
private static ExecutorService pool = Executors.newFixedThreadPool(5);
public static void main(String args[])
{
int door = 25565;
try
{
ServerSocket server = new ServerSocket(porta, 0, InetAddress.getByName("127.0.0.1"));
System.out.println("Server running on " + server.getInetAddress().getHostAddress() + ":" + door);
while (true)
{
Socket client = server.accept();
System.out.println("Client conectado do IP " + client.getInetAddress().getHostAddress());
ClientHandler clientThread = new ClientHandler(client, clients);
clients.add(clientThread);
pool.execute(clientThread);
}
}
catch (IOException ex)
{
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
ClientHandler:(服务器使用)
package main;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ClientHandler implements Runnable
{
private Socket client;
private Scanner in ;
private DataOutputStream out;
private ArrayList<ClientHandler> clients;
public ClientHandler(Socket clientSocket, ArrayList<ClientHandler> clients) throws IOException
{
this.client = clientSocket;
this.clients = clients;
in = new Scanner(cliente.getInputStream());
out = new DataOutputStream(cliente.getOutputStream());
}
private void sendToAll(String message) throws IOException
{
for (ClientHandler Client : clients)
{
Client.out.writeUTF(message);
}
}
@Override
public void run()
{
while(in.hasNextLine())
{
try
{
String msg = in.nextLine();
System.out.println(msg);
sendToAll(msg);
}
catch (IOException ex)
{
Logger.getLogger(ClientHandler.class.getName()).log(Level.SEVERE, null, ex);
}
}
try
{
out.close();
in.close();
}
catch (IOException ex)
{
Logger.getLogger(ClientHandler.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
回答我自己的问题:问题在于使用 JTextArea
,它与长字符串或其他东西有一些奇怪的交互。
我把它换成了 JTextPane
,现在一切正常。