Skip to content

Java send nullbyte (x00) as 1 character using AsynchronousSocketChannel

I need to send nullbyte as 1 character this code send its as 4 characters so not a nullbyte (x00) , it can’t be sending it as plain text. it’s sending to a flash client. I’m using AsynchronousSocketChannel to send the packets. the nullbyte is to tell the server that the packet has ended. for example when I send testx00 it sends it as testx00 which is wrong.

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.net.SocketAddress;

import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.Charset;

import java.util.concurrent.Future;

public class Main {



    public static void main(String[] args) throws Exception {
        String connect = "gfdg";
        System.out.println(connect);

        String request = connect;
        AsynchronousSocketChannel channel = AsynchronousSocketChannel.open();
        SocketAddress serverAddr = new InetSocketAddress("artix.aqw.aq.com", 5588);
        Future<Void> result = channel.connect(serverAddr);
        result.get();
        Attachment attach = new Attachment();
        attach.channel = channel;
        attach.buffer = ByteBuffer.allocate(2048);
        attach.isRead = false;
        attach.mainThread = Thread.currentThread();

        Charset cs = Charset.forName("UTF-8");
        String msg = request;
        byte[] data = msg.getBytes(cs);
        attach.buffer.put(data);
        attach.buffer.flip();

        ReadWriteHandler readWriteHandler = new ReadWriteHandler();
        channel.write(attach.buffer, attach, readWriteHandler);
        attach.mainThread.join();
    }
}

class Attachment {
    AsynchronousSocketChannel channel;
    ByteBuffer buffer;
    Thread mainThread;
    boolean isRead;
}

class ReadWriteHandler implements CompletionHandler<Integer, Attachment> {

    @Override
    public void completed(Integer result, Attachment attach) {
        if (attach.isRead) {
            attach.buffer.flip();
            Charset cs = Charset.forName("UTF-8");
            int limits = attach.buffer.limit();
            byte bytes[] = new byte[limits];
            attach.buffer.get(bytes, 0, limits);
            String msg = new String(bytes, cs);
            String str = new String(bytes,cs).split("")[0];

            System.out.format("Server Responded: " + str + "n");
            try {
                msg = this.getTextFromUser();
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (msg.equalsIgnoreCase("bye")) {
                attach.mainThread.interrupt();
                return;
            }
            attach.buffer.clear();
            byte[] data = msg.getBytes(cs);
            attach.buffer.put(data);
            attach.buffer.flip();
            attach.isRead = false; // It is a write
            attach.channel.write(attach.buffer, attach, this);
        } else {
            attach.isRead = true;
            attach.buffer.clear();
            attach.channel.read(attach.buffer, attach, this);
        }
    }

    @Override
    public void failed(Throwable e, Attachment attach) {
        e.printStackTrace();
    }

    private String getTextFromUser() throws Exception {
        System.out.println("Please enter a  message:");
        BufferedReader consoleReader = new BufferedReader(
                new InputStreamReader(System.in));

        String msg = consoleReader.readLine() + "\x00";
        return msg;
    }


}

Answer

You should write a single null byte (0x00) after writing your string to the channel. What you’re doing is not it: you’re appending the string x00 instead (a backslash followed by an x and two 0s).

Against my first instincts, it seems it will work if you append the unicode character u0000 to your string, but the optimal way to do it is simply to put a byte with value 0 into the ByteBuffer after putting your string.

To be clear, I expected the null byte to be doubled when you append u0000, as Java encodes chars as UTF-16, hence on 2 bytes. But we’re explicitly encoding the String to UTF-8 to get it as bytes, so the null char is indeed encoded as a single null byte.

Here’s a small demo of this, showing for each method the length of data written to the channel, then its value as bytes:

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

public class Buffer {
    
    public static void main(String[] args) throws IOException {

        try (Scanner scan = new Scanner(System.in)) {

            boolean done = false;
            while(!done) {

                System.out.println("Enter string to encode, 'bye' to exit:");

                String s = scan.nextLine();
                if ("bye".equals(s.toLowerCase())) {
                    done = true;
                    break;
                }

                System.out.println("withNullChar");
                String withNullChar = s + 'u0000';
                ByteBuffer buff = ByteBuffer.allocate(1024);
                buff.put(withNullChar.getBytes(StandardCharsets.UTF_8));
                System.out.println("Length: " + buff.position());
                buff.flip();
                byte[] result = readBack(buff);
                printArray(result);

                System.out.println("withNullCharFaulty");
                String withNullCharFaulty = s + "\x00";
                buff = ByteBuffer.allocate(1024);
                buff.put(withNullCharFaulty.getBytes(StandardCharsets.UTF_8));
                System.out.println("Length: " + buff.position());
                buff.flip();
                result = readBack(buff);
                printArray(result);

                System.out.println("with null byte");
                buff = ByteBuffer.allocate(1024);
                buff.put(s.getBytes(StandardCharsets.UTF_8)).put((byte) 0);
                System.out.println("Length: " + buff.position());
                buff.flip();
                result = readBack(buff);
                printArray(result);

            }

        }

    }

    public static byte[] readBack(ByteBuffer buff) throws IOException {
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
            try (WritableByteChannel channel = Channels.newChannel(bos)) {
                channel.write(buff);
                return bos.toByteArray();
            }
        }
    }

    public static void printArray(byte[] arr) {
        StringBuilder sb = new StringBuilder();
        for (byte b : arr)
            sb.append(String.format("%02X ", b));
        System.out.println(sb);
    }

}