API client: send encoded length

Hello folks.

I’m developing my own C++ client for MikroTik RouterOS API which I will use in some projects. (I don’t liked that existing one)
I’ve a question:

Example client provided by MikroTik on Wiki page sends each byte of encoded length at a time to the router. Is this really need? Can’t I send entire encoded length at once?
From given explanation of API communication, I could understand that I need to send lower one, two or three bytes, etc. but isn’t said they must be sent one by one in 1 byte length message. I only discovered this reading example client code.
I was thinking in to do the following:

If length < 0x200000, I need to send two lower bytes. So I send lenght & 0xffff (Allocate 2 bytes memory space to store it if need).

Won’t it work this way? That manner in Wiki hurts what I’ve as best practices.


Bye.

Yes. It will work as specified, i.e. normally.

The current Wiki client is sending one byte at a time, merely because that’s easier to write such code, not because it’s better in any way.


Keep in mind that when sending/receiving more than one byte at a time, you should check how much data was actually sent/received, as it may be less than the one you’ve specified. This is a common problem in many API clients, I think the C++ one included.


BTW, have you checked the other C++ client? The one in the “Elsewhere” section? Is that one not good either? (I haven’t personally used any C++ client; API wise, it seems slightly more sophisticated than the Wiki one though…)

Thanks for your answer.

Is there any need to allocate a memory space with encoded length size, to send a message with exact byte amount? This question comes from the use of chr() in example code to send 1 byte messages, instead of 4 bytes with 3 of them all 0.

1 byte without chr(): 00000000000000000000000011111111
1 byte using chr(): 11111111

3 bytes without chr(): 00000000111111111111111111111111
3 bytes using allocated 3 bytes memory space: 111111111111111111111111

Will this make any difference on any side (client or server/router)? Why does example client uses self.writeStr(chr(l)) instead of simply self.writeStr(l) or self.writeStr(l & 0xff), at least on fist if block (< 0x80)?

Ok. But what should I do when send() returns different quantity from what I sent? I guess send it again can cause problems…

In the “elsewhere” section there are only C and C# APIs. Although it’s possible to use C code within C++ applications, it’s not a good practice, since C does things in old and bad way. C# one is totally incompatible (.NET, Windows environment only and sintax differences).


Bye.

Is there any need to allocate a memory space with encoded length size, to send a message with exact byte amount? This question comes from the use of chr() in example code to send 1 byte messages, instead of 4 bytes with 3 of them all 0.

1 byte without chr(): 00000000000000000000000011111111
1 byte using chr(): 11111111

Yeah, you need to send the exact amount of bytes that the encoded length is. In other words, that second approach is the correct one. Padding it with zero bytes will make RouterOS think the length is 0.

Will this make any difference on any side (client or server/router)? Why does example client uses self.writeStr(chr(l)) instead of simply self.writeStr(l) or self.writeStr(l & 0xff), at least on fist if block (< 0x80)?

Because the author of the original client was somewhat lazy, and writing it byte by byte is easier than allocating the correct number of bytes, processing the string into the correct value, and sending the entire string.

Ok. But what should I do when send() returns different quantity from what I sent? I guess send it again can cause problems…

You check how many bytes were sent, and resend from that byte onwards to the end.

Similarly when receiving - you check how many bytes were received, and keep receiving until the length is satisfied.

Side note: This sort of “quirk” is actually why MikroTik have defined the length in such terms - the very first byte tells how many more bytes to wait for, so you know how much to receive a length, and then the length tells you how much to receive the word. Without such consistency, you wouldn’t know for sure where the length ends and when the content begins. The alternative consistent solution would’ve been to have the length be “fixed” to 4 bytes, which would work, except that it forbids potential future expansion (which are currently possible via the currently unused “control bytes”), plus, in most cases you write words with 1 byte length, so this adds unnecessary 3 bytes to each word, requiring more bandwidth between client and RouterOS.

Although it’s possible to use C code within C++ applications, it’s not a good practice, since C does things in old and bad way.

I would’ve suggested making a C++ wrapper around that C client, but come to think of it, that client, while better than the Wiki one in every way, is not THAT perfect to warrant this.

If you want to make a RouterOS API client useful beyond C++, I’d suggest you make a new C client, and wrap the C++ code around that.

Why do that? Because other languages (I’m thinking PHP) can be extended by C only, though others can use either C or C++ (JAVA, C#…). Of course, as far as C++ apps go, I guess a pure C++ application would be “cleaner” (in terms of code base; in terms of compiled code, I’m sure it could be optimized to the point where there’s absolutely no measurable difference), so it’s your call.

C way of doing things is hard to accomplish and obsolete. C++ does better memory spaces and pointers managing, etc.
Maybe I use #if’s to add C compatible implementation.

I can’t think on any kind of PHP (or other language) extension that uses RouterOS API.


Thanks for your answers. Now I can proceed developing my API client.
Karma has been added. :slight_smile:

Maybe I use #if’s to add C compatible implementation.

Better than nothing I guess, but that would still require the user to have a C++ compiler alongside their C one :confused: .

I can’t think on any kind of PHP (or other language) extension that uses RouterOS API.

Yet…

Not in C/C++ currently, yes. All API clients are currently written in the actual language they’re used in.

There’s no PHP extension yet to implement RouterOS API, exactly because there’s no good C API client, and that’s what’s needed before someone could write a PHP extension around it.

A similar thing can be said about Java, .NET and the rest, though the difference there is that there’s less of a reason to pursue that, since it would yield less of a performance boost, compared to the significant one it would be in PHP and other normally interpreted languages.

Not right. It will require just a C compiler. In other words, same code can be compiled as C or C++. C compiler will read only the code I put in #if statements that match C language.

I can’t think on something written in PHP that needs so high performance in communicating to a MikroTik router to warrant a PHP extension but, anyway, I’ll try (it’s harder) to write a C compatible API client.

Oh? That’s different from what I’ve seen in projects that “start off” as C++ projects. But if you can pull off what you say there, that’s great :smiley: .

It never hurts to have better performance :stuck_out_tongue: … Well, except if it’s at the price of a vital feature being absent I guess, but “all in good time”.



#ifdef __cplusplus
	//C++ way of doing something, e.g.:
	int* allocate_integer(int* value, int bytes) {
		//do it using smart pointers (C++ only)
	}
#else
	//C way of doing something
	int* allocate_integer(int* value, int bytes) {
		//do it the manual and old way, using malloc
	}
#endif

When writing some code that’s equal in C and C++, there’s no need for this.

C++ compiler will read only the code within #ifdef block, whereas C compiler will read only that one within #else block.

I’m aware of how this directive works…

My point is that most projects tend to not do the above, because that is essentially writing the same algorithm twice, making it harder to maintain. Wrapping one language’s API over the other makes it easier to debug and maintain.

The sample function you game makes me also warn you… Keep an eye out for binary string safety. The RouterOS API protocol allows null bytes in word contents, but current C/C++ clients seem to not allow those, by virtue of accepting standard null terminated strings as arguments. Granted, people rarely need to write a null byte in a word, so for user convenience, you’ll want to have both binary safe and binary unsafe functions/methods which are binary safe under the hood. Either way, you should support binary safety for cases when a user reads something with null bytes in it (e.g. a file’s contents).