Java Socket实现简易多人聊天室传输聊天内容或文件

本文介绍了如何使用JavaSocket构建一个简易的多人聊天室,包括服务器端的监听与客户端连接、消息与文件的发送和接收。服务器端通过ServerSocket接受客户端连接,为每个客户端创建独立线程处理通信。客户端发送消息或文件到服务器,服务器广播给其他客户端。ChatClientHandler类处理客户端通信细节,包括发送和接收消息及文件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java Socket 是一种用于在网络上进行通信的机制,可以实现客户端和服务器之间的数据传输。在简易多人聊天室中,使用 Java Socket 实现聊天内容或文件的传输的原理如下:

  1. 服务器端启动:聊天室的服务器端在指定的端口上监听客户端的连接。它创建一个 ServerSocket 对象,并通过调用 accept() 方法等待客户端的连接请求。

  2. 客户端连接:聊天室的客户端通过创建一个 Socket 对象并指定服务器的地址和端口,与服务器建立连接。客户端和服务器之间的连接建立后,可以进行数据传输。

  3. 服务器端接收消息:当客户端连接到服务器后,服务器会为每个客户端创建一个独立的线程,用于处理与该客户端的通信。在服务器端的线程中,通过调用输入流的 readLine() 方法读取客户端发送的消息。服务器可以根据接收到的消息类型(例如普通消息或文件传输请求),采取相应的处理逻辑。

  4. 服务器端广播消息:服务器在接收到客户端发送的消息后,可以将该消息广播给其他连接到服务器的客户端。通过遍历保存所有客户端的列表,并调用相应客户端的输出流发送消息给客户端。

  5. 客户端发送消息:客户端可以通过输出流向服务器发送消息。客户端将消息写入输出流,然后通过网络发送给服务器。

  6. 客户端接收消息:客户端在一个独立的线程中通过输入流不断监听服务器端的消息。通过调用输入流的 readLine() 方法读取服务器端发送的消息,并进行相应的处理,如将消息显示在用户界面上。

  7. 文件传输:客户端可以通过输入流读取文件内容,并通过输出流将文件数据发送给服务器。服务器在接收到文件数据后,可以将文件广播给其他客户端。

以上就是使用 Java Socket 实现简易多人聊天室传输聊天内容或文件的基本原理。通过建立客户端和服务器之间的连接,以及使用输入流和输出流进行数据的读写,可以实现实时的聊天和文件传输功能。

---------------------------------------------------------------------------------------------------------------------------------

项目结构如下

 

Server类

import java.io.*;
import java.net.*;
import java.util.*;

public class ChatServer {
    private static final int PORT = 12345;
    private List<ChatClientHandler> clients;

    public ChatServer() {
        clients = new ArrayList<>();
    }

    public void start() {
        try {
            ServerSocket serverSocket = new ServerSocket(PORT);
            System.out.println("Server started on port " + PORT);

            while (true) {
                Socket clientSocket = serverSocket.accept();
                System.out.println("New client connected");

                ChatClientHandler handler = new ChatClientHandler(clientSocket, this);
                clients.add(handler);
                new Thread(handler).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void broadcastMessage(String message, ChatClientHandler sender) {
        for (ChatClientHandler client : clients) {
            if (client != sender) {
                client.sendMessage(message);
            }
        }
    }

    public void broadcastFile(String fileName, byte[] fileData, ChatClientHandler sender) {
        for (ChatClientHandler client : clients) {
            if (client != sender) {
                client.sendFile(fileName, fileData);
            }
        }
    }

    public void removeClient(ChatClientHandler client) {
        clients.remove(client);
        System.out.println("Client disconnected");
    }

    public static void main(String[] args) {
        ChatServer server = new ChatServer();
        server.start();
    }
}
import java.io.*;
import java.net.*;
import java.util.*;

public class ChatServer {
    private static final int PORT = 12345;
    private List<ChatClientHandler> clients;

    public ChatServer() {
        clients = new ArrayList<>();
    }

    public void start() {
        try {
            ServerSocket serverSocket = new ServerSocket(PORT);
            System.out.println("Server started on port " + PORT);

            while (true) {
                Socket clientSocket = serverSocket.accept();
                System.out.println("New client connected");

                ChatClientHandler handler = new ChatClientHandler(clientSocket, this);
                clients.add(handler);
                new Thread(handler).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void broadcastMessage(String message, ChatClientHandler sender) {
        for (ChatClientHandler client : clients) {
            if (client != sender) {
                client.sendMessage(message);
            }
        }
    }

    public void broadcastFile(String fileName, byte[] fileData, ChatClientHandler sender) {
        for (ChatClientHandler client : clients) {
            if (client != sender) {
                client.sendFile(fileName, fileData);
            }
        }
    }

    public void removeClient(ChatClientHandler client) {
        clients.remove(client);
        System.out.println("Client disconnected");
    }

    public static void main(String[] args) {
        ChatServer server = new ChatServer();
        server.start();
    }
}
  • import语句导入了所需的Java类和接口。
  • ChatServer类是服务器端的主类。
  • PORT常量指定了服务器将要监听的端口号。
  • clients是一个存储ChatClientHandler对象的列表,用于跟踪连接到服务器的客户端。
  • start()方法启动服务器的主要逻辑。
  • 创建一个ServerSocket对象,绑定到指定的端口,并开始监听客户端连接请求。
  • serverSocket.accept()方法等待客户端连接并返回一个Socket对象表示与客户端的通信。
  • 为每个客户端连接创建一个ChatClientHandler对象,并将其添加到clients列表中。
  • 启动一个新的线程来处理每个客户端连接的消息传输。
  • broadcastMessage()方法用于向所有连接的客户端广播消息,但不发送给消息的发送者。
  • 遍历clients列表中的每个ChatClientHandler对象,并调用其sendMessage()方法发送消息。
  • broadcastFile()方法用于向所有连接的客户端广播文件内容,但不发送给文件的发送者。
  • 遍历clients列表中的每个ChatClientHandler对象,并调用其sendFile()方法发送文件内容。
  • removeClient()方法从clients列表中移除指定的ChatClientHandler对象,表示该客户端已断开连接。

这个类是 ChatServer 中的一个线程类,用于处理与客户端的通信。它实现了 Runnable 接口,通过在独立的线程中运行来处理客户端的消息。

ChatClientHandler 类的作用是与客户端建立连接并处理与客户端的通信,包括接收和发送消息以及接收和发送文件。通过该类,服务器可以与多个客户端同时进行通信并实现聊天室的功能。

ChatClientHandler 类

package com.qqcc.server;

/**
 * @author Scyth1
 * @create 2023/7/17 16:28
 */
import java.io.*;
import java.net.*;
import java.util.*;

public class ChatClientHandler implements Runnable {
    private Socket clientSocket;
    private BufferedReader reader;
    private PrintWriter writer;
    private ChatServer server;

    public ChatClientHandler(Socket clientSocket, ChatServer server) {
        this.clientSocket = clientSocket;
        this.server = server;

        try {
            reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            writer = new PrintWriter(clientSocket.getOutputStream(), true);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        try {
            String message;
            while ((message = reader.readLine()) != null) {
                if (message.startsWith("/file ")) {
                    String fileName = message.substring(6);
                    int fileSize = Integer.parseInt(reader.readLine());
                    byte[] fileData = new byte[fileSize];

                    // 使用 DataInputStream 读取字节数据
                    DataInputStream dis = new DataInputStream(clientSocket.getInputStream());
                    dis.readFully(fileData);

                    server.broadcastFile(fileName, fileData, this);
                } else {
                    server.broadcastMessage(message, this);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            server.removeClient(this);
            close();
        }
    }

    public void sendMessage(String message) {
        writer.println(message);
    }

    public void sendFile(String fileName, byte[] fileData) {
        writer.println("/file " + fileName);
        writer.println(fileData.length);

        try {
            // 使用 DataOutputStream 发送字节数据
            DataOutputStream dos = new DataOutputStream(clientSocket.getOutputStream());
            dos.write(fileData, 0, fileData.length);
            dos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void close() {
        try {
            if (reader != null) {
                reader.close();
            }
            if (writer != null) {
                writer.close();
            }
            if (clientSocket != null) {
                clientSocket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 ChatClient类(客户端)

import java.io.*;
import java.net.*;
import java.util.*;

public class ChatClient {
    private static final String SERVER_HOST = "localhost";
    private static final int SERVER_PORT = 12345;

    public static void main(String[] args) {
        try {
            Socket socket = new Socket(SERVER_HOST, SERVER_PORT);

            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);

            // 创建并启动接收消息的线程
            Thread receiverThread = new Thread(new MessageReceiver(reader));
            receiverThread.start();

            // 读取用户输入并发送消息或文件给服务器
            BufferedReader userInputReader = new BufferedReader(new InputStreamReader(System.in));
            String userInput;
            while ((userInput = userInputReader.readLine()) != null) {
                if (userInput.startsWith("/sendfile ")) {
                    String filePath = userInput.substring(10);
                    sendFile(filePath, writer);
                } else {
                    writer.println(userInput);
                }
            }

            // 关闭资源
            writer.close();
            reader.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void sendFile(String filePath, PrintWriter writer) {
        try {
            File file = new File(filePath);
            String fileName = file.getName();

            FileInputStream fileInputStream = new FileInputStream(file);
            byte[] fileData = new byte[(int) file.length()];
            fileInputStream.read(fileData);

            writer.println("/file " + fileName);
            writer.flush();
            writer.println(fileData.length);
            writer.flush();
            writer.println(Base64.getEncoder().encodeToString(fileData));
            writer.flush();

            fileInputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static class MessageReceiver implements Runnable {
        private BufferedReader reader;

        public MessageReceiver(BufferedReader reader) {
            this.reader = reader;
        }

        @Override
        public void run() {
            try {
                String message;
                while ((message = reader.readLine()) != null) {
                    if (message.startsWith("/file ")) {
                        String fileName = message.substring(6);
                        receiveFile(fileName, reader);
                    } else {
                        System.out.println(message);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        private void receiveFile(String fileName, BufferedReader reader) {
            try {
                int fileSize = Integer.parseInt(reader.readLine());
                String fileData = reader.readLine();
                byte[] fileBytes = Base64.getDecoder().decode(fileData);

                FileOutputStream fileOutputStream = new FileOutputStream(fileName);
                fileOutputStream.write(fileBytes);
                fileOutputStream.close();

                System.out.println("Received file: " + fileName);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
  • import语句导入了所需的Java类和接口。
  • ChatClient类是客户端的主类。
  • SERVER_HOSTSERVER_PORT常量分别指定服务器的主机名和端口号。
  • main()方法是程序的入口点。
  • 创建一个接收消息的线程,并通过Thread类进行管理和启动。
  • MessageReceiver类是一个实现Runnable接口的内部类,用于在单独的线程中接收服务器发送的消息。
  • 从标准输入读取用户输入的消息。
  • 使用BufferedReaderInputStreamReader从标准输入流(System.in)读取用户输入。
  • 循环读取用户输入,直到用户输入为null(按下Ctrl + D或输入结束标记)。
  • 如果用户输入以"/sendfile "开头,则解析文件路径,并调用sendFile()方法发送文件给服务器。
  • 否则,将用户输入的消息通过PrintWriterprintln()方法发送给服务器。
  • sendFile()方法用于发送文件给服务器。
  • 解析文件路径并获取文件名。
  • 使用FileInputStream读取文件的字节数据。
  • 将文件名、文件大小和文件内容依次发送给服务器,通过PrintWriterprintln()方法发送。
  • 使用Base64编码将文件内容转换为字符串形式发送。
  • MessageReceiver类实现了Runnable接口,用于在单独的线程中接收服务器发送的消息。
  • run()方法是线程的执行逻辑。
  • 通过BufferedReaderreadLine()方法从服务器接收消息。
  • 如果接收到的消息以"/file "开头,则解析文件名,并调用receiveFile()方法接收文件内容。
  • 否则,将接收到的消息打印到控制台。
  • receiveFile()方法用于接收文件内容。
  • 解析文件大小和文件内容,并进行Base64解码。
  • 使用FileOutputStream将文件内容写入到指定文件中。
  • 打印接收到的文件名到控制台。

客户端2

package com.qqcc.Client;

/**
 * @author Scyth1
 * @create 2023/7/17 16:34
 */
import java.io.*;
import java.net.*;
import java.util.*;

public class ChatClient1 {
    private static final String SERVER_HOST = "localhost";
    private static final int SERVER_PORT = 12345;

    public static void main(String[] args) {
        try {
            Socket socket = new Socket(SERVER_HOST, SERVER_PORT);

            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);

            // 创建并启动接收消息的线程
            Thread receiverThread = new Thread(new MessageReceiver(reader));
            receiverThread.start();

            // 读取用户输入并发送消息或文件给服务器
            BufferedReader userInputReader = new BufferedReader(new InputStreamReader(System.in));
            String userInput;
            while ((userInput = userInputReader.readLine()) != null) {
                if (userInput.startsWith("/sendfile ")) {
                    String filePath = userInput.substring(10);
                    sendFile(filePath, writer);
                } else {
                    writer.println(userInput);
                }
            }

            // 关闭资源
            writer.close();
            reader.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void sendFile(String filePath, PrintWriter writer) {
        try {
            File file = new File(filePath);
            String fileName = file.getName();

            FileInputStream fileInputStream = new FileInputStream(file);
            byte[] fileData = new byte[(int) file.length()];
            fileInputStream.read(fileData);

            writer.println("/file " + fileName);
            writer.flush();
            writer.println(fileData.length);
            writer.flush();
            writer.println(Base64.getEncoder().encodeToString(fileData));
            writer.flush();

            fileInputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static class MessageReceiver implements Runnable {
        private BufferedReader reader;

        public MessageReceiver(BufferedReader reader) {
            this.reader = reader;
        }

        @Override
        public void run() {
            try {
                String message;
                while ((message = reader.readLine()) != null) {
                    System.out.println(message);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

客户端3

package com.qqcc.Client;

/**
 * @author Scyth1
 * @create 2023/7/17 16:34
 */
import java.io.*;
import java.net.*;
import java.util.*;

public class ChatClient1 {
    private static final String SERVER_HOST = "localhost";
    private static final int SERVER_PORT = 12345;

    public static void main(String[] args) {
        try {
            Socket socket = new Socket(SERVER_HOST, SERVER_PORT);

            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);

            // 创建并启动接收消息的线程
            Thread receiverThread = new Thread(new MessageReceiver(reader));
            receiverThread.start();

            // 读取用户输入并发送消息或文件给服务器
            BufferedReader userInputReader = new BufferedReader(new InputStreamReader(System.in));
            String userInput;
            while ((userInput = userInputReader.readLine()) != null) {
                if (userInput.startsWith("/sendfile ")) {
                    String filePath = userInput.substring(10);
                    sendFile(filePath, writer);
                } else {
                    writer.println(userInput);
                }
            }

            // 关闭资源
            writer.close();
            reader.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void sendFile(String filePath, PrintWriter writer) {
        try {
            File file = new File(filePath);
            String fileName = file.getName();

            FileInputStream fileInputStream = new FileInputStream(file);
            byte[] fileData = new byte[(int) file.length()];
            fileInputStream.read(fileData);

            writer.println("/file " + fileName);
            writer.flush();
            writer.println(fileData.length);
            writer.flush();
            writer.println(Base64.getEncoder().encodeToString(fileData));
            writer.flush();

            fileInputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static class MessageReceiver implements Runnable {
        private BufferedReader reader;

        public MessageReceiver(BufferedReader reader) {
            this.reader = reader;
        }

        @Override
        public void run() {
            try {
                String message;
                while ((message = reader.readLine()) != null) {
                    System.out.println(message);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果图

 

 

 

 

Dimension ss = Toolkit.getDefaultToolkit().getScreenSize(); public ChatClient(){ super("登录聊天室"); pnlLogin = new JPanel(); this.getContentPane().add(pnlLogin); lblServer = new JLabel("服务器:"); lblPort = new JLabel("端口:"); lblName = new JLabel("用户名:"); lblPassword = new JLabel("口 令:"); tfServer = new JTextField(15); tfServer.setText("127.0.0.1"); tfPort = new JTextField(6); tfPort.setText("8000"); tfName = new JTextField(20); pwd = new JPasswordField(20); btnLogin = new JButton("登录"); btnRegister = new JButton("注册"); btnExit=new JButton("退出"); pnlLogin.setLayout(null); pnlLogin.setBackground(new Color(205,112,159)); lblServer.setBounds(40,35,50,30); tfServer.setBounds(90,35,102,25); lblPort.setBounds(195,35,35,30); tfPort.setBounds(230,35,55,25); lblName.setBounds(40,70,50,30); tfName.setBounds(90,70,195,25); lblPassword.setBounds(40,100,50,30); pwd.setBounds(90,100,195,25); btnLogin.setBounds(30,160,70,25); btnRegister.setBounds(130,160,70,25); btnExit.setBounds(230,160,70,25); pnlLogin.add(lblServer); pnlLogin.add(tfServer); pnlLogin.add(lblPort); pnlLogin.add(tfPort); pnlLogin.add(lblName); pnlLogin.add(tfName); pnlLogin.add(lblPassword); pnlLogin.add(pwd); pnlLogin.add(btnLogin); pnlLogin.add(btnRegister); pnlLogin.add(btnExit); //设置登录窗口 setResizable(false); setSize(320,260); setVisible(true); setLocation((ss.width-getWidth())/2,(ss.height-getHeight())/2); //为按钮注册监听 btnLogin.addActionListener(this); btnRegister.addActionListener(this); btnExit.addActionListener(this); addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e){ System.exit(0); } }); } //按钮监听响应 public void actionPerformed(ActionEvent ae){ Object source = ae.getSource(); if (source.equals(btnLogin)){ if (tfName.getText().equals("") || pwd.getPassword().equals("")) JOptionPane.showMessageDialog(null, "用户名密码不能为空"); else strServerIp = tfServer.getText(); login(); } if (source.equals(btnRegister)){ strServerIp = tfServer.getText(); this.dispose(); new Register(strServerIp,8000); } if (source == btnExit) { System.exit(0); } } public void login() { User data = new User(); data.name = tfName.getText(); data.password = new String(pwd.getPassword()); try { String str = InetAddress.getLocalHost().toString(); data.ip = " "+ str.substring(str.lastIndexOf("/"), str.length()); } catch (UnknownHostException ex) { Logger.getLogger(ChatClient.class.getName()).log(Level.SEVERE, null, ex); } try{ Socket sock = new Socket(strServerIp,8000); ObjectOutputStream os = new ObjectOutputStream(sock.getOutputStream()); os.writeObject((User) data); //读来自服务器socket的登录状态 BufferedReader br = new BufferedReader(new InputStreamReader(sock.getInputStream())); String status = br.readLine(); if (status.equals("登陆成功")){ new ChatRoom((String)data.name,strServerIp); this.dispose(); //关闭流对象 os.close(); br.close(); sock.close(); } else{ JOptionPane.showMessageDialog(null, status); os.close(); br.close(); sock.close(); } } catch (ConnectException e1){ JOptionPane.showMessageDialog(null, "连接到制定服务器失败!"); } catch (InvalidClassException e2) { JOptionPane.showMessageDialog(null, "类错误!"); } catch (NotSerializableException e3) { JOptionPane.showMessageDialog(null, "对象未序列化!"); } catch (IOException e4) { JOptionPane.showMessageDialog(null, "不能写入到指定服务器!"); } } public static void main(String arg[]){ new ChatClient(); } }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值