基于Java的局域网聊天工具

Puppetlover

发布日期: 2018-11-16 10:55:15 浏览量: 2000
评分:
star star star star star star star star star star_border
*转载请注明来自write-bug.com

一 需求分析

掌握Java语言的程序设计方法,理论结合实际操作巩固我们所学的现有知识,使用图形用户界面和socket通信实现一个聊天程序,充分利用线程知识,实现用一个局域网聊天室,同时学会处理各种异常和io输入输出流的应用,学习运用多线程操作。

聊天要以图形化界面的形式展现。可以实现聊天窗口的显示和关闭,同时可以载入客户输入的信息和读取输出的信息。在对话区域的右侧有滚动条,当该页面的面版满了,可以通过滚动条进行上拉和下拉。该对话区域可以实现多人同时进行聊天,也可以进行单人私聊。聊天内容前有显示是谁发送的消息,发送给谁等。

先启动一个服务器,设置服务器端口,然后启动客户端,通过连接IP地址和连接客户端成功之后即可以登入客户。只要连接共同的IP地址和共同的端口即可以通过线程和服务器、客户端之间的联系实现单人与单人私聊,单人与多人的群聊。

二 总体设计

2.1 服务器端的建立

服务器的功能是通过连接服务器端口实现客户端和服务器之间的的链接,当客户端成功连接到服务器端的时候,就新建一个Server_Thread线程,用于处理与客户端的通信,并启动该线程。显示一些信息,用户登录登出消息。

2.2 客户端的建立

客户端的功能是连接服务器之后,创建新客户ID,并能进行与其他用户聊天的聊天窗口。客户端会新建一个Client_Thread线程,用于客户端与服务器之间的通信,并启动线程。

2.3 服务器端线程的建立

服务端线程用于处理单个用户与其他用户进行通信,服务端线程一直处于运行状态,读取从客户端发来的消息,并对发来的消息类型进行处理,并将信息通过线程关系转发给不同的客户端。通过套接字跟迭代器遍历通知各线程用户上线,用户下线。

2.4 客户端线程的建立

客户端线程是当一个客户端建立时,客户端用于处理发送信息。当客户登入时并且输入信息该线程会读取客户写入的信息,同时更新在线客户列表实现客户的上线和下线的通知。

2.5 程序模块划分

局域网聊天程序包含以下几个模块:服务器端模块,服务器端线程模块,客户端模块和客户端线程模块。

如下图所示:

2.6 程序运行流程

打开程序软件,要先打开服务器和客户端,在服务器上设置一些信息,然后再服务器界面上显示出来,然后服务器就处于等待客户端连接的状态,当客户端连接后,马上建立两条线程,然后就开始运行,实现通信,通信好后就结束程序。如下图所示:

三 程序详细设计与实现

3.1 程序中主要方法分析

服务器端方法列表如下:

方法名 方法功能
ChatServer( ) 创建服务器界面
addWindowListener() 用于关闭服务器窗口。
connect() 用于服务器和客户端的连接

服务器线程方法列表如下:

方法名 方法功能
run() 用于实现用户上下线和聊天功能
logout() 方便起见给下线写的方法

客户端方法列表如下:

ChatClient() 创建聊天界面,并连接到服务器
setMonitorForSetIPJMenuItem(); 为设置ip项设置时间监听
setMonitorForSetPortJMenuItem(); 为设置端口项设置事件监听
setMonitorForAboutJMenuItem(); 为帮助项设置事件监听
setMonitorForLogoutJButton(); 为登出项设置事件监听
setMonitorForCurrentClientList(); 为当前用户列表设置事件监听
setMonitorForSentJButton(); 为发送按钮就行事件监听
loginButton.addActionListener(new Monitor()); 为登陆按钮设置监听
keyPressed() 为发送键设置的回车快捷键
createJMenuBar() 创建菜单栏

客户端线程方法列表如下:

方法名 方法功能
run() 用于更新客户列表和输入和显示功能
addDate() 显示时间的功能

3.2 程序实现

3.2.1 服务器端

ChatServer()

该方法是服务器端ChatServer类的构造方法,继承了JFrame类,用于创建服务器界面,对窗口设置了滚动条和其规格,并设置了关闭服务器窗口的按钮。主要代码如下:

  1. public ChatServer() {
  2. this.setTitle("服务器");
  3. Container container = this.getContentPane();
  4. displayJTextArea = new JTextArea();
  5. displayJTextArea.setEditable(false);
  6. JScrollPane jsp = new JScrollPane(displayJTextArea);
  7. jsp.setVerticalScrollBarPolicy
  8. (JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
  9. container.add(jsp, BorderLayout.CENTER);
  10. this.setBounds(400, 150, 500, 350);
  11. this.setVisible(true);
  12. this.addWindowListener(new WindowAdapter() {
  13. public void windowClosing(WindowEvent e) {
  14. System.exit(0);
  15. }
  16. });
  17. }

connect()

在服务器上记录服务器的一些信息:本机名、本机地址和本机监听的端口。服务器若一直开着,就会一直等待着,直到客户端的连接;客户端一旦连接成功,马上就会在服务器界面上显示连接客户的信息,然后同时会创建对应的线程,接下来就开始运行线程。主要代码如下:

  1. public void connect() {
  2. try {
  3. serverSocket = new ServerSocket(port); //记录服务器端的一些信息
  4. displayJTextArea.append("本机名:" + InetAddress.getLocalHost().getHostName() + "\n");
  5. displayJTextArea.append("本机地址:" + InetAddress.getLocalHost().getHostAddress() + "\n");
  6. displayJTextArea.append("本机监听端口:" + port + "\n");
  7. } catch (BindException e) {
  8. JOptionPane.showMessageDialog(null, "此端口已被占用,无法监听!", "提示", JOptionPane.WARNING_MESSAGE);
  9. } catch (UnknownHostException e) {
  10. e.printStackTrace();
  11. } catch (IOException e) {
  12. }
  13. while (true) {
  14. try {
  15. socket = serverSocket.accept(); //等待客户端的连接
  16. Date now = new Date();
  17. SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd E kk:mm:ss");
  18. displayJTextArea.append("\n" + f.format(now) + "\n");
  19. displayJTextArea.append(socket.getInetAddress().getHostAddress()
  20. + " is connected" + "\n");
  21. Server_Thread st = new Server_Thread(socket, threadList, displayJTextArea); //客户连接上,建立对应的线程
  22. st.start();
  23. } catch (IOException e) {
  24. e.printStackTrace();
  25. }
  26. }
  27. }

3.2.2 客户端

ChatClient()

该方法是客户端的构造方法,继承了JFrame类,用于创建客户端聊天界面,采用了GridBagLayout的布局,包含客户信息、在线客户列表、会话对象、聊天窗口等信息。对登陆、退出、发送和菜单等的按钮设置了监听。为方便起见,为发送按钮设置了回车的快捷键,且在聊天的界面进行了滚动条的设置。主要代码如下:

  1. public ChatClient() {
  2. this.setTitle("客户端");
  3. Container container = this.getContentPane();
  4. GridBagLayout layout = new GridBagLayout();
  5. GridBagConstraints constraints = new GridBagConstraints();
  6. constraints.fill = GridBagConstraints.BOTH; //组件随着所给区域可以进行扩展
  7. container.setLayout(layout);
  8. createJMenuBar();
  9. 。。。。。。 //中间省略语句为对界面的设置
  10. this.setBounds(400, 100, 450, 500);
  11. this.setVisible(true);
  12. this.addWindowListener(new WindowAdapter() {
  13. public void windowClosing(WindowEvent arg0) {
  14. System.exit(0);
  15. }
  16. });
  17. setMonitorForSetIPJMenuItem(); //为设置ip项设置时间监听
  18. setMonitorForSetPortJMenuItem(); //味设置端口项设置事件监听
  19. setMonitorForAboutJMenuItem(); //为帮助项设置事件监听
  20. setMonitorForLogoutJButton(); //为登出项设置事件监听
  21. setMonitorForCurrentClientList(); //为当前用户列表设置事件监听
  22. setMonitorForSentJButton(); //为发送按钮就行事件监听
  23. loginButton.addActionListener(new Monitor()); //为登陆按钮设置监听
  24. messageJTextField.addKeyListener(new KeyAdapter() {
  25. public void keyPressed(KeyEvent e) { //为发送按钮设置的监听
  26. if(e.getKeyChar() == KeyEvent.VK_ENTER) {
  27. sendMessage();
  28. }
  29. }
  30. });
  31. }

关键方法

  1. public void setMonitorForSetIPJMenuItem(); // 用于为设置ip项设置时间监听
  2. public void setMonitorForSetPortJMenuItem(); //用于为设置端口项设置事件监听
  3. public void setMonitorForAboutJMenuItem(); //用于为帮助项设置事件监听
  4. public void setMonitorForLogoutJButton(); //用于为登出项设置事件监听
  5. public void setMonitorForCurrentClientList(); //用于为当前用户列表设置事件监听
  6. public void setMonitorForSentJButton(); //用于为发送按钮就行事件监听
  7. public void loginButton.addActionListener(new Monitor());//用于为登陆按钮设置监听
  8. public void messageJTextField.addKeyListener(new KeyAdapter() //用于给发送按钮设置回车快捷键

3.2.3 服务器端线程

run()

迭代遍历,通过线程实现上线通知防止重名登入与消息的发送以及调用logout方法,选择回话对象。

  1. public void run() {
  2. while (true) {
  3. String msg = null;
  4. try {
  5. msg = reader.readLine();
  6. if (msg.startsWith("login")) {
  7. loginName = msg.substring(msg.indexOf(':') + 1).trim();
  8. if (! threadList.containsKey(loginName)) {
  9. writer.println(loginName + ": 登录成功!");
  10. threadList.put(loginName, this);
  11. displayTextArea.append(loginName + " 进入聊天室 \n"); displayTextArea.setCaretPosition(displayTextArea.
  12. getText().length());
  13. Iterator<Entry<String, Server_Thread>> it
  14. = threadList.entrySet().iterator();
  15. while (it.hasNext()) {
  16. Map.Entry entry = (Map.Entry) it.next();
  17. Server_Thread st = (Server_Thread) entry.getValue();
  18. if (st != this) {
  19. st.writer.println("用户上线:" + loginName); //通知各线程上线通知
  20. writer.println("用户上线:" + st.loginName); //刷新自己的线程
  21. }
  22. }
  23. } else {
  24. writer.println(loginName + " 已存在,请选择其他的用户名登录");
  25. }
  26. } else if(msg.startsWith("logout")){
  27. logout();
  28. break;
  29. } else {String [] toClientNames;
  30. toClientNames = msg.substring(msg.indexOf('#') + 1).split("#");
  31. String message = toClientNames[toClientNames.length - 1];
  32. for (int i = 0; i < toClientNames.length - 1; i++) {
  33. Server_Thread st = threadList.get(toClientNames[i].trim());
  34. st.writer.println(loginName + ": " + message);
  35. }
  36. writer.println(loginName + ": " + message);
  37. }
  38. } catch (IOException e) {
  39. logout();
  40. break;
  41. }
  42. }
  43. }

logout()

迭代遍历信息,实现下线通知,删除套接字端口。

  1. public void logout() {
  2. Iterator it = threadList.entrySet().iterator();
  3. while (it.hasNext()) {
  4. Map.Entry entry = (Map.Entry) it.next();
  5. Server_Thread st = (Server_Thread) entry.getValue();
  6. if (st != this && st.isAlive()) {
  7. st.writer.println("用户下线:" + loginName);
  8. }
  9. }
  10. threadList.remove(loginName);
  11. if (socket != null) {
  12. try {
  13. socket.close();
  14. }catch (IOException e1) {
  15. e1.printStackTrace();
  16. }
  17. }

3.2.4 客户端线程

run()

运行程序,实现对客户的登录,上线,下线以及对客户发送信息的读取,同时更新在线客户。

  1. public void run() {
  2. String s = null;
  3. while (true) {
  4. try {
  5. while((s = reader.readLine()) != null) {
  6. if (s.endsWith("登录成功!")) {
  7. addDate();
  8. cc.displayTextArea.append(s + "\n");
  9. cc.logoutJButton.setEnabled(true);
  10. cc.clientNameTextField.setEnabled(false);
  11. cc.loginButton.setEnabled(false);
  12. } else if (s.startsWith("用户上线:")) {
  13. String loginName = s.substring(s.indexOf(":") + 1);
  14. addDate();
  15. cc.displayTextArea.append("客户:" + loginName + "上线 \n");
  16. cc.displayTextArea.setCaretPosition(cc.displayTextArea.getText().length());
  17. cc.currentClientList.add(loginName);
  18. continue;
  19. } else if (s.startsWith("用户下线:")) {
  20. String loginName = s.substring(s.indexOf(":") + 1);
  21. addDate();
  22. cc.displayTextArea.append("客户:" + loginName + " 下线 \r\n");
  23. cc.displayTextArea.setCaretPosition(cc.displayTextArea.getText().length());
  24. cc.currentClientList.remove(loginName);
  25. continue;
  26. } else {
  27. addDate();
  28. cc.displayTextArea.append("客户" + s +"\n");
  29. cc.displayTextArea.setCaretPosition(cc.displayTextArea.getText().length());
  30. }
  31. }
  32. } catch (IOException e) {
  33. JOptionPane.showMessageDialog(null, "服务器异常!", "提示", JOptionPane.ERROR_MESSAGE);
  34. break;
  35. }
  36. }
  37. }

addDate()

显示日期,星期和时间.

  1. public void addDate() {
  2. Date now = new Date();
  3. SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd E kk:mm:ss");
  4. cc.displayTextArea.append("\n" + f.format(now) + "\n");
  5. cc.displayTextArea.setCaretPosition(cc.displayTextArea.getText().length());
  6. }

四 运行测试

打开服务器,立即弹出设置端口的窗口,提示要设置端口

当端口设置成功后,服务器显示一些信息

打开客户端,弹出客户端的主界面

用户注册新ID登陆显示

在服务器主界面显示客户端上下线信息如下

客户端界面显示在线客户列表及多用户聊天窗口

上传的附件 cloud_download 基于Java的局域网聊天工具.zip ( 350.32kb, 122次下载 )
error_outline 下载需要10点积分

keyboard_arrow_left上一篇 : 基于JAVA实现的网络五子棋游戏 基于JAVA和MySQL的离散数学题库管理系统 : 下一篇keyboard_arrow_right



Puppetlover
2018-11-16 10:54:37
使用Java和TCP Socket实现的局域网聊天程序
fengfengfeng
2019-11-12 17:03:10
哦哦哦可以,太感谢了太感谢了
tinggg
2020-06-22 16:02:02
太棒了

发送私信

据我测算,还可以退五十步,但生活只有五步

9
文章数
8
评论数
最近文章
eject