For my network architecture class, our programming “project” was to implement an FTP client in C using unix sockets. We were given almost a month to do it, and I kept putting it off, thinking, oh, that’s gotta be so easy, all you have to do is shovel a few bits across the wire. I was pretty confident I could do it. That, coupled with the fact that things have been busy at work, meant that I started the assignment 19 days after it was assigned, also known as the day before it was due.
It also happens to be a pretty worthless exercise, since (a) unix sockets abstract away a lot of the TCP layer, meaning I don’t learn as much as I should, (b) it’s in C. I thought I’d never had to write another malloc! The only hard thing about this assignment was dealing with the fact that every freaking line I wrote had the potential to “error out”, a situation that I had to handle gracefully while making sure every socket & piece of memory I allocated was cleaned up.
I did it with a hacky macro I called “EXEC_OR_DIE”:
#define EXEC_OR_DIE(command, cleanup, status) \
if(command) { \
cleanup; \
return status; \
}
Only later did I find this, an implementation of try/catch/finally in C. So how did I use this macro? Liberally. A typical method might look like this:
// socket
EXEC_OR_DIE((*dataHandle = socket(AF_INET, SOCK_STREAM, 0)) < 0,
perror("ERROR: failed to open data stream socket"),
METHOD_FAILURE);
// bind
EXEC_OR_DIE(bind(*dataHandle, (struct sockaddr *) &servAddr;, sizeof (servAddr)) < 0, {
perror("ERROR: failed to create listening port");
close(*dataHandle);
}, METHOD_FAILURE);
// listen
EXEC_OR_DIE(listen(*dataHandle, BACKLOG) < 0, {
perror("ERROR: failed listening to port");
close(*dataHandle);
}, METHOD_FAILURE);
// getsockname
size = sizeof(servAddr);
EXEC_OR_DIE(getsockname(*dataHandle, (struct sockaddr *) &servAddr;, &size;) < 0,{
perror("ERROR: failed to get listening port information");
close(*dataHandle);
}, METHOD_FAILURE);
If you get rid of all of the error handling, all I’m doing is the following five lines:
socket(...)
bind(...)
listen(...)
size = sizeof(...)
getsockname(...)
Pretty gross. In my 640 line .c file, I have 55 calls to EXEC_OR_DIE. At 3 lines per call, that’s 25% of my code right there.
The other notable thing (the only academically interesting thing, I guess, besides the vagaries of the FTP protocol), is the magic of sockets. At a level below “I just open my browser and it works woo”, sockets allow you to create point-to-point links between two processes on the same machine or on different machines and transfer arbitrary bits. I’ve used sockets on Windows and Unix, Java, C, PHP, and Python, and there’s a good reason for that: they’re very powerful.
Anyways, the assignment (640 line .c file, 90 line .h file) took an hour to plan and 12 hours to write. I’m all done!


before I made this post, genderanalyzer.com said:
We think http://blog.lv25.com is written by a man (68%).
Afterwards:
We think http://blog.lv25.com is written by a man (82%).
=)
Hey that first estimate is pretty close, since 2/3 of the bloggers are men! Except I seem to have a disproportionate number of posts in here, so…what the heck does that say about my writing… O_o;
great post.