Coder Social home page Coder Social logo

jhg023 / simplenet Goto Github PK

View Code? Open in Web Editor NEW
176.0 17.0 19.0 348 KB

An easy-to-use, event-driven, asynchronous network application framework compiled with Java 11.

License: MIT License

Java 100.00%
java nio2 nio network networking client client-server server bytes byte

simplenet's Introduction

What is SimpleNet?

SimpleNet is a simplistic, client-server framework written in Java. One or more Client objects can connect to a Server and send data back-and-forth via TCP. Most methods that read data from the network are non-blocking and are invoked asynchronously when the requested data arrives. Not having to block a thread and wait for data is what makes SimpleNet scalable for different types of applications such as chat servers, multiplayer game servers, and so much more!

Maven/Gradle Dependency

  1. Add SimpleNet as a dependency using either Maven or Gradle:

Maven:

<dependency>
    <groupId>com.github.jhg023</groupId>
    <artifactId>SimpleNet</artifactId>
    <version>1.6.6</version>
</dependency>

Gradle:

implementation 'com.github.jhg023:SimpleNet:1.6.6'
  1. Because SimpleNet is compiled with Java 11, you must first require its module in your module-info.java:
module my.project {
    requires com.github.simplenet;
}

What do I need to know before using SimpleNet?

  • As stated above, SimpleNet is mostly callback-based. This means that you can request a specific amount of bytes, n, and specify what should be done with those n bytes, as if the Client or Server has already received them.
// Request a single byte and print it out when it arrives.
client.readByte(System.out::println);

// Continuously request a single byte and print it out when it arrives.
// 
// After the callback completes, another byte will be requested automatically, 
// and the same callback will be invoked. This continues until the connection is closed.
client.readByteAlways(System.out::println);
  • Packets are sent from a Client to a Server (and vice versa) via the Packet class.
// Create a packet that contains two bytes (with the value 42) and send it to a client.
Packet.builder().putByte(42).putByte(42).queueAndFlush(client);

// Create two packets with the specified data and queue them (but don't flush) to a client.
Packet.builder().putInt(123456).queue(client);
Packet.builder().putString("Hello World!").queue(client);

// Flush the queued packets to the client (these packets will be transformed into a single,
// big packet to improve throughput.
// 
// This method only needs to be called when using Packet#queue and not Packet#queueAndFlush.
client.flush();

Client Example

// Instantiate a new client.
var client = new Client();

// Register a connection listener.
client.onConnect(() -> {
    System.out.println(client + " has connected to the server!");
    
    // Builds a packet and sends it to the server immediately.
    Packet.builder().putByte(1).putInt(42).queueAndFlush(client);
});

// Register a pre-disconnection listener.
client.preDisconnect(() -> System.out.println(client + " is about to disconnect from the server!"));

// Register a post-disconnection listener.
client.postDisconnect(() -> System.out.println(client + " successfully disconnected from the server!"));

// Attempt to connect to a server AFTER registering listeners.
client.connect("localhost", 43594);

Server Example

// Instantiate a new server.
var server = new Server();

// Register one connection listener.
server.onConnect(client -> {
    System.out.println(client + " has connected!");

    /*
     * When one byte arrives from the client, switch on it.
     * If the byte equals 1, then request an int and print it
     * when it arrives.
     *
     * Because `readByteAlways` is used, this will loop when
     * the callback completes, but does not hang the executing thread.
     */
    client.readByteAlways(opcode -> {
        switch (opcode) {
            case 1:
                client.readInt(System.out::println);
        }
    });

    // Register a pre-disconnection listener.
    client.preDisconnect(() -> System.out.println(client + " is about to disconnect!"));

    // Register a post-disconnection listener.
    client.postDisconnect(() -> System.out.println(client + " has successfully disconnected!"));
});

// Bind the server to an address and port AFTER registering listeners.
server.bind("localhost", 43594);

Chat Server Example

To emphasize how easy it is to use SimpleNet, I have written an implementation of a scalable chat server below. Note how only two classes are required, ChatServer and ChatClient. Ideally, a chat server should use a GUI and not the console, as this leads to message cut-off in the window, but this is only to serve as a proof-of-concept.

The full ChatServer implementation is as follows:

public class ChatServer {
    public static void main(String[] args) {
        // Initialize a new server.
        var server = new Server();
        
        // Create a map that will keep track of nicknames on our chat server.
        var nicknameMap = new ConcurrentHashMap<Client, String>();
        
        // This callback is invoked when a client connects to this server.
        server.onConnect(client -> {
            // When a client disconnects, remove them from the nickname map.
            client.postDisconnect(() -> nicknameMap.remove(client));
            
            // Repeatedly read a single byte.
            client.readByteAlways(opcode -> {
                switch (opcode) {
                    case 1: // Change nickname
                        client.readString(nickname -> nicknameMap.put(client, nickname));
                        break;
                    case 2: // Send message to connected clients.
                        client.readString(message -> {
                            message = nicknameMap.get(client) + ": " + message;
                            server.queueAndFlushToAllExcept(Packet.builder().putString(message), client);
                        });
                        break;    
                }
            });
        });
        
        // Bind the server to our local network AFTER registering listeners.
        server.bind("localhost", 43594);
    }
}

The full ChatClient implementation is as follows:

public class ChatClient {
    public static void main(String[] args) {
        // Initialize a new client.
        var client = new Client();
        
        // This callback is invoked when this client connects to a server.
        client.onConnect(() -> {
            var scanner = new Scanner(System.in);
            
            // If messages arrive from other clients, print them to the console.
            client.readStringAlways(System.out::println);
            
            System.out.print("Enter your nickname: ");
            Packet.builder().putByte(1).putString(scanner.nextLine()).queueAndFlush(client);
            
            // Infinite loop to accept user-input for the chat server.
            while (true) {
                System.out.print("> ");
                
                // Read the client's message from the console.
                var message = scanner.nextLine();
                
                // If this client types "/leave", close their connection to the server.
                if ("/leave".equals(message)) {
                    client.close();
                    break;
                }
                
                // Otherwise, send a packet to the server containing the client's message.
                Packet.builder().putByte(2).putString(message).queueAndFlush(client);
            }
        });
        
        // Attempt to connect to a server AFTER registering listeners.
        client.connect("localhost", 43594);
    }
}

FAQ (Frequently-Asked Questions)

  • Can data be sent and received outside of the onConnect callback?
    • Yes, but only if the client is already connected to the server. However, this is not recommended unless you're well-versed in concurrency, as the onConnect callback is performed asynchronously.
  • Can a SimpleNet Client be used with some unrelated server and vice-versa?
    • Absolutely!
  • I have large packets that exceed the default buffer size; what can I do to avoid an exception?
    • Ideally, the best option would be to split your single, large packet into multiple, small packets.
    • If splitting the packet is not possible for any reason, both Client and Server have an overloaded constructor that accepts a buffer size in bytes. You can simply specify a size larger than 8192 (the default size).
  • Will Java 8 ever be supported again?
    • No, as Java 8 is no longer supported commercially as of January, 2019. SimpleNet will do its best to keep up with LTS releases. However, you're free to clone the project and build it on an older version of Java, as not many code changes are required.
  • What's next for SimpleNet?
    • Once Project Loom is complete and integrated into the mainline JDK, SimpleNet will be rewritten entirely; blocking I/O will be using fibers at that point, which will be much more scalable than my current implementation that uses a fixed thread pool.

simplenet's People

Contributors

imrel avatar jhg023 avatar rubbaboy avatar technerder avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

simplenet's Issues

[Question] Socket with SSL

I just want to ask if there is any implmentation of Simplenet with SSL? I have searched a lot and didnt find anything. Appreciate any hints.

Custom buffer size overload missing?

I'm not sure if I understood the FAQ correctly, but it's not immediately obvious to me where the overload for setting the size of the buffer. Can you please confirm that the ability is there, and maybe give a bit of guidance on how to implement it? Thank you.

java.lang.NegativeArraySizeException

Hi,
in the server/client application the server send 49678 byte (the error also occurs with smaller sizes )
In the client application:
java.lang.NegativeArraySizeException: -15858
at com.simplenet.utility.exposed.data.StringReader.lambda$processBytes$4(StringReader.java:169)
at com.simplenet.utility.exposed.data.DataReader.lambda$read$0(DataReader.java:76)
at com.simplenet.Client.lambda$readUntil$4(Client.java:529)
at com.simplenet.Client$Listener.completed(Client.java:137)
at com.simplenet.Client$Listener.completed(Client.java:83)
at java.base/sun.nio.ch.Invoker.invokeUnchecked(Invoker.java:127)
at java.base/sun.nio.ch.Invoker$2.run(Invoker.java:219)
at java.base/sun.nio.ch.AsynchronousChannelGroupImpl$1.run(AsynchronousChannelGroupImpl.java:112)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
at java.base/java.lang.Thread.run(Thread.java:831)

Following the function in error:
private void processBytes(short n, Consumer consumer, Charset charset, ByteOrder order) {
int length = order == ByteOrder.LITTLE_ENDIAN ? Short.reverseBytes(n) : n;

    read(Byte.BYTES * (length & 0xFFFF), buffer -> {
        var b = new byte[length];
        buffer.get(b);
        consumer.accept(new String(b, charset));
    }, order);
}

Which is the problem?
Many Thanks

client.close() does not wait for packets to be flushed

Steps to reproduce :

  1. Build a packet and use queueAndFlush to flush it
  2. Close right after
  3. See the client not receiving the packet because the connection got closed before.

Pseudocode:

Packet packet = Packet.builder().putInt(100).putInt(200);
packet.queueAndFlush(client);
client.close();

Error in .limit() function

I am not sure how to elaborate this one. but when there is many frequent concurrent connections from both sides, server to client, and client to server. Sometimes the following error showing up.

java.lang.IllegalArgumentException: newLimit < 0: (-33395131 < 0)
	at java.base/java.nio.Buffer.createLimitException(Buffer.java:388)
	at java.base/java.nio.Buffer.limit(Buffer.java:362)
	at java.base/java.nio.ByteBuffer.limit(ByteBuffer.java:1322)
	at java.base/java.nio.MappedByteBuffer.limit(MappedByteBuffer.java:383)
	at java.base/java.nio.MappedByteBuffer.limit(MappedByteBuffer.java:70)
	at net.simplenet.Client$Listener.completed(Client.java:608)
	at net.simplenet.Client$Listener.completed(Client.java:567)
	at java.base/sun.nio.ch.Invoker.invokeUnchecked(Invoker.java:127)
	at java.base/sun.nio.ch.Invoker$2.run(Invoker.java:219)
	at java.base/sun.nio.ch.AsynchronousChannelGroupImpl$1.run(AsynchronousChannelGroupImpl.java:112)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
	at java.base/java.lang.Thread.run(Thread.java:832)

Client.Java:608

 while (buffer.remaining() >= (key = peek.getKey())) {
                    var wrappedBuffer = buffer.duplicate().mark().limit(buffer.position() + key); //Line 608 is here

                    if (shouldDecrypt) {
                        try {
                            wrappedBuffer = client.decryptionFunction.apply(client.decryptionCipher, wrappedBuffer)

Any idea what can cause the issue? and how to fix?

Thread-unsafe listeners list

Great project! Only nitpick I have is that currently the connectListeners is an ArrayList. New elements are added to this list via the onConnect method which does not specify any thread-safety guarantees.

However, adding listeners from different threads may be desirable (even after calling bind), and this list could be changed to a CopyOnWriteArrayList since it will rarely be mutated and will be heavily traversed.

this.connectListeners = new ArrayList<>(1);

RSA

Should allow for RSA encryption :)

hundreds connection

Hi,
How can I simulate hundreds of concurrent connections?
What is the correct sequence of instructions on both client and server side?
Many Thanks

Client#readStringUntil only ever returns first element

When testing the following code i came to the conclusion that only ever one element is returned for Client#readStringUntil

client.readByteAlways(opCode -> {
    if (opCode == 2) {
        List<String> tempWarps = new ArrayList<>();
        client.readString(s -> {
            client.readInt(warpSize -> {
                AtomicInteger count = new AtomicInteger(0);
               //this part
                client.readStringUntil(s1 -> {
                    tempWarps.add(s1);
                    return warpSize < count.getAndIncrement();
                });
            });
            serverWarps.put(s, tempWarps);
        });
    }
});

it seems to ignore the return value. if i swap < for > the result is the same.

I also tried using client.readStringAlways but this messes up when new packets are received as they're still getting handled in the readStringAlways

Client Callbacks

Hi,

Im wondering if this code supports a one-way client callbacks? Meaning the client connect to server. Then the server adds the client to a Map, and at some point, the server uses the client that has been added to the Map, and sends client data to it, and the client will handle the received data and print it out.

Thanks.

Issues with Encryption

There is a problem with Encryption when there are more than 2 clients connected to the same server.

By using the below code, the first client connect to the server without any issues, and there is encryption, but second client onward throwing the below error.

FYI, I am just sending a small amount of bytes, and I am not sure where the below number (e.g. 1892643530) is coming from.

My machine has enough ram and I set "XX:MaxDirectMemorySize=4096m" but it didn't help.

java.lang.OutOfMemoryError: Cannot reserve 1892643530 bytes of direct buffer memory (allocated: 75700, limit: 536870912)
	at java.base/java.nio.Bits.reserveMemory(Unknown Source)
	at java.base/java.nio.DirectByteBuffer.<init>(Unknown Source)
	at java.base/java.nio.ByteBuffer.allocateDirect(Unknown Source)
	at com.github.pbbl.direct.DirectByteBufferPool.allocate(DirectByteBufferPool.java:39)
	at com.github.pbbl.direct.DirectByteBufferPool.allocate(DirectByteBufferPool.java:35)
	at com.github.pbbl.AbstractBufferPool.lambda$take$1(AbstractBufferPool.java:92)
	at java.base/java.util.Optional.orElseGet(Unknown Source)
	at com.github.pbbl.AbstractBufferPool.take(AbstractBufferPool.java:92)
	at net.simplenet.Client$Listener.completed(Client.java:172)
	at net.simplenet.Client$Listener.completed(Client.java:78)
	at java.base/sun.nio.ch.Invoker.invokeUnchecked(Unknown Source)
	at java.base/sun.nio.ch.Invoker.invokeDirect(Unknown Source)
	at java.base/sun.nio.ch.UnixAsynchronousSocketChannelImpl.implRead(Unknown Source)
	at java.base/sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at java.base/sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at java.base/java.nio.channels.AsynchronousSocketChannel.read(Unknown Source)
	at net.simplenet.Client$Listener.completed(Client.java:173)
	at net.simplenet.Client$Listener.completed(Client.java:78)
	at java.base/sun.nio.ch.Invoker.invokeUnchecked(Unknown Source)
	at java.base/sun.nio.ch.Invoker.invokeDirect(Unknown Source)
	at java.base/sun.nio.ch.UnixAsynchronousSocketChannelImpl.implRead(Unknown Source)
	at java.base/sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at java.base/sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at java.base/java.nio.channels.AsynchronousSocketChannel.read(Unknown Source)
	at net.simplenet.Client$Listener.completed(Client.java:173)
	at net.simplenet.Client$Listener.completed(Client.java:78)
	at java.base/sun.nio.ch.Invoker.invokeUnchecked(Unknown Source)
	at java.base/sun.nio.ch.Invoker.invokeDirect(Unknown Source)
	at java.base/sun.nio.ch.UnixAsynchronousSocketChannelImpl.implRead(Unknown Source)
	at java.base/sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at java.base/sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at java.base/java.nio.channels.AsynchronousSocketChannel.read(Unknown Source)
	at net.simplenet.Client$Listener.completed(Client.java:173)
	at net.simplenet.Client$Listener.completed(Client.java:78)
	at java.base/sun.nio.ch.Invoker.invokeUnchecked(Unknown Source)
	at java.base/sun.nio.ch.Invoker.invokeDirect(Unknown Source)
	at java.base/sun.nio.ch.UnixAsynchronousSocketChannelImpl.implRead(Unknown Source)
	at java.base/sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at java.base/sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at java.base/java.nio.channels.AsynchronousSocketChannel.read(Unknown Source)
	at net.simplenet.Client$Listener.completed(Client.java:173)
	at net.simplenet.Client$Listener.completed(Client.java:78)
	at java.base/sun.nio.ch.Invoker.invokeUnchecked(Unknown Source)
	at java.base/sun.nio.ch.Invoker.invokeDirect(Unknown Source)
	at java.base/sun.nio.ch.UnixAsynchronousSocketChannelImpl.implRead(Unknown Source)
	at java.base/sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at java.base/sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at java.base/java.nio.channels.AsynchronousSocketChannel.read(Unknown Source)
	at net.simplenet.Client$Listener.completed(Client.java:173)
	at net.simplenet.Client$Listener.completed(Client.java:78)
	at java.base/sun.nio.ch.Invoker.invokeUnchecked(Unknown Source)
	at java.base/sun.nio.ch.Invoker.invokeDirect(Unknown Source)
	at java.base/sun.nio.ch.UnixAsynchronousSocketChannelImpl.implRead(Unknown Source)
	at java.base/sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at java.base/sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at java.base/java.nio.channels.AsynchronousSocketChannel.read(Unknown Source)
	at net.simplenet.Client$Listener.completed(Client.java:173)
	at net.simplenet.Client$Listener.completed(Client.java:78)
	at java.base/sun.nio.ch.Invoker.invokeUnchecked(Unknown Source)
	at java.base/sun.nio.ch.Invoker.invokeDirect(Unknown Source)
	at java.base/sun.nio.ch.UnixAsynchronousSocketChannelImpl.implRead(Unknown Source)
	at java.base/sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at java.base/sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at java.base/java.nio.channels.AsynchronousSocketChannel.read(Unknown Source)
	at net.simplenet.Client$Listener.completed(Client.java:173)
	at net.simplenet.Client$Listener.completed(Client.java:78)
	at java.base/sun.nio.ch.Invoker.invokeUnchecked(Unknown Source)
	at java.base/sun.nio.ch.Invoker.invokeDirect(Unknown Source)
	at java.base/sun.nio.ch.UnixAsynchronousSocketChannelImpl.implRead(Unknown Source)
	at java.base/sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at java.base/sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at java.base/java.nio.channels.AsynchronousSocketChannel.read(Unknown Source)
	at net.simplenet.Client$Listener.completed(Client.java:173)
	at net.simplenet.Client$Listener.completed(Client.java:78)
	at java.base/sun.nio.ch.Invoker.invokeUnchecked(Unknown Source)
	at java.base/sun.nio.ch.Invoker.invokeDirect(Unknown Source)
	at java.base/sun.nio.ch.UnixAsynchronousSocketChannelImpl.implRead(Unknown Source)
	at java.base/sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at java.base/sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at java.base/java.nio.channels.AsynchronousSocketChannel.read(Unknown Source)
	at net.simplenet.Client$Listener.completed(Client.java:173)
	at net.simplenet.Client$Listener.completed(Client.java:78)
	at java.base/sun.nio.ch.Invoker.invokeUnchecked(Unknown Source)
	at java.base/sun.nio.ch.Invoker$2.run(Unknown Source)
	at java.base/sun.nio.ch.AsynchronousChannelGroupImpl$1.run(Unknown Source)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.base/java.lang.Thread.run(Unknown Source)

Server.java:

private Cipher[] initCiphers(String cipher) throws GeneralSecurityException {
        byte[] raw = { (byte) 0xA5, (byte) 0x01, (byte) 0x7B, (byte) 0xE5,
                (byte) 0x23, (byte) 0xCA, (byte) 0xD4, (byte) 0xD2,
                (byte) 0x23, (byte) 0xCA, (byte) 0xD4, (byte) 0xD2,
                (byte) 0x23, (byte) 0xCA, (byte) 0xD4, (byte) 0xD2,
               };

        var ivSpec = new IvParameterSpec(raw);
        var keySpec = new SecretKeySpec(raw, "AES");

        var serverEncryption = Cipher.getInstance(cipher);
        var serverDecryption = Cipher.getInstance(cipher);

        serverEncryption.init(Cipher.ENCRYPT_MODE, keySpec,ivSpec);
        serverDecryption.init(Cipher.DECRYPT_MODE, keySpec,ivSpec);

        return new Cipher[] { serverEncryption, serverDecryption };
    }

public void startServer()  {
        var ciphers = initCiphers("AES/CFB8/NoPadding");
        server.onConnect(client -> {
            client.setEncryption(ciphers[0], UPDATE);
            client.setDecryption(ciphers[1], UPDATE);
            client.readByteAlways(opcode -> {
                  switch (opcode) {            
                  ..........
                  }
             } 
         }

Client:

 private Cipher[] initCiphers(String cipher) throws GeneralSecurityException {
        byte[] raw = {(byte) 0xA5, (byte) 0x01, (byte) 0x7B, (byte) 0xE5,
                (byte) 0x23, (byte) 0xCA, (byte) 0xD4, (byte) 0xD2,
                (byte) 0x23, (byte) 0xCA, (byte) 0xD4, (byte) 0xD2,
                (byte) 0x23, (byte) 0xCA, (byte) 0xD4, (byte) 0xD2,
        };

        var ivSpec = new IvParameterSpec(raw);
        var keySpec = new SecretKeySpec(raw, "AES");

        var clientEncryption = Cipher.getInstance(cipher);
        var clientDecryption = Cipher.getInstance(cipher);

        clientEncryption.init(Cipher.ENCRYPT_MODE, keySpec,ivSpec);
        clientDecryption.init(Cipher.DECRYPT_MODE, keySpec,ivSpec);

        return new Cipher[]{clientEncryption, clientDecryption};
    }


var ciphers = initCiphers("AES/CFB8/NoPadding");

            client.onConnect(() -> {

                client.setEncryption(ciphers[0], UPDATE);
                client.setDecryption(ciphers[1], UPDATE);

                client.readByteAlways(opcode -> {
                    switch (opcode) {
                         .........
                   }
                }
            }

Question: Get output from queueAndFlush

Hello,

I am wondering if there is anyway to send a request to the server with the below, and get the result output back.
Example:

var result = Packet.builder().putByte(2).putString(message).queueAndFlush(client);

Any hint appreciated.
Thanks.

Custom / advanced event types.

We have the events connection, pre-disconnection and disconnection.
Is it planned to give a user a way to add custom / more advanced types of events?
I think of events such as:

*A message / data packet with specific content received, treat it elsewhere different from everything else.

  • TCP connection still persists, but the last data packet received was some time ago, treat this case, e.g. with sending a "ping" (and hopefully receiving a "pong").
  • Connection to other side lost (as a special case of disconection).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.