If you're expressing things in bits/s or bytes/s please use 1000. If you're expressing the size of a file, please use 1024 bytes. A general approach is to refer to network speeds in bits/sec ("100 megabits/sec") and to refer to file sizes in bytes ("a 10 gigabyte file"), to help emphasize that one is using the networking convention and the other the 1024 convention.
An alternative option, which you are welcome to use (and, in fact, encouraged), is to refer to the file sizes using the binary prefixes "kibi" (Ki), "mebi" (Mi), "gibi" (Gi), and so on. Wikipedia has a nice article about the SI prefixes in computing. They are now an official IEEE standard, and it would be really great if their use took off.
There are two errors of interest: EPIPE and ECONNRESET. When a client disconnects, your program will get a SIGPIPE and errno will be set to EPIPE. Signals have default handlers unless you specify your own. SIGPIPE will simply exit, an undesirable feature. I suggest ignoring SIGPIPE:
signal(SIGPIPE, SIG_IGN); /* Block SIGPIPE Signals */
This will prevent your server from dying, but select() will still mark the fd as readable/writable (section 2.22 of the above link).
If read() returns -1 and sets errno to ECONNRESET ("Connection reset by peer"), you should remove the client. The other case of read() is EOF (ie 0), which also means the connection is closed, and you should remove the client. But remember that for robustness, your server should gracefully handle any failure returned by read.
If send() or write() returns -1 and sets errno to EPIPE, then you should remove the client.
You can assume that rt_sendto is non-blocking, because it's UDP. If a UDP socket is over-full, it just starts dropping packets. So it may fail, but it shouldn't block. That said, if you already have code that handles a buffered socket for writing stuff, you're welcome to reuse it - it will just never block.
You may not assume that rt_recvfrom is non-blocking. It will block if there are no packets waiting.
For example:
(Note: This is only to give you the idea of how to do it!)
... somewhere earlier, you defined a struct with the packet contents ... struct foo { u_int32_t type; u_int32_t last_node_id; u_int32_t seqnum; char nick[16]; ... } /* Convert host to network */ /* packet is a char * pointing to space for the packet */ struct foo *p; p = (struct foo *)packet; p->type = htonl( type ); p->last_node_id = htonl(last_node_id); p->seqnum = htonl( seqnum ); strncpy(p->nick, nick, 16);
To unmarshal the messages at the receiver:
type = ntohl(p->type); last_node_id = ntohl(p->last_node_id); seqnum = ntohl(p->seqnum); strncpy(nick, p->nick, 16);
Just set the timeval.tv_sec and timeval.tv_usec before calling select. You should not rely on the values in this structure after calling select (see earlier question about timers). We suggest something like:
struct timeval tv_start, tv_end; gettimeofday(&tv_start); select(...); gettimeofday(&tv_end); and then, if you need time_waited = tv_difference(&tv_start, &tv_end);
(tv_difference is a function you have to write, but it's trivial).
See the man page for connect for more details.
Note that the above is not true for UDP. UDP sends provide strict message boundaries. What you send in one sendto call is exactly what you receive at the other end in a single recvfrom call.
Last updated: Mon Aug 25 22:30:52 -0400 2008 [validate xhtml]