views:

167

answers:

4

I have to transfer some values to be used as commands over the network, and I wish to make it as efficient and robust as possible, i.e. I need opinions on what to use for these commands, #defines or enums?

The range of commands shall not be more than 20 commands(lets say 40 with every defined response to the command), so according to most things I've heard, it would fit well in the char limit.

So far, I'm assuming that the best approach would be to use an enum.

A: 

You want opinions? My opinion is that you should use a text-based protocol, where each of the commands is a keyword. Whether and how you want to convert them to/from enums in your program is up to you, but on the network level, using text-based commands means your protocol is more extensible.

A lot of real-life protocols are text-based, and I dare say they're pretty efficient for what they do. e.g., HTTP, FTP, SMTP, etc., are all text-based.

Chris Jester-Young
Much easier to debug too.
freespace
HTTP and SMTP are not very efficient for anything besides text, and FTP's actual data transfer is binary. Those real-life text protocols are good because they're simple, not because they're "pretty efficient".
Tom
+4  A: 

When transferring numeric values across the internet, you have 2 things to bear in mind.

The first is the endianess, the order that bytes (or sometimes bits) appear in. Suppose you have the 32-bit value 0xAABBCCDD. Intel CPUs are little-endian machines, which mean those bytes would be stored as { 0xDD, 0xCC, 0xBB, 0xAA }. In other words, the least significant byte is stored at the lowest address. In a big-endian machine, the bytes would be stored as { 0xAA, 0xBB, 0xCC, 0xDD }, with the least significant byte at the highest address.

When transferring multi-byte integers between 2 machines, you have to ensure that they both interpret each others data correctly, even if they have different byte orderings. Thankfully, there is a standard called Network byte order, which is big-endian, and there are 4 useful functions for converting between host-order and network-order:

ntohl (Network to Host, long)
ntohs (Network to host, short)
htonl (Host to network, long)
htons (Host to network, short)

The long versions work with 32-bit integers and the short versions with 16-bit integers. As long as you always call *hton** before transmitting data across the network and call *ntoh** when reading from the network, data will be in the correct byte order.

Of course, the easiest way to get around this problem, especially since you have only 20 commands, is to just use single bytes, or chars.

The second problem you have to deal with is encoding. How are signed integers represented? Using a sign-bit? Twos complement? Again, you run in to problems when different platforms on the network use different representations. If you stick to unsigned types, you shouldn't really have a problem.

What you use to represent your commands in your program is entirely up to you. Just make sure that you define your protocol well and adhere to that when transmitting and reading data.

For example:

enum command_t { one, two, three }

void send_command(command_t c) {
    send((unsigned char)c);
}

command_t read_command() {
    return (command_t)recv();
}

void send(unsigned char c) { ... }
unsigned char recv() { ... }
IRBMe
I think in this rather storing each command in unsigned char variable and equating it to a value, i shall store it in #define, as it has always been done. I think now i understand why #define have always been used for error-codes, state-names, commands... etc.
Vivek Sharma
+1  A: 

From an efficiency standpoint, it makes no difference whatsoever. #define or enum is just a way of assigning a numerical value... the real trick is how you pack that value onto the network.

That said, I agree with the other answers I see here, and I think you are worrying about the wrong thing anyway. Designing a network protocol takes thought, and making it portable and usable across many systems should generally override efficiency concerns (there are exceptions to that rule, of course).

Chris Arguin
+1  A: 

I would also recommend using a line-delimited text-based protocol. Aside from not having to worry about enum-vs.#define values, there are some other benefits:

hacking/testing

It's very easy to manually generate a set of commands for text-based protocols. Consider testing error conditions on an HTTP server:

$ telnet www.yahoo.com 80
Trying 69.147.76.15...
Connected to www-real.wa1.b.yahoo.com.
Escape character is '^]'.
GOAT / HTTP/1.1

I don't need anything fancy to understand how the server responded:

HTTP/1.1 400 Bad Request
Date: Sat, 11 Jul 2009 16:20:59 GMT
Cache-Control: private
Connection: close
Transfer-Encoding: chunked
Content-Type: text/html; charset=iso-8859-1

This is great for debugging. You can create a set of test scripts to feed into a telnet/nc and they'll be much easier to maintain than any binary-protocol test scripts.

portability

If you define your protocol in terms of ASCII text (or maybe UTF-8), you never have to worry about lower-level issues on new platforms. Endianness, structure alignment, and word size are all issues when dealing with a binary protocol. ASCII may require an extra serialize/deserialize step, but in a networking protocol that's generally necessary anyways.

sharing

If you want to work with other people on a project, it's great to be able to describe network operations as plain-ole-text. If someone else wants to develop their own client, there's no requirement for them to use your header files or anything, they just have to adhere to an RFC-like standard. This is not useful for a pet project, but it's a good thing to consider for anything that may get larger.

Tom