java - 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"));
但是当我进行所有这些编辑时,正常的多聊天进度被破坏了,我的错在哪里?为什么一切都坏了?
解决方案
好问题!要回答您提出的问题,您应该维护一个Map
用户到他们的 Socket 连接,这样对于 DM,您只需选择要发送消息的用户。您还需要一个消息传递协议(见下文)
...但是我必须告诉您,在当今时代使用 Sockets 和 SocketServer 类就像重新发明轮子一样。开始做聊天服务器的地方是使用网络套接字协议。即使在这种情况下,您也可能想要定义一个消息协议(就像我所做的那样 - 我使用 JSON 和消息类型创建了一个消息协议,其中 websocket 事件 onMessage 中的字符串消息首先被解析为一个对象)
在所有平台上都有支持 WS 的实现:java、.net、python、php 等。这应该是您的起点。
- - 更新 - -
我明白你来自哪里。为了帮助您理解 Sockets / ServerSockets,这里有一些指针和资源
- DatagramSockets (aka UDP):这是一种不同于常规 TCP 的传输协议,由 Shockwave 和 Flash 使用,也是 Flash 存在问题的根本原因。我强烈建议不要这样做
- 数据和对象输入/输出流:“数据”流仅适用于 Java(无法连接到构建在其他平台上的技术)。对象流是相似的,除了您通过流传输实际的整个对象(也仅限 Java)没有人*(几乎没有人)再使用这些。
- SocketException: 使用 java.net.[Server]Socket(s),您很可能会遇到此异常。当您在套接字上等待更多数据(通过 read / readLine 调用)并且套接字关闭时,就会发生这种情况。我花了很长时间才弄清楚这一点,但这个例外是你的朋友!当连接关闭(在客户端或服务器端)时,您会得到它。它允许在套接字上等待的线程唤醒,并允许您进行任何您需要做的清理工作。SocketException 是 IOException 的子类,所以你可能甚至没有意识到这是什么。但现在至少我已经警告过你
- Streams vs. Writers 和 Readers:Writers 和 Readers 用于将原始字节解释为 Java 字符和字符串。这是必要的,因为有多种文本格式(即 ascii、windows-xx、utf-8、utf-16)。Readers and Writers 帮助您以不同的文本格式读取和写入文本(以及从图像格式解释图像)。
- 缓冲写入器和流:这些用于低效率的读取和写入。对于写作,这意味着让您可以编写部分消息,并且在您准备好之前不要发送它。对于阅读,这意味着例如逐行阅读流,而不是一口气阅读所有内容。
- TUS:tjacobs/io - https://sourceforge.net/p/tus/code/HEAD/tree/tjacobs/io/这是我多年前在 SourceForge 上放置的 Java 库的旧集合,但这里有很多类与处理套接字有关。特别是,请参阅 SocketServerEx、DataFetcher、Main/App、Timeout 和 IOUtils。最重要的是,看看 DataFetcher,它是一个用于回调 I/O 监听的轻量级线程框架。
祝好运并玩得开心点!
推荐阅读
- javascript - 单击按钮,在主视图中的 foreach 语句中一次显示/隐藏部分视图结果
- amazon-web-services - 集成 Amazon SP API 以在 Google Sheet 上跟踪 FBA 库存
- php - Echo JSON -> PHP 中的数组
- android - 下载最新的安卓文档
- vba - VBA从PDF文件的Power Queries中提取数据
- iis - IIS 上的 Blazor Webassembly Brotli 和 Gzip 压缩
- flutter - 到达内部列表的结尾/开始时颤动嵌套列表滚动父级
- oauth-2.0 - TikTok 登录套件:状态=临时重定向,状态代码=307
- java - 如何在 Android 应用程序的 Here Maps 上显示兴趣点 (pois) 并与之交互?
- firebase - 如何在flutter firebase中将OTP发送到电子邮件以进行电子邮件验证