Java|Java Socket实现多人聊天系统
本文实例为大家分享了Java Socket实现多人聊天系统的具体代码,供大家参考,具体内容如下
前言
GitHub地址
开发环境:Eclipse Java 2019-06
注意:本项目只在单主机运行调试过,没试过在局域网和不同主机之间接发消息和文件(估计不行),有需要的自行查阅资料。
一、多人聊天系统
1.1 客户端
Login.java:登录界面
// Login.javapackage 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.javapackage 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.javapackage 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并且用户名长度大于等于1chatView = 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.javapackage 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.javapackage 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.javapackage exp5; import java.io.*; import java.net.*; import java.util.*; public class Server{ static ServerSocket server = null; static Socket socket = null; static List list = new ArrayList(); // 存储客户端 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.javapackage 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 list = new ArrayList(); // 存储客户端 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)
文章图片
登录界面(接着运行Client.java,运行一次生成一个登录界面)
文章图片
这里我还没有实现注册功能,登录的用户名随意(不为空即可),密码是123。
2.2 登录成功
文章图片
文章图片
2.3 发送信息
文章图片
2.4 发送文件
打开文件我设置了默认路径是在桌面。接受文件需要先在桌面创建一个名为“接受文件”的文件夹,用于存放所有用户接收的文件。
文章图片
如果出现无法发送文件,应该是ClientFileThread.java那里的路径问题,路径包括了电脑用户的名字,比如我的是“Samven”,可以试试修改为自己的真实路径。
【Java|Java Socket实现多人聊天系统】以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
推荐阅读
- JAVA(抽象类与接口的区别&重载与重写&内存泄漏)
- 关于QueryWrapper|关于QueryWrapper,实现MybatisPlus多表关联查询方式
- MybatisPlus使用queryWrapper如何实现复杂查询
- python学习之|python学习之 实现QQ自动发送消息
- 事件代理
- 孩子不是实现父母欲望的工具——林哈夫
- opencv|opencv C++模板匹配的简单实现
- Java|Java OpenCV图像处理之SIFT角点检测详解
- Node.js中readline模块实现终端输入
- java中如何实现重建二叉树