手把手教你写一个Java在线聊天系统
标签: Java Toy_Programm Thread Swing/AWT
东软2016暑假实训内容,之前写过,这次想按自己思路独立写一遍。
1. Let’s Chat V0.1
- 创建一个窗口,起一个客户端ChatClient.java
- extends Frame
设置客户端标题,可见性,位置,大小,窗口关闭形式
private void launchFrame() { this.setTitle("Let's Chat"); this.setBounds(100, 100, 400, 300); //这样写,可以在关闭窗口时,做资源回收的当作 this.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e) { System.exit(0); } }); this.setVisible(true); }
2. Let’s Chat V0.2
- 添加文本域
- TextField
- TextArea
- 设置TextField在BorderLayout.SOUTH
- 设置TextArea在BorderLayout.NORTH
当我尝试使用JTextField和JTextArea达不到TextField和TextArea的效果,所以我使用后者。
this.pack();//消除组件之间的间隙
3. Let’s Chat V0.3
- 往TextField里输入,输出到TextArea里
这里我们想当按下ENTER时达到这种效果,采用事件与监听器
为TextField添加一个监听器
常规写法,或者我们可以单独写一个类,如果方法里,代码太长,就应该写一个内部类
tf.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ta.setText(tf.getText().trim()); tf.setText(""); } });
这里的接口ActionListener只有一个方法,就是函数式接口,所以这里我们可以采用Lambda编程
tf.addActionListener((e)->{
ta.setText(tf.getText().trim());
tf.setText("");
});
4. Let’s Chat V0.4
- 起一个服务器端ChatServer.java,基于TCP的
- 服务器端
注意捕捉异常
private ServerSocket ss = null;
private Socket s = null;
ss = new ServerSocket(9600);
s = ss.accept();
- 服务器重复打开,占用同一端口
- IOException
- 开启服务器,开启客户端,两者能连上
- 客户端
注意捕捉异常
private Socket s = null;
s = new Socket("127.0.0.1",9600);
- 服务器未打开,先打开客户端
- IOException
5. Let’s Chat V0.5
- 客户端输入一句话,服务端能收到
服务器端
private DataInputStream dis = null; dis = new DataInputStream(s.getInputStream()); String str = dis.readUTF();
客户端
private DataOutputStream dos = null; dos = new DataOutputStream(s.getOutputStream()); dos.writeUTF(str); dos.flush();
6. Let’s Chat V0.6
客户端不断输入一句话,服务器能收到
使用while(true) {}while(true) { dis = new DataInputStream(s.getInputStream()); String str = dis.readUTF(); System.out.println(str); }
我们注意到关闭客户端会报一些异常,这是因为我们没有在关掉窗口时对资源进行关闭回收
- 关闭客户端,服务器端SocketException
关闭服务器,一旦尝试输入,服务器端SocketException
这里我们对其作简单处理,即在出现异常的地方,捕捉它,输出一个提示信息,然后退出程序
catch (SocketException se) {
System.out.println(“A Server quit…”);
System.exit(0);
}
7. Let’s Chat V0.7- 多个客户端能够连上服务器端
采用两个死循环,外层不断侦听并接受到此套接字的连接,内层不断读取字符。实际运行我们发现无论如何都只能连接一个客户端。这是应为readUTF()是一个阻塞性函数,所以main()会一直阻塞在这里。
while(true) {
s = ss.accept();
while(true) {
dis = new DataInputStream(s.getInputStream());
String str = dis.readUTF();
System.out.println(str);
}
}
思路:把内层循环里读取字符的动作封装成一个线程(这意味着程序要做很大的更改)。
8. Let’s Chat V0.8
- 服务器端能够把信息发送给每一个客户端,并且客户端能接收
客户端能接收
起一个线程,不断地读取字符public void run() { try { while(conned) { dis = new DataInputStream(s.getInputStream()); String str = dis.readUTF(); ta.setText(ta.getText()+str+"\n"); } } catch (IOException e) { e.printStackTrace(); } }
服务器端能够把信息发送给每一个客户端
为了达到这个目的,我们需要把每一个线程的信息都保存,于是我们想到用泛型ArrayList来实现。
所以服务器端需要做的就是,在每读取到字符时,都给每个客户端发一遍。for(int i=0 ; i<clients.size() ; ++i) { Client c = clients.get(i); if(c.equals(this)==false) //当前客户端不再发送,去重 c.send(str); }
9. Let’s Chat V0.9
做最后的进一步完善
当关闭一个客户端时,从ArrayList里remove(this)
注:移除了,但是,还是会报一些异常,我做捕捉,但是并未处理。- 设置窗口不可改大小
- 文本TextArea不可编辑
添加发送信息时的时间
添加了一些注释
后期添加功能
监听线程
数据端口和控制端口,不同端口号
好的处理关闭方法
告诉服务器端客服端要关闭了,服务器端做资源关闭处理
客户端信息
给每一个客户端一个名字,单独聊天(私聊),群聊
传文件
传表情,文件之类的
注:github源码