楼主想写一个聊天工具,迷你版的,用java网络编程中运输层的tcp协议,为了接受信息,然后启动一个新线程,但是这个县城启动之后,就会是整个程序都死掉。你先启动服务端,然后再启动客户端。当客户端向服务端发送几条消息之后,客户度啊就会死掉。目前还不会使用eclipse的debug功能,大神帮忙debug,顺便可否说下debug过程?如何下断点或者如何发现bug等!
我用System.out.println();发现bug出现在,但是不知道具体是什么bug!!
服务端:
import java.net.*;
import java.io.*;
public class Server {
private ServerSocket ss = null;
private DataInputStream dis = null;
private void start() {
try{
ss = new ServerSocket(8888);
while(true) {
Socket s = ss.accept();
System.out.println("A Client is coming...");
new SocketThread(s).start();
}
} catch(IOException e) {
System.out.println("The port is used");
System.exit(0);
}
}
public static void main(String[] args) {
new Server().start();
}
//define a new thread, to hold a socket
private class SocketThread extends Thread {
private Socket s = null;
private DataInputStream dis = null;
private DataOutputStream dos = null;
public SocketThread(Socket s) {
this.s = s;
try {
dis = new DataInputStream(s.getInputStream());
dos = new DataOutputStream(s.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
boolean bConnected = true;
String str = null;
try {
while(bConnected) {
str = dis.readUTF().trim();
if("886#".equals(str)) {
System.out.println("A client is closing...");
break;
}
System.out.println(str);
send(str.trim());
}
} catch (IOException e) {
e.printStackTrace();
}
if(s != null) {
try {
s.close();
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void send(String m) {
try {
dos.writeUTF(m);
dos.flush();
System.out.println("Send to client:" + m);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
客户端:
import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.io.*;
public class ChatClient extends JFrame {
private JTextArea jtaRecoder = new JTextArea();
private JTextArea jtaMessage = new JTextArea();
private JButton jbt = new JButton("Send");
private Socket socket = null;
private DataOutputStream dos = null;
private DataInputStream dis = null;
public ChatClient() {
// 聊天记录框和消息发送面板的属性
jtaRecoder.setBackground(Color.YELLOW);
jtaMessage.setBackground(Color.ORANGE);
jtaRecoder.setBorder(new TitledBorder("chatRord:"));
jtaMessage.setBorder(new TitledBorder("message:"));
jtaMessage.setFont(new Font("", Font.BOLD, 15));
jtaRecoder.setFont(new Font("", Font.BOLD, 14));
jtaMessage.setWrapStyleWord(true);
jtaMessage.setLineWrap(true);
jtaRecoder.setWrapStyleWord(true);
jtaRecoder.setLineWrap(true);
jtaRecoder.setEditable(false);
// 布局设计
this.setLayout(new GridLayout(2, 1));
this.add(new JScrollPane(jtaRecoder));
JPanel message = new JPanel();
message.setLayout(new BorderLayout());
message.add(new JScrollPane(jtaMessage), BorderLayout.CENTER);
message.add(jbt, BorderLayout.EAST);
this.add(message);
this.setTitle("ChatClient");
this.setResizable(false);
this.setSize(500, 300);
this.setVisible(true);
this.setLocationRelativeTo(null);
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent arg0) {
disconnect();
System.exit(0);
}
});
connect();
jbt.addActionListener(new ButtonActionListener());
new RecevThread().start();
}
public void connect() {
try {
socket = new Socket("127.0.0.1", 8888);
dos = new DataOutputStream(socket.getOutputStream());
dis = new DataInputStream(socket.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
public void disconnect() {
try {
dos.writeUTF("886#");
dos.flush();
} catch (IOException e) {
e.printStackTrace();
}
if(dos != null) {
try {
dos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private class ButtonActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent arg0) {
String str = jtaMessage.getText().trim();
jtaRecoder.append("Me: " + str + "\n");
jtaMessage.setText("");
try {
dos.writeUTF(str);
dos.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new ChatClient();
}
private class RecevThread extends Thread {
@Override
public void run() {
while(true) {
try {
String str = dis.readUTF().trim();
if("886#".equals(str)) {
ChatClient.this.disconnect();
break;
}
jtaRecoder.setText(jtaRecoder.getText() + "You: " +
str + "\n");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
------解决思路----------------------
可以看看 log4j 之类的日志工具
------解决思路----------------------
先跑了一下你的程序,发现客户端发送三次数据就崩了。。。。等我仔细研究研究。
------解决思路----------------------
ClientChat的线程并不是死掉,而是阻塞了。客户端线程阻塞的原因你已经定位到了,是因为Client端代码的130行调用了SetTest方法,导致并发更新UI界面,RecevThread被AWT-EventQueue(Swing的UI事件分发线程)阻塞了。调试多线程问题时,可以使用JDK提供的JConsole查看线程信息,截图如下:
Swing的事件分发进程
RecevThread线程
另外,Swing编程时不要在另一个线程中直接更改UI控件,这样会导致多线程问题,可以用SwingUtilities.invokeLater方法将修改动作放到AWT-EventQueue线程中执行,示例如下:
private class RecevThread extends Thread {
@Override
public void run() {
while(true) {
try {
final String str = dis.readUTF().trim();
if("886#".equals(str)) {
ChatClient.this.disconnect();
break;
}
// jtaRecoder.setText(jtaRecoder.getText() + "You: " +
// str + "\n");
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
jtaRecoder.setText(jtaRecoder.getText() + "You: " + str + "\n");
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
附送官方教程一篇:http://docs.oracle.com/javase/tutorial/uiswing/concurrency/
------解决思路----------------------
我说调式半天没看出哪有毛病来,原来是我未接触到的层面,受教了。
------解决思路----------------------
是死锁了
出现这种情况是因为RecevThread 在 setText的时候,将AbstractDocument 里面的一个当前写入线程变量设置为了RecevThread,然后经过一系列调用以后,又需要ThreeLock 锁来继续下面的工作。
而这时候,TreeLock 的拥有者是AWT Thread. AWT 在执行到后面某一步的时候,需要确保 AbstractDocument 不在写入状态,而RecevThread 已经将AbstractDocument 设置为写入状态,因此AWT Thread 在循环休眠等待 写入状态清除,因此发生了死锁。