我目前正在制作一个简单的聊天服务器程序,可以同时处理多个连接的客户端(创建一个基本的聊天室),我遇到了一个相当小,但令人讨厌的问题,我似乎无法破解。
我有一个JButton,当单击它时,检索IP地址、端口号和名称,这些都是用户在各自的JTextFields中输入的。然后将这些值提供给Socket构造函数,以尝试打开连接(名称被发送到其他地方,仅标识服务器上的用户)。我试着做的是有一个"连接……"消息,然后在文本区域中显示连接成功报告或错误(如果抛出错误)。然而,在实践中-如果我故意输入错误的IP(强制抛出错误)-在抛出错误之前,不会将文本添加到文本区域中;也就是说,它是一次全部追加的。
下面是点击按钮时运行的代码:
private void submitBtnClicked() {
String hostName = ipJtf.getText();
int port = Integer.parseInt(portJtf.getText());
String name = nameJtf.getText();
if (!name.trim().equals("")) {
// This is where I want the code to print "Connecting..."
jta.setText("");
jta.append("Connecting...n");
try {
socket = new Socket(hostName, port);
jta.append("Connected to server on: " + hostName + " : " + port + "n");
writer = new PrintWriter(socket.getOutputStream(), true);
writer.println(name);
new ReadThread(socket, this).start();
new WriteThread(socket, this).start();
}
catch (UnknownHostException ex) {
jta.append("Server not found: " + ex.getMessage() + "n");
}
catch (IOException e) {
jta.append("I/O Error: " + e.getMessage() + "n");
}
}
else {
jta.setText("");
jta.append("Please enter a user name before connecting.n");
}
}
所以当代码运行时,它当前会附加:
Connecting...
I/O Error: Connection refused: connect
同时显示,而不是打印&;Connecting…&;首先,然后是错误,当它不成功时。
我是编程方案的初学者,所以我确信这只是由于一些我不理解或在学习中还没有遇到的基本问题,但到目前为止我真的还没有找到解决方案。我怀疑这更多的是由于我不能弄清楚什么是正确的问题是问,因为我相信有人一定有这个问题之前,并在stackoverflow上给出了一个很好的答案;如果是这样的话,我将非常感谢你为我指出这一点。
事先感谢你对这件事的帮助。
p。S:我知道目前的代码有点乱——这主要是由于我在构建应用程序时测试了各种各样的东西。
issue复制的附加代码
Client.java
package ChatServerV2.Client;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.*;
public class Client extends JFrame {
private JTextField ipJtf = new JTextField(13);
private JTextField portJtf = new JTextField(4);
private JTextField nameJtf = new JTextField(15);
private JTextField messageJtf = new JTextField();
private JTextArea jta = new JTextArea();
private Socket socket;
private String hostName;
private int port;
private String userName;
// IO streams will go here
private PrintWriter writer;
public static void main(String[] args) {
new Client();
}
public Client() {
initUi();
}
// Builds the client UI and sets it as visible
private void initUi() {
JPanel p = new JPanel();
p.setLayout(new BorderLayout());
p.add(new JLabel("IP and Port: "), BorderLayout.WEST);
p.add(ipJtf, BorderLayout.CENTER);
p.add(portJtf, BorderLayout.EAST);
// Add jtf listeners
JPanel p2 = new JPanel();
p2.setLayout(new BorderLayout());
p2.add(new JLabel("Name: "), BorderLayout.WEST);
p2.add(nameJtf, BorderLayout.CENTER);
// Add jtf listener
JPanel p5 = new JPanel();
p5.setLayout(new BorderLayout());
JButton btn = new JButton("Submit");
btn.addActionListener((ActionEvent e) -> {
submitBtnClicked();
});
p5.add(btn, BorderLayout.EAST);
JPanel p4 = new JPanel();
p4.setLayout(new BorderLayout());
p4.add(p, BorderLayout.NORTH);
p4.add(p2, BorderLayout.SOUTH);
JPanel p6 = new JPanel(new BorderLayout());
// p6.setLayout(new BorderLayout());
p6.add(p4, BorderLayout.CENTER);
p6.add(p5, BorderLayout.EAST);
JPanel p3 = new JPanel();
p3.setLayout(new BorderLayout());
p3.add(new JLabel("Message: "), BorderLayout.WEST);
messageJtf.addActionListener((ActionEvent e) -> {
sendingAMessage();
});
p3.add(messageJtf, BorderLayout.CENTER);
// Add jtf listener
setLayout(new BorderLayout());
add(p6, BorderLayout.NORTH);
add(new JScrollPane(jta), BorderLayout.CENTER);
jta.setEditable(false);
add(p3, BorderLayout.SOUTH);
setTitle("Client");
setSize(500, 300);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
/** Writes a message locally to the client's window */
public void clientWrite(String message) {
jta.append(message + "n");
}
private void submitBtnClicked() {
String hostName = ipJtf.getText();
int port = Integer.parseInt(portJtf.getText());
String name = nameJtf.getText();
if (!name.trim().equals("")) {
jta.setText("");
jta.append(javax.swing.SwingUtilities.isEventDispatchThread() + "n");
jta.append("Connecting...n");
try {
socket = new Socket(hostName, port);
jta.append("Connected to server on: " + hostName + " : " + port + "n");
writer = new PrintWriter(socket.getOutputStream(), true);
writer.println(name);
new ReadThread(socket, this).start();
new WriteThread(socket, this).start();
}
catch (UnknownHostException ex) {
jta.append("Server not found: " + ex.getMessage() + "n");
}
catch (IOException e) {
jta.append("I/O Error: " + e.getMessage() + "n");
}
}
else {
jta.setText("");
jta.append("Please enter a user name before connecting.n");
}
}
private void sendingAMessage() {
}
}
ReadThread.java
package ChatServerV2.Client;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
public class ReadThread extends Thread {
private BufferedReader reader;
private Socket socket;
private Client client;
public ReadThread(Socket socket, Client client) {
this.socket = socket;
this.client = client;
try {
InputStream input = socket.getInputStream();
reader = new BufferedReader(new InputStreamReader(input));
}
catch (IOException e) {
client.clientWrite("Error getting input stream: " + e.getMessage());
}
}
public void run() {
while (true) {
try {
String response = reader.readLine();
client.clientWrite(response);
}
catch (IOException e) {
client.clientWrite("Error reading from server: " + e.getMessage());
break;
}
}
}
}
WriteThread.java
package ChatServerV2.Client;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
public class WriteThread extends Thread {
private PrintWriter writer;
private Socket socket;
private Client client;
public WriteThread(Socket socket, Client client) {
this.socket = socket;
this.client = client;
try {
OutputStream output = socket.getOutputStream();
writer = new PrintWriter(output);
}
catch (IOException e) {
client.clientWrite("Error getting output stream: " + e.getMessage());
}
}
public void run() {
}
}
如前所述,这是一个初学者正在进行的工作,所以如果它看起来很糟糕,那是因为它是。
为了更好地演示问题(和解决方案),将延迟添加到ReadThread
:
try {
TimeUnit.SECONDS.wait(1); //todo remove after testing
InputStream input = socket.getInputStream();
reader = new BufferedReader(new InputStreamReader(input));
}
catch (IOException | InterruptedException e) {
client.clientWrite("Error getting input stream: " + e.getMessage());
}
运行Client
,看到问题变得明显。要解决这个问题,您需要从EDT中删除任何长过程。
一个简单的方法是在不同的线程上运行长进程。
submitBtnClicked
修改如下:
private void submitBtnClicked() {
String hostName = ipJtf.getText();
int port = Integer.parseInt(portJtf.getText());
String name = nameJtf.getText();
if (!name.trim().equals("")) {
jta.setText("");
jta.append(javax.swing.SwingUtilities.isEventDispatchThread() + "n");
jta.append("Connecting...n");
new Thread(()->{//take long process off the EDT
try {
socket = new Socket(hostName, port);
//Modifying a Swing component must be done by the EDT
SwingUtilities.invokeLater(()-> jta.append("Connected to server on: " + hostName + " : " + port + "n"));
writer = new PrintWriter(socket.getOutputStream(), true);
writer.println(name);
new ReadThread(socket, this).start();
new WriteThread(socket, this).start();
}
catch (UnknownHostException ex) {
SwingUtilities.invokeLater(()-> jta.append("Server not found: " + ex.getMessage() + "n"));
}
catch (IOException e) {
SwingUtilities.invokeLater(()-> jta.append("I/O Error: " + e.getMessage() + "n"));
}
}).start();
}
else {
jta.setText("");
jta.append("Please enter a user name before connecting.n");
}
}
(在这里在线运行)