Saya sedang mengembangkan sistem komunikasi klien dan server menggunakan Netty NIO di Jawa. Kode saya dapat ditemukan di repositori berikut. Saat ini saya memiliki satu server dan dua klien dan saya mengirim informasi dari server ke klien dan sebaliknya.

Apa yang saya coba cari tahu, ketika saya menerima pesan dari klien pertama ke server, bagaimana saya bisa mengirim pesan itu ke klien kedua (dan kebalikan dari klien 2 ke klien 1). Bagaimana saya bisa mengirim pesan ke klien tertentu?

Saya perhatikan bahwa masalah saya muncul karena cara saya mencoba mengirim pesan dari server. Kode saya di serverHandler adalah sebagai berikut:

for (Channel ch : channels1) {
    responseData.setIntValue(channels1.size());
    remoteAddr.add(ch.remoteAddress().toString());
    future = ch.writeAndFlush(responseData);
    //future.addListener(ChannelFutureListener.CLOSE);
    System.out.println("the requested data from the clients are: "+requestData);
    responseData1.setStringValue(requestData.toString());
    future = ch.writeAndFlush(responseData1);
    System.out.println(future);
}

Secara default saya mengirim pesan tentang jumlah koneksi, tetapi juga ketika saya menerima pesan dari klien 1 atau 2 saya ingin mengirimnya kembali ke 2 dan 1. Jadi saya ingin melakukan komunikasi antara dua komponen. Bagaimana saya bisa mengirim dari server ke klien tertentu? Saya tidak yakin bagaimana saya bisa mengirim pesan kembali ke klien.

7
konstantin 13 November 2017, 19:58

1 menjawab

Jawaban Terbaik

Pendekatan umum

Mari kita jelaskan sebuah pendekatan untuk masalah ini.

Saat menerima data di sisi server, gunakan alamat saluran jarak jauh (java.net.SocketAddress Channel.remoteAddress() metode) untuk mengidentifikasi klien.

Identifikasi tersebut dapat dilakukan dengan menggunakan peta seperti: Map<SocketAddress, Client>, di mana kelas atau antarmuka Client harus berisi koneksi klien (saluran) yang sesuai dengan konteks terkait, termasuk Channel-nya. Pastikan untuk selalu memperbarui peta: tangani peristiwa «klien terhubung» dan «klien terputus» dengan tepat.

Setelah klien diidentifikasi, Anda dapat mengirim pesan yang sesuai ke klien, kecuali klien pengirim saat ini, menggunakan peta koneksi (saluran) klien.

Selain itu, saya ingin merekomendasikan Anda untuk menemukan implementasi yang baik dari aplikasi obrolan menggunakan Netty dan untuk melihatnya.

Solusi khusus Netty

Mari kita pertimbangkan implementasi sisi server, khususnya implementasi kelas ProcessingHandler.

Itu sudah mengelola saluran aktif dengan mewakili mereka sebagai grup saluran:

static final ChannelGroup channels1 =
    new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

Menjaga grup saluran tetap up-to-date

Implementasi saat ini menangani acara «saluran menjadi aktif» agar grup saluran tetap terbarui:

@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
    channels1.add(ctx.channel());
    // ...
}

Tapi ini hanya setengah: perlu untuk menangani acara «saluran menjadi tidak aktif» secara simetris juga. Implementasinya akan terlihat seperti:

@Override
public void channelInactive(final ChannelHandlerContext ctx) throws Exception {
    channels1.remove(ctx.channel());
}

Penyiaran: Mengirim pesan yang diterima ke semua saluran, kecuali saluran saat ini

Untuk mengimplementasikan perilaku yang diinginkan, cukup perbarui implementasi dengan memperkenalkan pemeriksaan yang sesuai sebagai berikut:

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    // ...

    for (Channel ch : channels1) {
        // Does `ch` represent the channel of the current sending client?
        if (ch.equals(ctx.channel())) {
            // Skip.
            continue;
        }

        // Send the message to the `ch` channel.
        // ...
    }

    // ...
}

Masalah pengiriman dan penerimaan string

Saat ini, fungsionalitas di sekitar kelas ResponseData tidak ada (tidak dilaksanakan).

Perubahan draf berikut diperlukan untuk membuat klien dan server berfungsi.

  1. Kelas ResponseData: metode getStringValue dan toString harus diperbaiki:

    String getStringValue() {
        return this.strValue;
    }
    
    @Override
    public String toString() {
        return intValue + ";" + strValue;
    }
    
  2. Kelas ResponseDataEncoder: harus menggunakan nilai string:

    private final Charset charset = Charset.forName("UTF-8");
    
    @Override
    protected void encode(final ChannelHandlerContext ctx, final ResponseData msg, final ByteBuf out) throws Exception {
        out.writeInt(msg.getIntValue());
        out.writeInt(msg.getStringValue().length());
        out.writeCharSequence(msg.getStringValue(), charset);
    }
    
  3. Kelas ResponseDataDecoder: harus menggunakan nilai string:

    private final Charset charset = Charset.forName("UTF-8");
    
    @Override
    protected void decode(final ChannelHandlerContext ctx, final ByteBuf in, final List<Object> out) throws Exception {
        ResponseData data = new ResponseData();
        data.setIntValue(in.readInt());
        int strLen = in.readInt();
        data.setStringValue(in.readCharSequence(strLen, charset).toString());
        out.add(data);
    }
    
  4. Kelas ClientHandler: harus menerima dan menangani pesan dengan benar:

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        final ResponseData responseData = (ResponseData) msg;
        System.out.println("The message sent from the server " + responseData);
        update.accept(responseData.getIntValue());
    }
    

Referensi tambahan

  1. «SecureChat - server obrolan berbasis TLS, berasal dari contoh Telnet», Dokumentasi Netty. Secara khusus, implementasi SecureChatServerHandler class< /a>.
  2. «Netty in Action», Norman Maurer, Marvin Allen Wolfthal (ISBN-13: 978-1617291470 ), «Bagian 3 — Protokol jaringan», subbab «12.2 Contoh aplikasi WebSocket kami». Mencakup implementasi «aplikasi obrolan berbasis browser».
7
Sergey Brunov 23 November 2017, 13:44