Java Socket实现多人聊天系统(图形界面+文件传输功能)

Java Socket实现多人聊天系统(图形界面+文件传输功能)文章目录前言一 多人聊天系统 1 1 客户端 1 2 服务端前言 GitHub 地址 https github com Samven7 mutichat system 一 多人聊天系统 1 1 客户端 Login java 登录界面 Login javapackagee importjava awt importjavax swing publicclass

前言

GitHub地址:https://github.com/Samven7/multichat-system

开发环境:Eclipse Java 2019-06

注意:本项目只在单主机运行调试过,没试过在局域网和不同主机之间接发消息和文件(估计不行),有需要的自行查阅资料。

一、多人聊天系统

1.1 客户端

Login.java:登录界面

// Login.java package exp5; import java.awt.*; import javax.swing.*; public class Login { 
    JTextField textField = null; JPasswordField pwdField = null; ClientReadAndPrint.LoginListen listener=null; // 构造函数 public Login() { 
    init(); } void init() { 
    JFrame jf = new JFrame("登录"); jf.setBounds(500, 250, 310, 210); jf.setResizable(false); // 设置是否缩放 JPanel jp1 = new JPanel(); JLabel headJLabel = new JLabel("登录界面"); headJLabel.setFont(new Font(null, 0, 35)); // 设置文本的字体类型、样式 和 大小 jp1.add(headJLabel); JPanel jp2 = new JPanel(); JLabel nameJLabel = new JLabel("用户名:"); textField = new JTextField(20); JLabel pwdJLabel = new JLabel("密码: "); pwdField = new JPasswordField(20); JButton loginButton = new JButton("登录"); JButton registerButton = new JButton("注册"); // 没设置功能 jp2.add(nameJLabel); jp2.add(textField); jp2.add(pwdJLabel); jp2.add(pwdField); jp2.add(loginButton); jp2.add(registerButton); JPanel jp = new JPanel(new BorderLayout()); // BorderLayout布局 jp.add(jp1, BorderLayout.NORTH); jp.add(jp2, BorderLayout.CENTER); // 设置监控 listener = new ClientReadAndPrint().new LoginListen(); // 新建监听类 listener.setJTextField(textField); // 调用PoliceListen类的方法 listener.setJPasswordField(pwdField); listener.setJFrame(jf); pwdField.addActionListener(listener); // 密码框添加监听 loginButton.addActionListener(listener); // 按钮添加监听 jf.add(jp); jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置关闭图标作用 jf.setVisible(true); // 设置可见 } } 

ChatView.java:登录成功后的个人聊天界面

// ChatView.java package exp5; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import javax.swing.*; import javax.swing.filechooser.FileNameExtensionFilter; public class ChatView { 
    String userName; //由客户端登录时设置 JTextField text; JTextArea textArea; ClientReadAndPrint.ChatViewListen listener; // 构造函数 public ChatView(String userName) { 
    this.userName = userName ; init(); } // 初始化函数 void init() { 
    JFrame jf = new JFrame("客户端"); jf.setBounds(500,200,400,330); //设置坐标和大小 jf.setResizable(false); // 缩放为不能缩放 JPanel jp = new JPanel(); JLabel lable = new JLabel("用户:" + userName); textArea = new JTextArea("*登录成功,欢迎来到多人聊天室!\n",12, 35); textArea.setEditable(false); // 设置为不可修改 JScrollPane scroll = new JScrollPane(textArea); // 设置滚动面板(装入textArea) scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); // 显示垂直条 jp.add(lable); jp.add(scroll); text = new JTextField(20); JButton button = new JButton("发送"); JButton openFileBtn = new JButton("发送文件"); jp.add(text); jp.add(button); jp.add(openFileBtn); // 设置“打开文件”监听 openFileBtn.addActionListener(new ActionListener() { 
    public void actionPerformed(ActionEvent e) { 
    showFileOpenDialog(jf); } }); // 设置“发送”监听 listener = new ClientReadAndPrint().new ChatViewListen(); listener.setJTextField(text); // 调用PoliceListen类的方法 listener.setJTextArea(textArea); listener.setChatViewJf(jf); text.addActionListener(listener); // 文本框添加监听 button.addActionListener(listener); // 按钮添加监听 jf.add(jp); jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置右上角关闭图标的作用 jf.setVisible(true); // 设置可见 } // “打开文件”调用函数 void showFileOpenDialog(JFrame parent) { 
    // 创建一个默认的文件选择器 JFileChooser fileChooser = new JFileChooser(); // 设置默认显示的文件夹 fileChooser.setCurrentDirectory(new File("C:/Users/Samven/Desktop")); // 添加可用的文件过滤器(FileNameExtensionFilter 的第一个参数是描述, 后面是需要过滤的文件扩展名) // fileChooser.addChoosableFileFilter(new FileNameExtensionFilter("(txt)", "txt")); // 设置默认使用的文件过滤器(FileNameExtensionFilter 的第一个参数是描述, 后面是需要过滤的文件扩展名 可变参数) fileChooser.setFileFilter(new FileNameExtensionFilter("(txt)", "txt")); // 打开文件选择框(线程将被堵塞,知道选择框被关闭) int result = fileChooser.showOpenDialog(parent); // 对话框将会尽量显示在靠近 parent 的中心 // 点击确定 if(result == JFileChooser.APPROVE_OPTION) { 
    // 获取路径 File file = fileChooser.getSelectedFile(); String path = file.getAbsolutePath(); ClientFileThread.outFileToServer(path); } } } 

Client.java:客户端

// Client.java package exp5; import java.net.*; import javax.swing.*; import java.awt.event.*; import java.io.*; public class Client { 
    // 主函数,新建登录窗口 public static void main(String[] args) { 
    new Login(); } } / * 负责客户端的读和写,以及登录和发送的监听 * 之所以把登录和发送的监听放在这里,是因为要共享一些数据,比如mySocket,textArea */ class ClientReadAndPrint extends Thread{ 
    static Socket mySocket = null; // 一定要加上static,否则新建线程时会清空 static JTextField textInput; static JTextArea textShow; static JFrame chatViewJFrame; static BufferedReader in = null; static PrintWriter out = null; static String userName; // 用于接收从服务端发送来的消息 public void run() { 
    try { 
    in = new BufferedReader(new InputStreamReader(mySocket.getInputStream())); // 输入流 while (true) { 
    String str = in.readLine(); // 获取服务端发送的信息 textShow.append(str + '\n'); // 添加进聊天客户端的文本区域 textShow.setCaretPosition(textShow.getDocument().getLength()); // 设置滚动条在最下面 } } catch (Exception e) { 
   } } /登录监听(内部类)/ class LoginListen implements ActionListener{ 
    JTextField textField; JPasswordField pwdField; JFrame loginJFrame; // 登录窗口本身 ChatView chatView = null; public void setJTextField(JTextField textField) { 
    this.textField = textField; } public void setJPasswordField(JPasswordField pwdField) { 
    this.pwdField = pwdField; } public void setJFrame(JFrame jFrame) { 
    this.loginJFrame = jFrame; } public void actionPerformed(ActionEvent event) { 
    userName = textField.getText(); String userPwd = String.valueOf(pwdField.getPassword()); // getPassword方法获得char数组 if(userName.length() >= 1 && userPwd.equals("123")) { 
    // 密码为123并且用户名长度大于等于1 chatView = new ChatView(userName); // 新建聊天窗口,设置聊天窗口的用户名(静态) // 建立和服务器的联系 try { 
    InetAddress addr = InetAddress.getByName(null); // 获取主机地址 mySocket = new Socket(addr,8081); // 客户端套接字 loginJFrame.setVisible(false); // 隐藏登录窗口 out = new PrintWriter(mySocket.getOutputStream()); // 输出流 out.println("用户【" + userName + "】进入聊天室!"); // 发送用户名给服务器 out.flush(); // 清空缓冲区out中的数据 } catch (IOException e) { 
    e.printStackTrace(); } // 新建普通读写线程并启动 ClientReadAndPrint readAndPrint = new ClientReadAndPrint(); readAndPrint.start(); // 新建文件读写线程并启动 ClientFileThread fileThread = new ClientFileThread(userName, chatViewJFrame, out); fileThread.start(); } else { 
    JOptionPane.showMessageDialog(loginJFrame, "账号或密码错误,请重新输入!", "提示", JOptionPane.WARNING_MESSAGE); } } } /聊天界面监听(内部类)/ class ChatViewListen implements ActionListener{ 
    public void setJTextField(JTextField text) { 
    textInput = text; // 放在外部类,因为其它地方也要用到 } public void setJTextArea(JTextArea textArea) { 
    textShow = textArea; // 放在外部类,因为其它地方也要用到 } public void setChatViewJf(JFrame jFrame) { 
    chatViewJFrame = jFrame; // 放在外部类,因为其它地方也要用到 // 设置关闭聊天界面的监听 chatViewJFrame.addWindowListener(new WindowAdapter() { 
    public void windowClosing(WindowEvent e) { 
    out.println("用户【" + userName + "】离开聊天室!"); out.flush(); System.exit(0); } }); } // 监听执行函数 public void actionPerformed(ActionEvent event) { 
    try { 
    String str = textInput.getText(); // 文本框内容为空 if("".equals(str)) { 
    textInput.grabFocus(); // 设置焦点(可行) // 弹出消息对话框(警告消息) JOptionPane.showMessageDialog(chatViewJFrame, "输入为空,请重新输入!", "提示", JOptionPane.WARNING_MESSAGE); return; } out.println(userName + "说:" + str); // 输出给服务端 out.flush(); // 清空缓冲区out中的数据 textInput.setText(""); // 清空文本框 textInput.grabFocus(); // 设置焦点(可行) // textInput.requestFocus(true); // 设置焦点(可行) } catch (Exception e) { 
   } } } } 

ClientFileThread.java:文件传输功能(客户端)

// ClientFileThread.java package exp5; import java.io.*; import java.net.*; import javax.swing.*; public class ClientFileThread extends Thread{ 
    private Socket socket = null; private JFrame chatViewJFrame = null; static String userName = null; static PrintWriter out = null; // 普通消息的发送(Server.java传来的值) static DataInputStream fileIn = null; static DataOutputStream fileOut = null; static DataInputStream fileReader = null; static DataOutputStream fileWriter = null; public ClientFileThread(String userName, JFrame chatViewJFrame, PrintWriter out) { 
    ClientFileThread.userName = userName; this.chatViewJFrame = chatViewJFrame; ClientFileThread.out = out; } // 客户端接收文件 public void run() { 
    try { 
    InetAddress addr = InetAddress.getByName(null); // 获取主机地址 socket = new Socket(addr, 8090); // 客户端套接字 fileIn = new DataInputStream(socket.getInputStream()); // 输入流 fileOut = new DataOutputStream(socket.getOutputStream()); // 输出流 // 接收文件 while(true) { 
    String textName = fileIn.readUTF(); long totleLength = fileIn.readLong(); int result = JOptionPane.showConfirmDialog(chatViewJFrame, "是否接受?", "提示", JOptionPane.YES_NO_OPTION); int length = -1; byte[] buff = new byte[1024]; long curLength = 0; // 提示框选择结果,0为确定,1位取消 if(result == 0){ 
    // out.println("【" + userName + "选择了接收文件!】"); // out.flush(); File userFile = new File("C:\\Users\\Samven\\Desktop\\接受文件\\" + userName); if(!userFile.exists()) { 
    // 新建当前用户的文件夹 userFile.mkdir(); } File file = new File("C:\\Users\\Samven\\Desktop\\接受文件\\" + userName + "\\"+ textName); fileWriter = new DataOutputStream(new FileOutputStream(file)); while((length = fileIn.read(buff)) > 0) { 
    // 把文件写进本地 fileWriter.write(buff, 0, length); fileWriter.flush(); curLength += length; // out.println("【接收进度:" + curLength/totleLength*100 + "%】"); // out.flush(); if(curLength == totleLength) { 
    // 强制结束 break; } } out.println("【" + userName + "接收了文件!】"); out.flush(); // 提示文件存放地址 JOptionPane.showMessageDialog(chatViewJFrame, "文件存放地址:\n" + "C:\\Users\\Samven\\Desktop\\接受文件\\" + userName + "\\" + textName, "提示", JOptionPane.INFORMATION_MESSAGE); } else { 
    // 不接受文件 while((length = fileIn.read(buff)) > 0) { 
    curLength += length; if(curLength == totleLength) { 
    // 强制结束 break; } } } fileWriter.close(); } } catch (Exception e) { 
   } } // 客户端发送文件 static void outFileToServer(String path) { 
    try { 
    File file = new File(path); fileReader = new DataInputStream(new FileInputStream(file)); fileOut.writeUTF(file.getName()); // 发送文件名字 fileOut.flush(); fileOut.writeLong(file.length()); // 发送文件长度 fileOut.flush(); int length = -1; byte[] buff = new byte[1024]; while ((length = fileReader.read(buff)) > 0) { 
    // 发送内容 fileOut.write(buff, 0, length); fileOut.flush(); } out.println("【" + userName + "已成功发送文件!】"); out.flush(); } catch (Exception e) { 
   } } } 

1.2 服务器端

MultiChat.java:多人聊天系统界面(服务器端)

// MultiChat.java package exp5; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.*; public class MultiChat { 
    JTextArea textArea; // 用于向文本区域添加信息 void setTextArea(String str) { 
    textArea.append(str+'\n'); textArea.setCaretPosition(textArea.getDocument().getLength()); // 设置滚动条在最下面 } // 构造函数 public MultiChat() { 
    init(); } void init() { 
    JFrame jf = new JFrame("服务器端"); jf.setBounds(500,100,450,500); // 设置窗口坐标和大小 jf.setResizable(false); // 设置为不可缩放 JPanel jp = new JPanel(); // 新建容器 JLabel lable = new JLabel("==欢迎来到多人聊天系统(服务器端)=="); textArea = new JTextArea(23, 38); // 新建文本区域并设置长宽 textArea.setEditable(false); // 设置为不可修改 JScrollPane scroll = new JScrollPane(textArea); // 设置滚动面板(装入textArea) scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); // 显示垂直条 jp.add(lable); jp.add(scroll); jf.addWindowListener(new WindowAdapter() { 
    public void windowClosing(WindowEvent e) { 
    System.exit(0); } }); jf.add(jp); jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置关闭图标作用 jf.setVisible(true); // 设置可见 } } 

Server.java:服务器端

// Server.java package exp5; import java.io.*; import java.net.*; import java.util.*; public class Server{ 
    static ServerSocket server = null; static Socket socket = null; static List<Socket> list = new ArrayList<Socket>(); // 存储客户端 public static void main(String[] args) { 
    MultiChat multiChat = new MultiChat(); // 新建聊天系统界面 try { 
    // 在服务器端对客户端开启文件传输的线程 ServerFileThread serverFileThread = new ServerFileThread(); serverFileThread.start(); server = new ServerSocket(8081); // 服务器端套接字(只能建立一次) // 等待连接并开启相应线程 while (true) { 
    socket = server.accept(); // 等待连接 list.add(socket); // 添加当前客户端到列表 // 在服务器端对客户端开启相应的线程 ServerReadAndPrint readAndPrint = new ServerReadAndPrint(socket, multiChat); readAndPrint.start(); } } catch (IOException e1) { 
    e1.printStackTrace(); // 出现异常则打印出异常的位置 } } } / * 服务器端读写类线程 * 用于服务器端读取客户端的信息,并把信息发送给所有客户端 */ class ServerReadAndPrint extends Thread{ 
    Socket nowSocket = null; MultiChat multiChat = null; BufferedReader in =null; PrintWriter out = null; // 构造函数 public ServerReadAndPrint(Socket s, MultiChat multiChat) { 
    this.multiChat = multiChat; // 获取多人聊天系统界面 this.nowSocket = s; // 获取当前客户端 } public void run() { 
    try { 
    in = new BufferedReader(new InputStreamReader(nowSocket.getInputStream())); // 输入流 // 获取客户端信息并把信息发送给所有客户端 while (true) { 
    String str = in.readLine(); // 发送给所有客户端 for(Socket socket: Server.list) { 
    out = new PrintWriter(socket.getOutputStream()); // 对每个客户端新建相应的socket套接字 if(socket == nowSocket) { 
    // 发送给当前客户端 out.println("(你)" + str); } else { 
    // 发送给其它客户端 out.println(str); } out.flush(); // 清空out中的缓存 } // 调用自定义函数输出到图形界面 multiChat.setTextArea(str); } } catch (Exception e) { 
    Server.list.remove(nowSocket); // 线程关闭,移除相应套接字 } } } 

ServerFileThread.java:文件传输功能(服务器端)

// ServerFileThread.java package exp5; import java.io.*; import java.net.*; import java.util.ArrayList; import java.util.List; public class ServerFileThread extends Thread{ 
    ServerSocket server = null; Socket socket = null; static List<Socket> list = new ArrayList<Socket>(); // 存储客户端 public void run() { 
    try { 
    server = new ServerSocket(8090); while(true) { 
    socket = server.accept(); list.add(socket); // 开启文件传输线程 FileReadAndWrite fileReadAndWrite = new FileReadAndWrite(socket); fileReadAndWrite.start(); } } catch (IOException e) { 
    e.printStackTrace(); } } } class FileReadAndWrite extends Thread { 
    private Socket nowSocket = null; private DataInputStream input = null; private DataOutputStream output = null; public FileReadAndWrite(Socket socket) { 
    this.nowSocket = socket; } public void run() { 
    try { 
    input = new DataInputStream(nowSocket.getInputStream()); // 输入流 while (true) { 
    // 获取文件名字和文件长度 String textName = input.readUTF(); long textLength = input.readLong(); // 发送文件名字和文件长度给所有客户端 for(Socket socket: ServerFileThread.list) { 
    output = new DataOutputStream(socket.getOutputStream()); // 输出流 if(socket != nowSocket) { 
    // 发送给其它客户端 output.writeUTF(textName); output.flush(); output.writeLong(textLength); output.flush(); } } // 发送文件内容 int length = -1; long curLength = 0; byte[] buff = new byte[1024]; while ((length = input.read(buff)) > 0) { 
    curLength += length; for(Socket socket: ServerFileThread.list) { 
    output = new DataOutputStream(socket.getOutputStream()); // 输出流 if(socket != nowSocket) { 
    // 发送给其它客户端 output.write(buff, 0, length); output.flush(); } } if(curLength == textLength) { 
    // 强制退出 break; } } } } catch (Exception e) { 
    ServerFileThread.list.remove(nowSocket); // 线程关闭,移除相应套接字 } } } 

二、运行效果

2.1 初始化

  • 服务器端(先运行Server.java)
    Snipaste_2019-10-14_21-07-09

  • 登录界面(接着运行Client.java,运行一次生成一个登录界面)
    Snipaste_2019-10-14_21-08-44
    这里我还没有实现注册功能,登录的用户名随意(不为空即可),密码是123。




2.2 登录成功

Snipaste_2019-10-14_21-12-23
Snipaste_2019-10-14_21-13-03

2.3 发送信息

Snipaste_2019-10-14_22-01-35

2.4 发送文件

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/213349.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 下午6:02
下一篇 2026年3月18日 下午6:02


相关推荐

  • Double转BigDecimal的坑

    Double转BigDecimal的坑publicstaticvoidmain(String[]args){floata=57.3f;BigDecimaldecimalA=newBigDecimal(a);System.out.println(decimalA)…

    2022年6月4日
    34
  • 根目录执行chmod -R 777 / 的补救方法

    根目录执行chmod -R 777 / 的补救方法

    2022年3月12日
    358
  • Python面试题之基础篇(一)「建议收藏」

    PHP中文网为大家准备了Python面试题,本文是一些基础问题。例如:为什么学习Python、通过什么途径学习Python、谈谈对Python和其他语言的区别、简述解释型和编译型编程语言,等等。

    2022年1月18日
    102
  • css下拉菜单制作原理

    css下拉菜单制作原理css 下拉菜单制作原理详解 2009 06 2414 51css 下拉菜单制作原理这几天一直研究下拉式菜单的 css 制作方法 以前没有仔细研究 感觉很难 看不懂 云雾缭绕的 现在发现用 css 不仅有不错的效果的 而且简单易用 基本原理就是 先把下拉菜单的 ul 隐藏 再设置主链接的鼠标移过状态 navli hoverul nbsp nbsp 让隐藏的下拉菜单显示出来 页面代码 信息资讯博客影音相册

    2026年3月16日
    3
  • 运筹学单纯形法求解线性规划问题_运筹学单纯形法计算步骤

    运筹学单纯形法求解线性规划问题_运筹学单纯形法计算步骤线性规划是研究在一组线性不等式或等式约束下使得某一线性目标函数取最大(或最小)的极值问题。

    2022年8月6日
    11
  • Java IO 和 NIO的区别

    Java IO 和 NIO的区别主要区别IONIO面向字节流面向缓冲区阻塞基于Selector的非阻塞JavaIO和NIO的主要区别体现在以上两个方面,以下详细说明这些区别的具体含义。面向流和面向缓冲区这个概念和编程方法中的面向过程、面向对象类似。JavaIO是面向流的而JavaNIO是面向缓冲区的。在JavaIO中读取数据和写入数据是面向流(Stream)的,这表示当我们从流中读取数据,写入数据时也将其写入流,流…

    2022年5月22日
    39

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号