Wednesday, September 26, 2007

BSD Sockets in Ruby

I love Ruby. Completely. I don't want to write in anything else. However, meeting with the BSD sockets interface left me with a sour taste. Here's what I learned. Hopefully, you'll find this post and won't waste time if you have to work with Ruby's sockets.

You can send packets with send
The method name is good for consistency, but a horrible choice in general, if you ask me, because it overrides send in Object (used to send a message to an object, same thing as calling a method).

send's parameters are a string representing the data (same encoding as in recv), and a number for flags. The number is NOT optional (messed up choice, in my opinion), so my sockets code always has send data, 0 in it.

send fails oddly
If you're seeeing in `send': symbol string may not contain `\0' (ArgumentError) then your socket is object is null. This happens because nil uses Object's send, so it thinks you're passing it a method name.

pack_sockaddr_in doesn't like localhost
Silly me, I looked the example using and I thought I can give pack_sockaddr_in any address. If I give it localhost, I get in `connect': Invalid argument - connect(2) (Errno::EINVAL).
Solution: use

Using strings for send / recv is annoying
If I'm using sockets, I'm working at the byte level. Using Fixnum arrays to represent bytes in a packet is a lot more convenient than using strings. Here are snippers for converting between the two formats. (I did Scheme at MIT, so I love map-reduce... I meant, collect-inject).

bytes > string:
block_string = { |value| value.chr }.join('')

string > bytes:
block = (0...block_string.length).map { |i| block_string[i] }

That's about it. Hope this helps someone!

1 comment:

  1. This helped someone!

    The 'send fails' tipped me off that I was attempting a lookup before being fully connected.