Серия Netty (3): сервер Netty отправляет сообщения клиенту.
Серия Netty (3): сервер Netty отправляет сообщения клиенту.

Обычно клиент активно отправляет контрольные сообщения только для поддержания соединения с сервером, в то время как другие сообщения часто требуют, чтобы сервер отправлял сообщения клиенту для получения.

Этапы реализации

  1. клиент При первом подключении к Серверу направьте это соединение в Map Сохранено, чтобы обеспечить потокобезопасность, вы можете использовать потокобезопасность. ConcurrentHashMap
  2. При отправке сообщения клиенту просматривайте идентификатор устройства. ConcurrentHashMap Найдите целевой канал клиентсоединять. Найдя его, сначала определите, активен ли канал. Если соединение активно, отправьте сообщение клиенту через этот канал. Если оно неактивно, отправьте его из него. Map Удалите информацию об этом канале.
  3. После отправки сообщения клиенту,Сервер получает информацию, возвращаемую клиентом, в обычном режиме.

Реализация кода

Уже представлено в двух предыдущих статьях netty Здесь представлен общий код платформы, только некоторые основные ключевые коды, а остальная часть кода не будет подробно описываться.

Направления:

  1. Серия Netty (1): Springboot интегрирует Netty и реализует собственные протоколы.
  2. Серия Netty (2): Решение проблем с распаковкой/погружением Netty

Создать новый ChannelMap Класс, сохраняется при первом подключении клиента. channel соединять. Когда сервер впоследствии отправит сообщение клиенту, он сначала Map Найдите соответствующее соединение канала сообщений клиента, а затем запишите сообщение в канал для отправки.

Язык кода:javascript
копировать
/**
 * @Author крокодил
 * @Description синхронизировать канал сохранить карту
 * @date 2022/11/27 16:30
 * @Version 1.0
 */

public class ChannelMap {
    /**
     * Сохраните соответствующую связь между идентификатором идентификации клиента (идентификатором сообщения) и каналом.
     */
    private static volatile ConcurrentHashMap<String, Channel> channelMap = null;

    private ChannelMap() {
    }
    
    public static ConcurrentHashMap<String, Channel> getChannelMap() {
        if (null == channelMap) {
            synchronized (ChannelMap.class) {
                if (null == channelMap) {
                    channelMap = new ConcurrentHashMap<>();
                }
            }
        }
        return channelMap;
    }

    public static Channel getChannel(String id) {
        return getChannelMap().get(id);
    }
}

Когда клиент устанавливает соединение (сервер получает сообщение Heartbeat), канал добавляется на карту.

Язык кода:javascript
копировать
public class ServerListenerHandler extends SimpleChannelInboundHandler<Message> {
    private static final Logger log = LoggerFactory.getLogger(ServerListenerHandler.class);

    /**
     * Обрабатывается, когда устройство подключено к сети
     *
     * @param ctx
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        log.info("Появились новые соединения: [{}]", ctx.channel().id().asLongText());
    }

    /**
     * Обработка данных
     *
     * @param ctx
     * @param msg
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Message msg) {
        // Получить тело сообщения в экземпляре сообщения
        String content = msg.getContent();
        // Обработка различных типов сообщений
        MessageEnum type = MessageEnum.getStructureEnum(msg);
        switch (type) {
            case CONNECT:
                // Добавить канал в ChannelMap
                ChannelMap.getChannelMap().put(msg.getId(), ctx.channel());
                
                // Добавьте clientID в качестве настраиваемого атрибута канала, чтобы упростить получение идентификатора пользователя из канала в любое время.
                AttributeKey<String> key = AttributeKey.valueOf("id");
                ctx.channel().attr(key).setIfAbsent(msg.getId());

                // TODO Обработка сообщений Heartbeat
            case STATE:
                // TODO Статус устройства
            default:
                System.out.println(type.content + «Содержимое сообщения» + content);
        }
    }

    /**
     * Автономная обработка оборудования
     *
     * @param ctx
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {
        log.info("Устройство не в сети: {}", ctx.channel().id().asLongText());
        // Удалить канал с карты
        removeId(ctx);
    }

    /**
     * Устройство поддерживает обработку исключений
     *
     * @param ctx
     * @param cause
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        // Исключение печати
        log.info("Исключение: {}", cause.getMessage());
        // Удалить канал с карты
        removeId(ctx);
        // закрытиесоединять        ctx.close();
    }

    private void removeId(ChannelHandlerContext ctx) {
        AttributeKey<String> key = AttributeKey.valueOf("id");
        // Получить идентификатор в канале
        String id = ctx.channel().attr(key).get();
        // карта удалить канал
        ChannelMap.getChannelMap().remove(id);
    }
}

Напишите класс бизнес-уровня, который отправляет сообщения.,И получить канал канала в карте через clientid,После преобразования сообщения в строку json,проходитьwriteAndFlushОтправитьклиент。

Язык кода:javascript
копировать
/**
 * @Author крокодил
 * @Description Отправить сообщение клиенту
 * @date 2022/11/27 17:29
 * @Version 1.0
 */

@Service
public class PushMsgServiceImpl implements PushMsgService {

    /**
     * Отправить сообщение клиенту
     *
     * @param msg
     */
    @Override
    public void push(Message msg) {
        // клиентID
        String id = msg.getId();
        Channel channel = ChannelMap.getChannel(id);
        if (null == channel) {
            throw new RuntimeException("клиент не в сети");
        }
        channel.writeAndFlush(msg);
    }
}

Уведомление:writeAndFlushПараметр представляет собой экземпляр универсального объекта с пользовательской кодировкой.。Как указано в этой статьеMessageКласс разбора сообщений。

Язык кода:javascript
копировать
public class MessageEncodeHandler extends MessageToByteEncoder<Message> {

    private static String delimiter;

    public MessageEncodeHandler(String delimiter) {
        this.delimiter = delimiter;
    }

    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, Message message, ByteBuf out) throws Exception {
        out.writeBytes(
                (message.toJsonString() + delimiter)
                        .getBytes(CharsetUtil.UTF_8)
        );
    }
}

Напиши еще позжеControllerдобрый(Опустить здесь),существоватьControllerдобрыйсерединавызовPushMsgServiceсерединаpushff,Вы можете завершить отправку сообщения клиенту.

boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose