Когда вы работаете инженером-программистом или разработчиком, это только вопрос времени, так как вы сталкиваетесь с большим врагом: асинхронным поведением! Вы можете найти его в связи между клиентом и сервером, или, возможно, он включен в язык программирования или в структуру, которую вы используете.
Веб-сайты: новая надежда
Во время моего текущего проекта мы (я и моя команда) столкнулись со следующей проблемой: после определенного действия пользователя приложение должно предотвращать все остальные действия и ждать OK / KO с сервера. Было неизвестно, сколько времени может потребоваться серверу для отправки ответа, но требование было ясным: независимо от того, сколько времени пользователь должен дождаться ответа или позвонить в службу поддержки клиентов, если он считает, что это займет много времени. После небольшого обсуждения мы решили попробовать реализовать веб-сайт, чтобы позволить клиенту ждать сообщения (даже вечно, если это необходимо).
Что такое веб-карман?
Я не хочу беспокоить вас информацией, которую вы можете получить самостоятельно из более авторитетных источников. Проще говоря, Websocket – это протокол, который обеспечивает полнодуплексную связь по протоколу TCP, позволяя как клиенту, так и серверу отправлять/получать сообщения друг от друга и управлять событиями на основе полученного сообщения.
Теперь внешний интерфейс – это чисто React + машинопись, в то время как внутренний интерфейс написан на Java в рамках OSGi, поэтому невозможно использовать простые решения, такие как socket.io это позволяет deleoper использовать одну и ту же технологию как на FE, так и на BE.
Давайте кодировать вместе – Интерфейс
Поскольку я отвечал за переднюю часть, я сначала опишу свой код реакции. Еще одним требованием, которое следует иметь в виду, было то, что веб-сайт открывается при запуске приложения. Поэтому я решил использовать ref крюк для управления объектом WebSocket и проверки, закрыт он или открыт, и логическое значение Должно быть сохранено , чтобы включить/отключить функцию, которая поддерживает соединение во время ожидания ответа:
const connection = useRef(); const shouldKeepWSAlive = useRef (false);
После этого нам нужно узнать, какое событие запускает веб-сайт. Теперь у меня была эта переменная под названием ждет ли что-то , что отвечает за блокировку приложения, как сказано до поэтому я решил использовать использовать эффект хук для управления открытием веб-сайта ( что такое эффект использования? )
useEffect(() => {
if (!(connection &&
connection.current &&
connection.current.readyState === 1))
{
connection.current = new WebSocket("ws://path-to-websocket");
connection.current.onopen = () => {
//do something, maybe just log that the websocket is open;
}
connection.current.onclose = () => {
//do something, maybe just log that the websocket is closed;
};
connection.current.onmessage = (e) => {
aFunction();
};
}
}, [dependencies]);
Просто небольшое объяснение:
- оператор if вверху помогает мне проверить, открыто ли уже соединение;
- если соединение не открыто, код блокируется внутри, если открыть новое соединение;
- onopen и onclose – это события по умолчанию, которые запускаются при запуске и закрытии соединения;
- onmessage – важная часть: это событие, которое запускается при получении сообщения на интерфейсе;
- функция() – это моя пользовательская функция, которая выполняет логику, которую я хочу;
- с правильными зависимостями Websocket открывается при запуске приложения;
- поскольку даже у Websocket есть тайм-аут, вам может потребоваться снова открыть его.
Однако, если серверу требуется много времени для отправки сообщения, во время ожидания Websocket может истечь время ожидания и закрыться, поэтому я добавил простую функцию keepAlive() таким образом:
const keepAlive = useCallback(() => {
if (shouldKeepWSAlive.current) {
if (connection.current !== undefined &&
connection.current !== null &&
connection.current.readyState === 1)
{
connection.current.send("");
}
setTimeout(() => {
keepAlive();
}, 20000);
}
}, []);
useEffect(() => {
if (isWaitingVendi) {
shouldKeepWSAlive.current = true;
keepAlive();
} else {
shouldKeepWSAlive.current = false;
}
}, [isWaitingVendi, keepAlive]);
После этого мой веб-сайт работал и работал хорошо.
Давайте кодировать вместе – Бэкэнд
В этом разделе я кратко опишу Java-часть Websocket. BE управлялся другим членом команды, поэтому я не буду вставлять подробные объяснения, но я попросил его написать специальный пост.
Мы разрабатываем в OSGi-фреймворке и мы используем Причал . Список необходимых импортных товаров довольно длинный (я спрятал некоторые…):
import com.google.gson.Gson; import it.hiddenstuff.common.topic.TopicConstants; import org.eclipse.jetty.websocket.api.RemoteEndpoint; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; import org.osgi.service.event.Event; import org.osgi.service.event.EventAdmin; import org.osgi.service.event.EventConstants; import org.osgi.service.event.EventHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Hashtable;
и здесь у вас есть объявление класса с правильными аннотациями и конструктором:
@WebSocket
public class SellWebSocket implements EventHandler {
public SellWebSocket() {
BundleContext bundleContext = FrameworkUtil.
getBundle(SellWebSocket.class).getBundleContext();
Hashtable stringStringHashMap = new Hashtable<>();
stringStringHashMap.
put( EventConstants.EVENT_TOPIC , TopicConstants.TOPIC_END_SELL);
bundleContext.
registerService(EventHandler.class , this , stringStringHashMap);
}
}
Затем вам нужно добавить некоторые объявления для управления сеансом, журналами и конечными точками:
private Session session;
private RemoteEndpoint remote;
private Logger log = LoggerFactory.getLogger(getClass());
public Session getSession() {
return session;
}
public void setSession(Session session) {
this.session = session;
}
public RemoteEndpoint getRemote() {
return remote;
}
Что касается внешнего интерфейса, вам нужно слушать события ( открывать , закрывать , отправить , получить ):
@OnWebSocketConnect
public void onConnect(Session session) {
setSession(session);
this.remote = session.getRemote();
}
@OnWebSocketClose
public void onClose(int statusCode, String reason) {
this.session = null;
}
@OnWebSocketMessage
public void onText(String message) {
if (session == null) {
log.debug("null session");
// no connection, do nothing.
// this is possible due to async behavior
return;
}
//do something
}
/**
* Called by the {@link EventAdmin} service to notify the listener of an
* event.
*
* @param event The event that occurred.
*/
@Override
public void handleEvent(Event event) {
//do what you need to do
}
Выводы
До этого я был не совсем знаком со всеми этими вещами, и поиск в Google немного свел меня с ума, так как было трудно найти решение со всеми нужными характеристиками: иногда вы находите socket.io , иногда вы находите старые классы React, когда вам нужны крючки, и некоторые подобные проблемы. Во всяком случае, мне удалось собрать все вместе, выйдя с этим решением. Поскольку я не претендую на роль эксперта, не стесняйтесь комментировать и добавлять полезные предложения. Если вместо этого вы считаете, что эта статья полезна, я был бы очень рад узнать 😀
Оригинал: “https://dev.to/fpeluso/a-simple-websocket-between-java-and-react-5c98”