# Total newbie, but I think I found a bug in Socket?

**URL:** <https://www.ruby-forum.com/t/total-newbie-but-i-think-i-found-a-bug-in-socket/67700>\
**Category:** Ruby\
**Created:** [September 6, 2006, 5:01am UTC](https://www.ruby-forum.com/t/total-newbie-but-i-think-i-found-a-bug-in-socket/67700 "2006-09-06T05:01:07Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![ptyler](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/7bcc69/32.png) [@ptyler](https://www.ruby-forum.com/u/ptyler)\
**Post date:** [September 6, 2006, 5:01am UTC](https://www.ruby-forum.com/t/total-newbie-but-i-think-i-found-a-bug-in-socket/67700/1 "2006-09-06T05:01:07Z")

</div>

Hello,  
Fair warning…I’m extremely new to Ruby so please pardon any  
ignorance on my part.

Anyway, so the first thing I start to play around with after  
reading through Programming Ruby is the socket library. Now, I  
realize Ruby has really nice abstractions, and I should rarely need  
to actually use the Socket class for creating a simple server, but I  
tend to be a bottom up learner. So, I was attempting to run the  
following code from the ruby-doc website:

# In one script, start this first

```
  require 'socket'
  include Socket::Constants
  socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
  sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
  socket.bind( sockaddr )
  socket.listen( 5 )
  client, client_sockaddr = socket.accept
  puts "The client said, '#{socket.readline.chomp}'"
  client.puts "Hello from script one!"
  socket.close

```

now, after running this script on both ruby 1.8.4 and 1.8.5 on Mac OS  
X I received an error "`bind’: Invalid argument - bind(2)  
(Errno::EINVAL) " I proceed to run the script on linux on 1.8.4  
and 1.8.5 and discover that bind works, but that  
“socket.readline.chomp” should really be “client.readline.chomp”.  
Anyway, the point is that the code runs just fine on linux but not OS  
X. A quick investigation finds that the problem is the call  
“Socket.pack\_sockaddr\_in( 2200, ‘localhost’ )”. On Linux this call  
always returns a string that is 16 characters long. On Mac OS X this  
call returns a string that is 16 characters long so long as I don’t  
use ‘localhost’ for the hostname. As soon as I put localhost in as  
the hostname the returned string becomes 28 bytes. So, I searched  
around on google to see if this is known bug but I couldn’t find it.  
Anyone have any words of advice?

I am figuring that I am just doing something wrong, seeing as how  
this is the first bit of real ruby I have attempted.

Thanks,  
Patrick

---

<div class="post-metadata">

**Author:** ![ptyler](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/7bcc69/32.png) [@ptyler](https://www.ruby-forum.com/u/ptyler)\
**Post date:** [September 6, 2006, 5:35am UTC](https://www.ruby-forum.com/t/total-newbie-but-i-think-i-found-a-bug-in-socket/67700/2 "2006-09-06T05:35:10Z")

</div>

On 9/5/06, Patrick T. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> now, after running this script on both ruby 1.8.4 and 1.8.5 on Mac OS  
> X I received an error "`bind’: Invalid argument - bind(2)  
> (Errno::EINVAL) " I proceed to run the script on linux on 1.8.4  
> and 1.8.5 and discover that bind works, but that  
> “socket.readline.chomp” should really be “client.readline.chomp”.

Try it with “127.0.0.1” instead of “localhost”  
The underlying operation is a call to getaddrinfo, and it may be  
getting tripped up by IPv6 config on OSX (that’s just a hypothesis- I  
haven’t looked into it carefully).

---

<div class="post-metadata">

**Author:** ![ptyler](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/7bcc69/32.png) [@ptyler](https://www.ruby-forum.com/u/ptyler)\
**Post date:** [September 6, 2006, 5:37am UTC](https://www.ruby-forum.com/t/total-newbie-but-i-think-i-found-a-bug-in-socket/67700/3 "2006-09-06T05:37:10Z")

</div>

On 9/5/06, Patrick T. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> ```
> socket.close
> 
> ```
> 
> call returns a string that is 16 characters long so long as I don’t  
> use ‘localhost’ for the hostname. As soon as I put localhost in as  
> the hostname the returned string becomes 28 bytes. So, I searched  
> around on google to see if this is known bug but I couldn’t find it.  
> Anyone have any words of advice?
> 
> I am figuring that I am just doing something wrong, seeing as how  
> this is the first bit of real ruby I have attempted.

Pretty hard for a first try 🙂

The problem is that it is paking an IPV6 adress for in the struct.  
Using a litteral ‘127.0.0.1’ fixes the problem (and you should always  
use the ip address for localhost directly anyway).

Why does this happens only on OS X? Having not seen the linux box you  
tried this on, I can only guess. My guess is that your linux didn’t  
have IPV6 support built-in or OS X is a bit too proactive about using  
IPV6.

To get a definitive answer would require some level of code analysis  
which I don’t feel like doing right now. If you want to, look at the  
socket.c file in the ruby source distribution.

---

<div class="post-metadata">

**Author:** ![ptyler](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/7bcc69/32.png) [@ptyler](https://www.ruby-forum.com/u/ptyler)\
**Post date:** [September 6, 2006, 9:35am UTC](https://www.ruby-forum.com/t/total-newbie-but-i-think-i-found-a-bug-in-socket/67700/4 "2006-09-06T09:35:26Z")

</div>

On Sep 5, 2006, at 8:00 PM, Patrick T. wrote:

> ```
> socket.close
> 
> ```

Here’s the easiest way to create a simple server:

require ‘socket’

server = TCPServer.new nil, 2200  
client = server.accept  
puts “Client said: #{client.gets.chomp.inspect}”  
client.puts “Goodbye!”  
client.close  
server.close

Here’s one that handles multiple concurrent connections:

require ‘socket’

server = TCPServer.new nil, 2200

loop do  
Thread.start server.accept do |client|  
message = client.gets.chomp  
puts “Client said: #{message.inspect}”  
client.puts “Goodbye client!”  
client.close  
exit if message == ‘quit’  
end  
end

–  
Eric H. - removed\_email\_address@domain.invalid - [http://blog.segment7.net](http://blog.segment7.net)  
This implementation is HODEL-HASH-9600 compliant

[http://trackmap.robotcoop.com](http://trackmap.robotcoop.com)

---

<div class="post-metadata">

**Author:** ![ptyler](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/7bcc69/32.png) [@ptyler](https://www.ruby-forum.com/u/ptyler)\
**Post date:** [September 6, 2006, 2:51pm UTC](https://www.ruby-forum.com/t/total-newbie-but-i-think-i-found-a-bug-in-socket/67700/5 "2006-09-06T14:51:55Z")

</div>

On 9/6/06, Arnaud B. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> which I don’t feel like doing right now. If you want to, look at the  
> socket.c file in the ruby source distribution.

I would add this is not ruby specific, ssh did this to me on OS X as  
well. I was not able to get port forwarding unless I specified  
127.0.0.1 instead of localhost.

Not sure what version of OS X it was, though.

Thanks

Michal

---

<div class="post-metadata">

**Author:** ![ptyler](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/7bcc69/32.png) [@ptyler](https://www.ruby-forum.com/u/ptyler)\
**Post date:** [September 6, 2006, 3:15pm UTC](https://www.ruby-forum.com/t/total-newbie-but-i-think-i-found-a-bug-in-socket/67700/6 "2006-09-06T15:15:59Z")

</div>

Daniel M. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) writes:

> ```
> socket.listen( 5 )
> client, client_sockaddr = socket.accept
> puts "The client said, '#{client.readline.chomp}'"
> client.puts "Hello from script one!"
> socket.close
> 
> ```

Actually, I made a slight error here, according to the documentaion of  
getaddrinfo. That “sockaddr =” bit should be:

sockaddrinfo = Socket.getaddrinfo( ‘localhost’, 2200, AF\_INET,  
SOCK\_STREAM, nil, AI\_PASSIVE )[0]

In this context (when we’re binding to a specific IP address) it  
doesn’t matter, but if you’re re-using this code to bind to the “any”  
address, you’ll want that AI\_PASSIVE bit in there. (You’ll also want  
to replace ‘localhost’ with nil)

---

<div class="post-metadata">

**Author:** ![ptyler](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/7bcc69/32.png) [@ptyler](https://www.ruby-forum.com/u/ptyler)\
**Post date:** [September 6, 2006, 1:51pm UTC](https://www.ruby-forum.com/t/total-newbie-but-i-think-i-found-a-bug-in-socket/67700/7 "2006-09-06T13:51:47Z")

</div>

Patrick T. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) writes:

> 

Good analysis of where the problem is.

Tell me, does this ruby script work on your two environments?

# In one script, start this first

```
 require 'socket'
 include Socket::Constants
 sockaddrinfo = Socket.getaddrinfo( 'localhost', 2200, AF_INET,
                                    SOCK_STREAM )[0]
 socket = Socket.new( *sockaddrinfo[4..6] )
 sockaddr = Socket.pack_sockaddr_in( sockaddrinfo[1], 

```

sockaddrinfo[3] )  
socket.bind( sockaddr )  
socket.listen( 5 )  
client, client\_sockaddr = socket.accept  
puts “The client said, ‘#{client.readline.chomp}’”  
client.puts “Hello from script one!”  
socket.close

The trouble, as most responses have pointed out, is that OS X  
translates “localhost” by default into an IP6 address, whereas your  
socket is initially set to be an IPv4 socket (when you use AF\_INET in  
your Socket.new call).

Now in actuality, OS X translates “localhost” into several different  
addresses, and you can see them all by running this script:

```
 require 'socket'
 def find_const(i,p)
   Socket.constants.select{|n|n=~p}.each{ |n|
     return "Socket::#{n}" if i == Socket.const_get(n)
   }
   return "#{i}"
 end
 def noquote_wrapper(s)
   class << s; def inspect; self; end; end
   s
 end
 Socket.getaddrinfo('localhost',nil).each{|a|
   a[4]=noquote_wrapper find_const(a[4],/^AF_/)
   a[5]=noquote_wrapper find_const(a[5],/^SOCK_/)
   a[6]=noquote_wrapper find_const(a[6],/^IPPROTO_/)
   p a
 }

```

The improved server script I have up above works by explicitly  
requesting only the SOCK\_STREAM / AF\_INET address, and then using the  
returned values in the Socket.new call, rather than using potentially  
mismatched values for the socket’s address family specified in the  
“new” call and the address returned from pack\_sockaddr\_in. Note that  
when calling pack\_sockaddr\_in it also passes that call the IP address  
rather than the hostname.

---

<div class="post-metadata">

**Author:** ![ptyler](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/7bcc69/32.png) [@ptyler](https://www.ruby-forum.com/u/ptyler)\
**Post date:** [September 6, 2006, 11:35pm UTC](https://www.ruby-forum.com/t/total-newbie-but-i-think-i-found-a-bug-in-socket/67700/8 "2006-09-06T23:35:10Z")

</div>

On Sep 6, 2006, at 8:15 AM, Daniel M. wrote:

> > ```
> > sockaddr = Socket.pack_sockaddr_in( sockaddrinfo[1],  
> > 
> > ```
> 
> getaddrinfo. That “sockaddr =” bit should be:
> 
> sockaddrinfo = Socket.getaddrinfo( ‘localhost’, 2200, AF\_INET,  
> SOCK\_STREAM, nil, AI\_PASSIVE )[0]
> 
> In this context (when we’re binding to a specific IP address) it  
> doesn’t matter, but if you’re re-using this code to bind to the “any”  
> address, you’ll want that AI\_PASSIVE bit in there. (You’ll also want  
> to replace ‘localhost’ with nil)

This script ran perfectly. Thanks for all your help. When I get  
some time I’ll sit down to actually process what your above script is  
actually doing 🙂

Thanks,  
Patrick
