当前位置:   article > 正文

Java 多人聊天室_java多人聊天室

java多人聊天室

Java实训做的多人聊天室,效果如图:

 功能:

  • 能够实现多个客户端之间的互相聊天,服务端来负责接收数据和转发数据。
  • 每个客户端可以自己设置和修改自己的名称,并传给服务器让其他客户端知道。
  • 当有客户端加入和退出时,群聊里每个客户端都有提示。(优化了异常检测,意外断开终止时也能不报错并提示用户xxx退出聊天室)
  • 当服务器异常断开或正常退出,客户端都可以提示并且程序不报错。
  • 服务端能够实时的显示在线的人数、  

关键技术: 

  • 集合(服务端并不是对一个客户端进行操作,是多个客户端之间来回通信充当转发数据的功能。服务端需要创建一个集合用来存放服务端连接对象。这样只要监听到客户端发送过来的数据,就可以遍历整个集合,转发给每一个客户端)
  • 多线程(客户端通过主线程来发送数据,还需要再启动一个线程来不断循环接收服务端转发过来的消息。服务端主线程使用while循环来不停的接收socket连接,然后为每个客户端的socket通道分配一个子线程接收)
  • 网络TCP传输的嵌套字socket(实现客户端和服务端之间的数据传输,选择TCP更安全可靠)
  • IO流(通道间的数据传输需要用到io流)

服务端代码:

  1. import javax.swing.*;
  2. import java.awt.*;
  3. import java.awt.event.ActionEvent;
  4. import java.awt.event.ActionListener;
  5. import java.awt.event.WindowAdapter;
  6. import java.awt.event.WindowEvent;
  7. import java.io.*;
  8. import java.net.ServerSocket;
  9. import java.net.Socket;
  10. import java.net.SocketException;
  11. import java.util.ArrayList;
  12. import java.util.Iterator;
  13. public class ServerChar extends JFrame {
  14. private static final int PORT = 8888;
  15. JTextArea jta = new JTextArea();
  16. JScrollPane jsp = new JScrollPane(jta);
  17. private JPanel south = new JPanel(); //面板
  18. private JLabel jb = new JLabel();
  19. private JButton stopBtn = new JButton("停止服务器");
  20. private boolean isStart = false;//服务器是否启动
  21. private ServerSocket serverSocket = null;
  22. private ArrayList<ClientConn> clientConn = new ArrayList<>();
  23. private int peopleOnline = 0;
  24. public ServerChar(){
  25. this.setTitle("服务端");
  26. this.add(jsp, BorderLayout.CENTER);
  27. jta.setEditable(false); //不能输入
  28. south.add(jb);
  29. jb.setText("聊天室在线人数: "+peopleOnline+" ");
  30. south.add(stopBtn);
  31. this.add(south,BorderLayout.SOUTH);
  32. stopBtn.addActionListener(new ActionListener() {
  33. @Override
  34. public void actionPerformed(ActionEvent e) {
  35. isStart = false;
  36. try {
  37. if (serverSocket != null){
  38. serverSocket.close();
  39. isStart = false;
  40. }
  41. } catch (IOException ex) {
  42. ex.printStackTrace();
  43. }finally {
  44. System.exit(0);
  45. }
  46. }
  47. });
  48. this.addWindowListener(new WindowAdapter() {
  49. @Override
  50. public void windowClosing(WindowEvent e) {
  51. isStart = false;
  52. try {
  53. if (serverSocket != null){
  54. serverSocket.close();
  55. isStart = false;
  56. }
  57. } catch (IOException ex) {
  58. ex.printStackTrace();
  59. }finally {
  60. System.exit(0);
  61. }
  62. }
  63. });
  64. this.setBounds(200,100,500,500);
  65. this.setVisible(true);
  66. startServer();
  67. }
  68. //服务器启动的方法
  69. public void startServer() {
  70. try{
  71. try {
  72. serverSocket = new ServerSocket(PORT);
  73. isStart = true;
  74. jta.append("服务器已启动,等待客户端连接..."+System.lineSeparator());
  75. } catch (IOException e) {
  76. e.printStackTrace();
  77. }
  78. while(isStart){
  79. Socket socket = serverSocket.accept();
  80. clientConn.add(new ClientConn(socket));
  81. jta.append("客户端"+"["+socket.getInetAddress()+"-"+socket.getPort()+"]"+"加入聊天室"+System.lineSeparator());
  82. jb.setText("聊天室在线人数: "+(++peopleOnline)+" ");
  83. }
  84. }catch (SocketException e){
  85. System.out.println("服务器中断");
  86. }catch (IOException e){
  87. e.printStackTrace();
  88. }
  89. }
  90. //这个对象属于服务器端的连接对象
  91. class ClientConn implements Runnable{
  92. Socket socket = null;
  93. //构造器 传入socket
  94. public ClientConn(Socket socket){
  95. this.socket=socket;
  96. (new Thread(this)).start();
  97. }
  98. @Override//同时接收客户端信息
  99. public void run() {
  100. String name = socket.getInetAddress()+"-"+socket.getPort();
  101. try {
  102. DataInputStream dis = new DataInputStream(socket.getInputStream());
  103. while (isStart){
  104. //读过来
  105. String str = dis.readUTF();
  106. if (str.startsWith("##")){
  107. name = str.substring("##".length(),str.length());
  108. jta.append("客户端"+"["+socket.getInetAddress()+"-"+socket.getPort()+"]"+"将名称设置为"+"["+name+"]"+System.lineSeparator());
  109. Iterator<ClientConn> it = clientConn.iterator();
  110. while(it.hasNext()) {
  111. ClientConn o = it.next();
  112. o.send(name+"加入了聊天室"+System.lineSeparator());
  113. }
  114. }else{
  115. jta.append("["+name+"]"+"说:"+str+System.lineSeparator());
  116. //发过去
  117. String strSend = "["+name+"]"+"说:"+str+System.lineSeparator();
  118. //要遍历所有的客户端来发送 遍历ccList 调用send方法
  119. Iterator<ClientConn> it = clientConn.iterator();
  120. while(it.hasNext()) {
  121. ClientConn o = it.next();
  122. o.send(strSend);
  123. }
  124. }
  125. }
  126. } catch (SocketException e){
  127. jta.append("客户端"+"["+name+"]"+"下线了"+System.lineSeparator());
  128. Iterator<ClientConn> it = clientConn.iterator();
  129. while(it.hasNext()) {
  130. ClientConn o = it.next();
  131. o.send(name+"退出了聊天室"+System.lineSeparator());
  132. }
  133. jb.setText("聊天室在线人数: "+(--peopleOnline)+" ");
  134. } catch (IOException e) {
  135. e.printStackTrace();
  136. }
  137. }
  138. //对每个连接发送信息的方法
  139. public void send(String str){
  140. try {
  141. DataOutputStream dos = new DataOutputStream(this.socket.getOutputStream());
  142. dos.writeUTF(str);
  143. dos.flush();
  144. } catch (IOException e) {
  145. e.printStackTrace();
  146. }
  147. }
  148. }
  149. public static void main(String[] args) {
  150. new ServerChar();
  151. }
  152. }

 客户端代码: 

  1. import javax.swing.*;
  2. import java.awt.*;
  3. import java.awt.event.ActionEvent;
  4. import java.awt.event.ActionListener;
  5. import java.awt.event.KeyEvent;
  6. import java.awt.event.KeyListener;
  7. import java.io.DataInputStream;
  8. import java.io.DataOutputStream;
  9. import java.io.IOException;
  10. import java.net.Socket;
  11. import java.net.SocketException;
  12. public class ClientChar extends JFrame implements KeyListener{
  13. public static void main(String[] args) throws IOException {
  14. new ClientChar().init();
  15. }
  16. //属性
  17. private boolean isConn = false;
  18. String name = "";
  19. private JTextArea jta;//文本域
  20. private JScrollPane jsp;//滚动条
  21. private JPanel jp;//面板
  22. private JTextField jtf;//文本框
  23. private JButton jb1;//发送信息
  24. //通道
  25. private Socket socket = null;
  26. private static final String CONNSTR = "127.0.0.1";
  27. private static final int CONNPORT = 8888;
  28. //流
  29. private DataOutputStream dos = null;
  30. //构造器
  31. public ClientChar(){
  32. //初始化组件
  33. jta = new JTextArea();
  34. jta.setEditable(false);//设置为不可编辑
  35. jsp = new JScrollPane(jta); //让文本域实现滚动效果
  36. //面板
  37. jp = new JPanel();
  38. jtf = new JTextField(15);
  39. jb1 = new JButton("发送信息");
  40. }
  41. public void init(){
  42. //将文本框和按钮添加到面板中
  43. jp.add(jtf);
  44. jtf.addKeyListener(this);
  45. jp.add(jb1);
  46. jb1.addActionListener(new ActionListener() {
  47. @Override
  48. public void actionPerformed(ActionEvent e) {
  49. //获取文本框内容
  50. String text = jtf.getText();
  51. sendMessage(text);
  52. if (text.length()==0){
  53. return;
  54. }
  55. //拼接内容并发送
  56. jtf.setText("");
  57. }
  58. });
  59. //将滚动条与面板全部添加到窗口中
  60. this.add(jsp, BorderLayout.CENTER);
  61. this.add(jp, BorderLayout.SOUTH);
  62. //设置窗口
  63. this.setBounds(700, 300, 300, 300);
  64. this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  65. this.setVisible(true);
  66. try {
  67. socket = new Socket(CONNSTR,CONNPORT);
  68. isConn = true;
  69. name = JOptionPane.showInputDialog(this.getContentPane(), "请输入你的用户名:");
  70. dos = new DataOutputStream(socket.getOutputStream());
  71. dos.writeUTF("##"+name);
  72. dos.flush();
  73. } catch (IOException e) {
  74. e.printStackTrace();
  75. }
  76. this.setTitle("聊天室-"+name);
  77. //启动线程类接收
  78. new Thread(new Receive()).start();
  79. }
  80. //发送信息到服务器的方法
  81. private void sendMessage(String str) {
  82. try {
  83. if(jtf.getText().length()==0){
  84. return;
  85. }
  86. dos = new DataOutputStream(socket.getOutputStream());
  87. dos.writeUTF(str);
  88. dos.flush();
  89. } catch (IOException e) {
  90. e.printStackTrace();
  91. }
  92. }
  93. @Override
  94. public void keyPressed(KeyEvent e) {
  95. if (e.getKeyCode()==KeyEvent.VK_ENTER){
  96. //回去输入框的内容
  97. String text = jtf.getText();
  98. //调用发送信息的方法
  99. sendMessage(text);
  100. //发送完后让输入框为空
  101. jtf.setText("");
  102. }
  103. }
  104. //多线程的类,实现了Runnable接口 用来接收
  105. class Receive implements Runnable{
  106. @Override
  107. public void run() {
  108. try {
  109. while (isConn) {
  110. DataInputStream dis = new DataInputStream(socket.getInputStream());
  111. String str = dis.readUTF(); //从管道读出服务端转发的数据 放到文本框里面
  112. jta.append(str);
  113. }
  114. } catch (SocketException e) {
  115. System.out.println("服务器终止");
  116. jta.append("服务器终止"+System.lineSeparator());
  117. } catch (IOException e) {
  118. e.printStackTrace();
  119. }
  120. }
  121. }
  122. @Override
  123. public void keyReleased(KeyEvent e) {
  124. }
  125. @Override
  126. public void keyTyped(KeyEvent e) {
  127. }
  128. }

如果有帮助的话麻烦点赞收藏,谢谢。你的支持是对我最大的鼓励。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/99161
推荐阅读
相关标签
  

闽ICP备14008679号