本文实例为大家分享了 Java Socket实现多人 聊天系统 的具体代码,供大家参考,具体内容如下
前言
GitHub地址
开发环境: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)
登录界面(接着运行Client.java,运行一次生成一个登录界面)
这里我还没有实现注册功能,登录的用户名随意(不为空即可),密码是123。
2.2 登录成功
2.3 发送信息
2.4 发送文件
打开文件我设置了默认路径是在桌面。接受文件需要先在桌面创建一个名为[接受文件]的文件夹,用于存放所有用户接收的文件。
如果出现无法发送文件,应该是ClientFileThread.java那里的路径问题,路径包括了电脑用户的名字,比如我的是[Samven],可以试试修改为自己的真实路径。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
原文链接:https://blog.csdn.net/qq_42780289/article/details/102556693
查看更多关于Java Socket实现多人聊天系统的详细内容...