Логика, вроде как, реализована верно, но валидатор не принимает. Куда копать?
package com.javarush.task.task30.task3008;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
//основной класс сервера
//Сервер должен поддерживать множество соединений с разными клиентами одновременно.
//Это можно реализовать с помощью следующего алгоритма:
//- Сервер создает серверное сокетное соединение.
//- В цикле ожидает, когда какой-то клиент подключится к сокету.
//- Создает новый поток обработчик Handler, в котором будет происходить обмен сообщениями с клиентом.
//- Ожидает следующее соединение.
public class Server {
//ключ - имя клиента, а значение - соединение с ним, для потокобезопасности конкарент.
private static Map<String, Connection> connectionMap = new ConcurrentHashMap<>();
//должен реализовывать протокол общения с клиентом.
private static class Handler extends Thread {
private Socket socket;
public Handler (Socket socket) {
this.socket = socket;
}
public void run() {
// super.run();
ConsoleHelper.writeMessage("Connection established with address: " + socket.getRemoteSocketAddress());
String userName = null;
try (Connection connection = new Connection(socket)){
//Вызывать метод, реализующий рукопожатие с клиентом, сохраняя имя нового клиента.
userName = serverHandshake(connection);
// Рассылать всем участникам чата информацию об имени присоединившегося участника
sendBroadcastMessage(new Message(MessageType.USER_ADDED, userName));
// Сообщать новому участнику о существующих участниках.
notifyUsers(connection, userName);
//Запускать главный цикл обработки сообщений сервером
serverMainLoop(connection, userName);
} catch (ClassNotFoundException | IOException e) {
ConsoleHelper.writeMessage("В Handler произошла ошибка при обмене данными с удаленным адресом");
// e.printStackTrace();
} finally {
//После того как все исключения обработаны, если п.11.3 отработал и возвратил нам имя,
//мы должны удалить запись для этого имени из connectionMap и разослать всем остальным участникам сообщение
//с типом USER_REMOVED и сохраненным именем.
if (userName != null) {
connectionMap.remove(userName);
sendBroadcastMessage(new Message(MessageType.USER_REMOVED, userName));
// ConsoleHelper.writeMessage("Соединение с удаленным адресом закрыто");
}
}
ConsoleHelper.writeMessage("Соединение с удаленным адресом закрыто");
}
private String serverHandshake(Connection connection) throws IOException, ClassNotFoundException {
Message answer;
while (true) {
//Сформировать и отправить команду запроса имени пользователя
connection.send(new Message(MessageType.NAME_REQUEST));
//Получить ответ клиента
answer = connection.receive();
//Достать из ответа имя,
if (answer.getType() == MessageType.USER_NAME) {
//проверить, что оно не пустое
if (!answer.getData().isEmpty()) {
//и пользователь с таким именем еще не подключен (используй connectionMap)
if (!connectionMap.containsKey(answer.getData())) {
break;
}
}
}
}
//После успешного проведения всех проверок,
//метод serverHandshake должен добавлять новую пару (имя, соединение) в connectionMap
connectionMap.put(answer.getData(), connection);
//и отправлять сообщение о том, что имя было принято.
connection.send(new Message(MessageType.NAME_ACCEPTED));
//должен возвращать имя нового клиента с которым было установлено соединение.
return answer.getData();
}
//должен отправлять сообщение message всем соединениям из connectionMap.
//Если при отправке сообщение произойдет исключение IOException, нужно отловить его и
//сообщить пользователю, что не смогли отправить сообщение.
public static void sendBroadcastMessage(Message message) {
for (Map.Entry<String, Connection> pair : connectionMap.entrySet()) {
try {
pair.getValue().send(message);
} catch (IOException e) {
System.out.println("Message not sended. Error: " + e.getMessage());
}
}
}
//отправка клиенту (новому участнику) информации об остальных клиентах (участниках) чата
//connection - соединение с участником, которому будем слать информацию, а userName - его имя.
private void notifyUsers(Connection connection, String userName) throws IOException {
for (Map.Entry<String, Connection> entry : connectionMap.entrySet()) {
if (!entry.getKey().equals(userName))
connection.send(new Message(MessageType.USER_ADDED, entry.getKey()));
}
}
//must принимать сообщение клиента
private void serverMainLoop(Connection connection, String userName) throws IOException, ClassNotFoundException {
Message answer;
while (true) {
answer = connection.receive();
//Если сообщение имеет тип MessageType.TEXT, то должно быть отправлено новое сообщение всем участникам чата
if (answer.getType() == MessageType.TEXT) {
//текстовое сообщение путем конкатенации: имени клиента, двоеточия, пробела и текста сообщения.
//Например, если мы получили сообщение с текстом "привет чат" от пользователя "Боб",
// то нужно сформировать сообщение "Боб: привет чат".
sendBroadcastMessage(new Message(MessageType.TEXT, String.format("%s: %s", userName, answer.getData())));
} else {
ConsoleHelper.writeMessage("Message wrong!");
}
}
}
}
public static void main(String[] args) {
int portNumber;
try {
System.out.println("Enter server socket port: ");
portNumber = ConsoleHelper.readInt();
ServerSocket serverSocket = new ServerSocket(portNumber);
System.out.println("ServerSocket created on port №" + portNumber + ". Server running.");
while (true) {
try {
new Handler(serverSocket.accept()).start();
} catch (Exception e) {
serverSocket.close();
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}