广州大学学生实验报告
开课实验室:
学院 | 计算机科学与网络工程学院 | 年级、专业、班 | 姓名 | 学号 | ||||
实验课程名称 | Java语言实验 | 成绩 | ||||||
实验项目名称 | 实验二十 实验报告(三)题目 | 指导老师 |
- 实验题目
实验任务
编写一个 Java 应用程序,实现图形界面多人聊天室(多线程实现),要求聊天室窗口标题是
“欢迎使用 XXX 聊天室应用”,其中 XXX 是自己的班级姓名学号,如“软件 171 张三 1234”。
二、实验目的
熟悉 Java 综合应用程序的开发。
三、程序设计
(1)程序代码:Server.java
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Server {
public final static Map<Socket,String> socketsMaps = Collections.synchronizedMap(new HashMap<Socket,String>()); //定义Map集合用于存储用户的Socket以及用户的名字 key:Socket Value:用户名
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
ServerSocket serverSocket = new ServerSocket(7777); // 创建服务端套接字
System.out.println("------多人聊天服务器已开启-------");
while (true) {
Socket socket = serverSocket.accept(); // 监听客户端套接字,若有客户端连接,则代码不会往下执行,否则会堵塞在此处。
new ThreadServer(socket).start(); // 开启线程,用于读取客户端发送的信息,并转发给每一个客户端
}
} catch (Exception e) {
e.printStackTrace(); // TODO: handle exception
}
}
}
class ThreadServer extends Thread {
private Socket socket;
ThreadServer(){};
ThreadServer(Socket socket) {
this.socket = socket;
}
@Override
public void run() { //重写run方法
try {
while(true){
DataInputStream dis = new DataInputStream(socket.getInputStream());
String data = dis.readUTF();
if(data.startsWith("①②③④")&&data.endsWith("①②③④")) { //发送过来的是用户信息
Server.socketsMaps.put(socket, data.replace("①②③④","")); //将Socket以及用户名字都存放在Map集合中
Set<Socket> sockets = Server.socketsMaps.keySet(); //获取所有的key(Socket),将所有用户的名字发送至客户端
Collection<String> names = Server.socketsMaps.values(); //获取所有的用户的名字,将这些名字拼装成一个字符串
StringBuffer sbf = new StringBuffer();
for(String userName :names) {
sbf.append(userName).append(",");
}
System.out.println("sbf:"+sbf.toString());
for(Socket soc:sockets) {
DataOutputStream dos = new DataOutputStream(soc.getOutputStream());
dos.writeUTF("①②③④"+sbf.toString()+"①②③④");
dos.flush();
}
}
else { //发送过来的是聊天信息
Set<Socket> sockets = Server.socketsMaps.keySet(); //获取所有的key(Socket),将所有用户的名字发送至客户端
for(Socket soc:sockets) { //將聊天信息广播出去
DataOutputStream dos = new DataOutputStream(soc.getOutputStream());
dos.writeUTF(Server.socketsMaps.get(socket)+":"+data);
dos.flush();
}
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
(2)程序代码:Client.java
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
public class Client {
private JFrame mainWin = new JFrame("聊天窗口");
private JTextArea displayTa = new JTextArea(14, 40); // 消息展示框
private DefaultListModel<String> userListModel = new DefaultListModel<>(); // 在线用户名称展示框
private JList<String> userList = new JList<>(userListModel);
private JTextArea inputTF = new JTextArea(4, 40); // 消息发送框
private JButton sendBn = new JButton("发送"); // 消息按钮
private String curUser; // 用户记录当前聊天用户名
public static void main(String[] args) {
new Client().init();
}
private void init() {
try {
String userName = JOptionPane.showInputDialog(mainWin, "请输入您的班级姓名学号:"); // 弹出“输入”对话框获取用户输入的信息
curUser = userName; // 把用户输入的信息,赋给curUser
mainWin.setTitle(curUser + "的聊天窗口");
Socket socket = new Socket("127.0.0.1", 7777); // 创建套接字
DataOutputStream dos = new DataOutputStream(socket.getOutputStream()); // 向服务器声明
dos.writeUTF("①②③④"+userName+"①②③④"); //发送用户名到服务端
dos.flush();
new ThreadClient(socket, this).start(); // 开启线程,用于读取服务器发送的信息
JPanel bottomPanel = new JPanel();
mainWin.add(bottomPanel, BorderLayout.SOUTH); // 将消息框和发送按钮添加到聊天窗口的底端
bottomPanel.add(inputTF);
bottomPanel.add(sendBn);
ActionListener listener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String message = inputTF.getText(); // 获取用户发送的消息
sendSms(message,socket);
}
};
sendBn.addActionListener(listener); // 给发送消息按钮添加事件监听器
JPanel centerPanel = new JPanel();
mainWin.add(centerPanel); // 将展示消息区centerPanel添加到窗口的中间
centerPanel.add(new JScrollPane(displayTa)); // 让展示消息区可以滚动
displayTa.setEditable(false);
Box rightBox = new Box(BoxLayout.Y_AXIS); // 用户列表和是否私聊放到窗口的最右边
userList.setFixedCellWidth(60);
userList.setVisibleRowCount(13);
rightBox.add(new JLabel("在线用户列表:"));
rightBox.add(new JScrollPane(userList));
centerPanel.add(rightBox);
mainWin.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 关闭窗口退出当前程序
mainWin.pack(); // swing加上这句就可以拥有关闭窗口的功能
mainWin.setVisible(true);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
protected void sendSms(String sms, Socket socket) { //点击发送后将消息发送到服务器
try {
DataOutputStream dos = new DataOutputStream(socket.getOutputStream()); //发送聊天消息到服务端
dos.writeUTF(sms);
dos.flush();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
public DefaultListModel<String> getUserListModel() {
return userListModel;
}
public JTextArea getDisplayTa() {
return displayTa;
}
public JTextArea getInputTF()
{
return inputTF;
}
}
class ThreadClient extends Thread { // 继承多线程类,用来读取服务器发送的信息
private Socket socket;
private Client client;
ThreadClient() {
}
ThreadClient(Socket socket, Client client) {
this.socket = socket;
this.client = client;
}
@Override
public void run() { //重写run方法
try {
while (true) {
DataInputStream dis = new DataInputStream(socket.getInputStream());
String message = dis.readUTF();
if(message.startsWith("①②③④")&&message.endsWith("①②③④")) //输入信息为用户名时
{
String[] names = message.replace("①②③④","").split(",");
client.getUserListModel().clear();// 清空用户列表
for (int i = 0; i < names.length; ++i) {
client.getUserListModel().addElement(names[i]);
}
}
else //输入信息为聊天信息时,将聊天信息放在displayTa中
{
client.getInputTF().setText("");
client.getDisplayTa().append(message+"\t\n");
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
四、程序运行与测试
运行的结果如图所示。
(图片涉及个人隐私,大家可以自己在命令行窗口运行上述代码,不行的话可以在评论区留言~)
五、实验总结与心得
1、该实验是一个综合性的实验,它用到了这学期学Java的很多知识,例如:图形界面、多线程、网络编程等。以实现多人聊天的功能。
2、通过与同学的讨论与思考,摸清了这个应用的实现思路。首先需要两个类,一个叫Server,用来接受信息,并把收到的信息传到在线用户的聊天框中去。另一个叫Client,用来输入聊天内容。
3、需要特别注意的是:
(1)Server若是没有写成多线程的形式,将会给一个用户一直占用资源。
(2)用户的输入其实就是一个循环机制,用户需要的话就可以一直输入聊天内容,然后聊天内容会经过服务器传给在线用户的聊天框中,从而实现多人聊天的功能。