KMBox NET Tester

C# (.NET 8) console tool that drives a KMBox NET box over UDP: handshake, test moves and clicks, and measurement of latency, jitter and packet loss.

Overview

The KMBox NET is a hardware box that behaves like a mouse and a keyboard, and receives its commands over the network. When the box does not respond, the cause can be the cable, the port, the device identifier, the network card or the firewall. I wrote KMBox NET Tester to settle that quickly: a C# console application on .NET 8 that speaks the box's UDP protocol, sends test moves and clicks, and measures the response time of the link. The README presents it as useful for so-called DMA setups, and its disclaimer mentions misuse in online games. The tool targets no particular game and reads no memory. The C# code fits in four files.

A 72-byte packet written field by field

In KMBoxClient, I reproduced the official packet structure and the command codes from the C++ header kmboxNet.h. Every packet starts with a 16-byte header: four unsigned 32-bit little-endian integers, which carry the device identifier (entered in hexadecimal), a random value, an index incremented on every send, and the command code. Mouse commands add a 56-byte payload, 72 bytes in total. The first integer of that payload carries the button state, the next two the relative x and y movement.

I write each field with BinaryPrimitives on Spans rather than marshalling a struct: WriteUInt32LittleEndian for the header, WriteInt32LittleEndian for the payload, where x and y are signed. The offsets stay visible and the byte order does not depend on the machine. The class declares six commands, but the client only sends three of them: connect, move and left click.

A listening loop to time the round trip

The client opens a UdpClient on a port picked by the system. If the user gives the IP address of their Ethernet card, the socket binds to that interface, which matters on a PC with several network cards. As soon as it is constructed, it starts ReceiveLoop as a background task, which listens continuously with ReceiveAsync.

Before the handshake, IsReachableAsync sends an ICMP ping with a 500 ms timeout, to tell a cabling problem apart from a protocol problem. For the measurement, MeasureLatencyAsync creates a TaskCompletionSource<bool>, starts a Stopwatch, sends a connect packet made of the header alone, and races the reply against Task.Delay(1000) with Task.WhenAny. The first datagram the loop receives completes the task and stops the measurement. Otherwise, the method returns -1, counted as a loss.

On launch, the tool asks for the box's address, port and identifier and for the local IP, checks that the box responds before sending the handshake, then opens the test menu.

Ten samples and four tiers

The Program.RunDiagnostics method runs ten measurements, with a 100 ms pause after each one. It derives the average, the minimum, the maximum, the jitter and the loss rate. The average is sorted into four tiers: under 1 ms, under 3 ms, under 7 ms and above. The diagnostics.png screenshot in the repository shows a 2.69 ms average with no loss.

Ten round-trip samples, then the summary: a 2.69 ms average, no loss, and a 0.54 ms jitter that is just the gap between the maximum and the minimum.

The menu also offers a single (100, 100) move, a left click (press, then release 50 ms later) and a circle test. The address, port and identifier are saved to config.json with System.Text.Json and offered as defaults on the next launch.

A circle that does not drift

The move packet only carries integer relative movements. Rounding the gap between two successive theoretical points would let the error pile up at every step, and the circle would slide. Tester.RunCircleTest therefore keeps the cumulative position actually sent and computes each movement from it: the deviation from the ideal path does not exceed half a unit per axis.

angle += stepSize;
double targetX = radius * Math.Cos(angle);
double targetY = radius * Math.Sin(angle);
 
int deltaX = (int)Math.Round(targetX - lastX);
int deltaY = (int)Math.Round(targetY - lastY);
 
if (deltaX != 0 || deltaY != 0)
{
    client.SendMove(deltaX, deltaY);
    lastX += deltaX; lastY += deltaY;
}

The menu runs it with a radius of 200, 100 steps per turn and a 10 ms pause per step, until a key is pressed.

What I would fix today

The receive loop releases the measurement on any datagram, without reading its content or checking that it answers the packet that was sent. A late reply to a lost sample can close the next measurement too early. I would match each reply to its request, for example with the header index if the box sends it back.

Computed as maximum minus minimum, the jitter depends only on the two extreme values. A standard deviation would be more representative. The circle test, paced by Thread.Sleep whose precision depends on the Windows timer, does not measure the send rate it actually achieves: it shows smoothness by eye.

The configuration is saved before the connection test, while the README talks about the last working configuration. The program subscribes to the OnLog event, which the client never raises. The kmbox tester.slnx solution does not reference the C# project, only a "Hello World" C++ project generated by Visual Studio, whose build files are committed. Finally, there are no automated tests, even though packet construction lends itself well to them.