首页 > 解决方案 > Java 多客户端聊天服务器私信选项

问题描述

我是 stackoverflow 的新手,如果之前有人问过这种问题,但我快速搜索了一下,我找不到像我这样的标题,我很抱歉。我正在开发一个基于 Java 的多客户端聊天应用程序。我按照教程进行操作,我可以发送应用程序中的每个用户都可以看到的消息。但我想知道如何创建私人消息并将其发送给特定用户到聊天中。

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

public class ChatServer {
    private int port;

    private Set<String> userNames = new HashSet<>();
    private Set<UserThread> userThreads = new HashSet<>();

    public ChatServer(int port) {
        this.port = port;
    }

    public static void main(String[] args) {
        new ChatServer(9999).execute();
    }

    private void execute() {
        try {
            ServerSocket serverSocket = new ServerSocket(9999);
            System.out.println("Server is running");
            while (true) {
                Socket socket = serverSocket.accept();
                System.out.println("New user connected");
                UserThread newUser = new UserThread(socket, this);
                userThreads.add(newUser);
                newUser.start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void addUserName(String s) {
        this.userNames.add(s);
    }

    public void broadcast(String serverMessage, UserThread excludeUser) {
        for (UserThread aUser : userThreads) {
            if (aUser != excludeUser)
                aUser.sendMessage(serverMessage);
        }
    }
}

上面的代码是我的服务器代码。

public void run() {
    Console console = System.console();
    String userName = console.readLine("Enter your username : ");
    writer.println(userName);
    String text;
    do {
        text = console.readLine("[" + userName + "]: ");
        if (text.startsWith("[")) {
            isTargeted = true;
            this.aimUserName = text.substring(text.indexOf("[") + 1, text.indexOf("]"));
            //System.out.println("Private Message to: " + aimUserName);
        } else {
            isTargeted = false;
        }
        writer.println(text);
    } while (!text.equals("bye"));

    try {
        socket.close();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

上面的代码是我写线程类的一部分。如您所见,如果消息以 '[name]' 部分开头,则“name”表示我们要发送私人消息的用户。通过这样做,我可以获得我想要发送私人消息的用户的名称,但我无法弄清楚如何将此消息广播给那个特定的用户。我相信我需要在 ChatServer 类中配置我的广播功能,但我真的不知道该怎么做。我应该遵循哪些步骤?

- 编辑 -

我一直在研究我的问题,并做了一些补充来解决我的问题。首先,我想我应该把我所拥有的一切都分享给你。我之前分享了我的 ChatServer 课程。我的其他课程是:

import java.io.IOException;
import java.net.Socket;

public class ChatClient {

    public static void main(String[] args) {
        new ChatClient().execute();
    }

    private void execute() {
        try {
            Socket socket = new Socket("localhost", 3);
            System.out.println("Connected to chat server");
            new ReadThread(socket, this).start();
            new WriteThread(socket, this).start();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

}

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

public class ReadThread extends Thread{
    private BufferedReader reader;
    private Socket socket;
    private ChatClient client;
    public ReadThread(Socket socket, ChatClient client) {
        this.socket = socket;
        this.client = client;

        InputStream input;
        try {
            input = this.socket.getInputStream();
            this.reader = new BufferedReader(new InputStreamReader(input));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    public void run() {
        while(true) {
            try {
                String response = this.reader.readLine();
                System.out.println("\n" + response);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                break;
            }
        }
    }
}

import java.net.Socket;
import java.io.*;
public class UserThread extends Thread {

    private Socket socket;
    private ChatServer server;
    PrintWriter writer = null;
    public String userName;
    public UserThread(Socket socket, ChatServer chatServer) {
        this.socket = socket;
        this.server = chatServer;
    }


    public void run() {
        try {
            InputStream input = socket.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(input));
            OutputStream output = socket.getOutputStream();
            writer = new PrintWriter(output,true);

            String userName = reader.readLine();
            this.userName = userName;
            server.addUserName(userName);
            String serverMessage = "New user connected: " + userName;
            server.broadcast(serverMessage,this);



            String clientMessage;
            do {
                clientMessage = reader.readLine();
                serverMessage = "[" + userName + "] : " + clientMessage;
                server.broadcast(serverMessage, this);      

            }while(!clientMessage.equals("bye"));



        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }


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


}

import java.net.*;
import java.io.*;
public class WriteThread extends Thread {
    private Socket socket;
    private ChatClient client;
    private PrintWriter writer;
    public WriteThread(Socket socket, ChatClient client) {
        this.socket = socket;
        this.client = client;
        OutputStream output;
        try {
            output = socket.getOutputStream();
            this.writer = new PrintWriter(output, true);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    public void run() {
        Console console = System.console();
        String userName = console.readLine("Enter your username : ");
        writer.println(userName);
        String text;
        do {
            text = console.readLine("[" + userName + "]: ");
            if(text.startsWith("[")){
                String aimUserName = text.substring(text.indexOf("[")+1,text.indexOf("]"));
                System.out.println("Private Message to: " + aimUserName);}
            writer.println(text);
        }while(!text.equals("bye"));

        /*do {
            text = console.readLine("[" + userName + "]: ");
            writer.println(text);
        }while(!text.equals("bye"));*/

        try {
            socket.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }


}

这些代码可以正常工作,我可以非常干净地进行多方聊天。但是在处理私人聊天内容时,我在 ChatServer 中添加了以下行:

public void privatebr(String serverMessage, String targetUserName){
        for(UserThread aUser: userThreads){
            if(aUser.userName == targetUserName)
                aUser.sendMessage(serverMessage);
        }

到 UserThread,我将部分编辑为:

String clientMessage;
            do {
                clientMessage = reader.readLine();
                serverMessage = "[" + userName + "] : " + clientMessage;
                if(clientMessage.startsWith("[")){
                    String targetUserName = clientMessage.substring(clientMessage.indexOf("[")+1,clientMessage.indexOf("]"));
                    serverMessage = "[" + userName + "] : " + clientMessage;
                    server.privatebr(serverMessage, targetUserName);
                }else{

                    server.broadcast(serverMessage, this);
                }


            }while(!clientMessage.equals("bye"));

但是当我进行所有这些编辑时,正常的多聊天进度被破坏了,我的错在哪里?为什么一切都坏了?

标签: java

解决方案


好问题!要回答您提出的问题,您应该维护一个Map用户到他们的 Socket 连接,这样对于 DM,您只需选择要发送消息的用户。您还需要一个消息传递协议(见下文)

...但是我必须告诉您,在当今时代使用 Sockets 和 SocketServer 类就像重新发明轮子一样。开始做聊天服务器的地方是使用网络套接字协议。即使在这种情况下,您也可能想要定义一个消息协议(就像我所做的那样 - 我使用 JSON 和消息类型创建了一个消息协议,其中 websocket 事件 onMessage 中的字符串消息首先被解析为一个对象)

在所有平台上都有支持 WS 的实现:java、.net、python、php 等。这应该是您的起点。

- - 更新 - -

我明白你来自哪里。为了帮助您理解 Sockets / ServerSockets,这里有一些指针和资源

  1. DatagramSockets (aka UDP):这是一种不同于常规 TCP 的传输协议,由 Shockwave 和 Flash 使用,也是 Flash 存在问题的根本原因。我强烈建议不要这样做
  2. 数据和对象输入/输出流:“数据”流仅适用于 Java(无法连接到构建在其他平台上的技术)。对象流是相似的,除了您通过流传输实际的整个对象(也仅限 Java)没有人*(几乎没有人)再使用这些。
  3. SocketException: 使用 java.net.[Server]Socket(s),您很可能会遇到此异常。当您在套接字上等待更多数据(通过 read / readLine 调用)并且套接字关闭时,就会发生这种情况。我花了很长时间才弄清楚这一点,但这个例外是你的朋友!当连接关闭(在客户端或服务器端)时,您会得到它。它允许在套接字上等待的线程唤醒,并允许您进行任何您需要做的清理工作。SocketException 是 IOException 的子类,所以你可能甚至没有意识到这是什么。但现在至少我已经警告过你
  4. Streams vs. Writers 和 Readers:Writers 和 Readers 用于将原始字节解释为 Java 字符和字符串。这是必要的,因为有多种文本格式(即 ascii、windows-xx、utf-8、utf-16)。Readers and Writers 帮助您以不同的文本格式读取和写入文本(以及从图像格式解释图像)。
  5. 缓冲写入器和流:这些用于低效率的读取和写入。对于写作,这意味着让您可以编写部分消息,并且在您准备好之前不要发送它。对于阅读,这意味着例如逐行阅读流,而不是一口气阅读所有内容。
  6. TUS:tjacobs/io - https://sourceforge.net/p/tus/code/HEAD/tree/tjacobs/io/这是我多年前在 SourceForge 上放置的 Java 库的旧集合,但这里有很多类与处理套接字有关。特别是,请参阅 SocketServerEx、DataFetcher、Main/App、Timeout 和 IOUtils。最重要的是,看看 DataFetcher,它是一个用于回调 I/O 监听的轻量级线程框架。

祝好运并玩得开心点!


推荐阅读