Вот что происходит: когда клиент присоединяется к чату или покидает его, сервер отправляет сообщение, содержащее список клиентов, всем подключенным клиентам. Я пытаюсь обновить список пользователей, отображаемый в графическом интерфейсе на стороне клиента, на основе этого сообщения. Однако я заметил, что иногда список участников четвертого или пятого клиента отображается пустым в их графическом интерфейсе, а иногда список не обновляется правильно при присоединении нового клиента.
Я' Я обдумываю, есть ли более эффективный способ отправить список клиентов непосредственно с сервера и добавить их в список участников клиента, полностью минуя необходимость в сообщении «/members».
Я внедрил правило, исключающее отображение списка «/members» в области чата, поскольку я намерен добавлять имена непосредственно в список участников в графическом интерфейсе на стороне клиента. Я также отделил графический интерфейс от серверной логики, чтобы гарантировать, что графический интерфейс индивидуален для каждого клиента.
Я не уверен, что этот подход является наиболее эффективным, и я Мне нужен совет о том, как решить эти проблемы и улучшить функциональность моего приложения.
Заранее благодарим вас за любую помощь, которую вы можете оказать!
Клиентская часть кода:
Код: Выделить всё
package TCP;
import javax.swing.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Base64;
public class Client implements Runnable {
private Socket client;
private BufferedReader in;
private PrintWriter out;
private int port;
private String ip;
private boolean done;
private GuiChatt guiChatt;
public Client(GuiChatt guiChatt, String ip, int port) {
this.guiChatt = guiChatt;
this.ip = ip;
this.port = port;
}
@Override
public void run(){
try {
client = new Socket(ip, port);
out = new PrintWriter(client.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(client.getInputStream()));
done = true;
String userName = guiChatt.getUserName();
out.println(userName); //send user join name
InputHandler inHandler = new InputHandler(in);
Thread thread = new Thread(inHandler);
thread.start();
String inMessage;
while (done && (inMessage = in.readLine()) != null) {
guiChatt.addMessage(inMessage);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
shutdown();
}
}
private void shutdown() {
done = true;
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
if (client != null && !client.isClosed()) {
client.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void sendMessageToServer(String message) {
if (out != null) {
System.out.println("(sendMessage to server Client class)Attempting to send message: " + message);
out.println(message);
if (out.checkError()) {
System.out.println("(sendMessage to server Client class) Message not sent: " + message);
} else {
System.out.println("(sendMessage to server Client class) Message sent: " + message);
}
}
}
//server message listener gets messeages from the server that other clients have sent
class InputHandler implements Runnable { //asks constantly for new line inputs
BufferedReader in;
public InputHandler(BufferedReader in) {
this.in = in;
}
@Override
public void run() {
try {
String inMessage;
while (done && (inMessage = in.readLine()) != null) {
if (inMessage.startsWith("/members")) {
// Handle /members message
String[] members = inMessage.substring(9).split(",");
System.out.println("Received members list: " + String.join(", ", members)); // Added logging
SwingUtilities.invokeLater(() -> {
guiChatt.updateMembersList(members);
});
} else if (inMessage.startsWith("/image")) {
// Handle image messages
String imageData = inMessage.substring(7);
byte[] decodedImage = Base64.getDecoder().decode(imageData);
ImageIcon receivedImage = new ImageIcon(decodedImage);
guiChatt.addImage(receivedImage);
} else if (!inMessage.startsWith("/")) { // Skip messages starting with "/"
// Handle regular chat messages
guiChatt.addMessage(inMessage);
}
}
} catch (IOException e) {
e.printStackTrace();
shutdown();
}
}
}
}
```
Код: Выделить всё
private JList membersList;
////
membersListModel = new DefaultListModel();
membersList = new JList(membersListModel);
membersList.setBackground(new Color(46, 46, 46));
membersList.setForeground(Color.WHITE);
membersList.setFont(new Font("Arial", Font.PLAIN, 14));
JScrollPane membersScrollPane = new JScrollPane(membersList);
membersScrollPane.setBorder(null);
membersScrollPane.setPreferredSize(new Dimension(150, messagesScrollPane.getHeight()));
////
public void updateMembersList(String[] members) {
SwingUtilities.invokeLater(() -> {
membersListModel.clear();
for (String member : members) {
membersListModel.addElement(member);
}
});
}
Код: Выделить всё
package TCP;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Server implements Runnable {
private ServerSocket serverSocket;
private int port;
private List connectionHandlerList; //this broadcast to all clientsList
private ExecutorService pool;
private Set clientsList;
private boolean done;
public Server(int port) {
this.port = port;
connectionHandlerList = new CopyOnWriteArrayList();
clientsList = Collections.synchronizedSet(new HashSet());
done = false;
}
@Override
public void run(){
try {
serverSocket = new ServerSocket(port);
pool = Executors.newCachedThreadPool(); //maybe add this to the constructor later on for debugging
done = true;
while (done) {
Socket client = serverSocket.accept();
ConnectionHandler handler = new ConnectionHandler(client, this);//for each new client
connectionHandlerList.add(handler);
pool.execute(handler);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
shutdown();
}
}
public void broadcast(String message){
synchronized (connectionHandlerList){
for(ConnectionHandler connectionHandler : connectionHandlerList){
if (connectionHandler != null){
connectionHandler.sendMessage(message);
}
}
}
}
public synchronized void newClient(String client, ConnectionHandler handler){
clientsList.add(client);
broadcast(client + " joined the chat");
updateMembers();
}
public synchronized void leftClient(String client, ConnectionHandler handler) {
clientsList.remove(client);
broadcast(client + " left the chat");
updateMembers();
}
public void updateMembers() {
StringBuilder memberList = new StringBuilder("/members ");
synchronized (clientsList) {
for (String client : clientsList) {
memberList.append(client).append(",");
}
}
if (memberList.length() > 0) {
memberList.setLength(memberList.length() - 1); // Remove the trailing comma
}
System.out.println("Broadcasting members: " + memberList.toString());
broadcast(memberList.toString());
}
public void shutdown() {
done = true;
pool.shutdown();
try {
if (serverSocket != null && !serverSocket.isClosed()) {
serverSocket.close();
}
synchronized (connectionHandlerList) {
for (ConnectionHandler connectionHandler : connectionHandlerList) {
connectionHandler.shutdown();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
//the class below is created for each client
class ConnectionHandler implements Runnable {
private Socket client;
private BufferedReader in; //gets the stream from socket, meaning when client send
private PrintWriter out; //writes out to the client
private String userName;
private final Server server;
public ConnectionHandler(Socket client, Server server) {
this.client = client;
this.server = server;
}
@Override
public void run() {
try {
out = new PrintWriter(client.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(client.getInputStream()));
//ADD FUNCTIONALITY FOR SENDING AND RECIVNG HERE
userName = in.readLine(); //to get input from streams
server.newClient(userName, this);
//add each client to the list
String message;
while ((message = in.readLine()) != null) {
if (message.startsWith("/image ")) {
server.broadcast("/image " + message.substring(7));
} else {
server.broadcast(userName + ": " + message);
}
}
} catch (IOException e){
e.printStackTrace();
} finally {
//server.leftClient(client, this);
shutdown();
}
}
public void sendMessage(String message) {
out.println(message);
}
public void shutdown() {
try {
if(!client.isClosed()){
client.close();
}
server.leftClient(userName, this);
} catch (IOException e) {
//throw new RuntimeException(e);
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Server server = new Server(12345);
new Thread(server).start();
}
}
Я попытался реализовать функцию обновления, которая периодически проверяет список подключенных клиентов каждые 10 секунд и соответствующим образом обновляет список участников. Однако я не уверен в его практичности и в том, правильно ли он синхронизирован. Я застрял в этой проблеме несколько дней и, несмотря на поиск решения, мне не удалось найти конкретное решение.
Подробнее здесь: https://stackoverflow.com/questions/785 ... plentation