# Socket.rb

**URL:** <https://www.ruby-forum.com/t/socket-rb/157385>\
**Category:** Development\
**Created:** [January 10, 2009, 12:12pm UTC](https://www.ruby-forum.com/t/socket-rb/157385 "2009-01-10T12:12:26Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![Tanaka\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c67d28/32.png) [@Tanaka\_A](https://www.ruby-forum.com/u/Tanaka_A)\
**Post date:** [January 10, 2009, 12:12pm UTC](https://www.ruby-forum.com/t/socket-rb/157385/1 "2009-01-10T12:12:26Z")

</div>

Socket e$B$r;H$C$F$$$k$H!"@8@.$9$k$H$3$m$,e(B TCPSocket e$BEy$KHf$Y$Fe(B  
e$BITJX$J$N$G!"0J2\<$N$h$&$J%a%=%C%I$r$D$1$F$b$$$$$G$7$g$&$+!#e(B

Socket.tcp(host, port, local\_host=nil, local\_port=nil) {|socket| … }  
Socket.tcp\_server\_loop(host=nil, port) {|socket, client\_addrinfo| …  
}  
Socket.unix(path) {|socket| … }  
Socket.unix\_server\_loop(path) {|socket, client\_addrinfo| … }

Socket.tcp e$B$He(B Socket.unix e$B$Oe(B  
TCPSocket.open e$B$He(B UNIXSocket.open e$B$KBP1~$7$^$9!#e(B

Socket.tcp\_server\_loop e$B$He(B Socket.unix\_server\_loop e$B$Oe(B  
TCPServer.open, UNIXServer.open e$B$K2C$($F!"e(Baccept e$B$9$k%k!\<%We(B  
e$B$^$GF~$C$F$$$^$9!#e(B

accept e$B$^$G9T$&$N$O!"e(BTCPServer.open e$BFb$G!“e(Bgetaddrinfo  
e$B$,JV$7e(B  
e$B$?$9$Y$F$N%”%I%l%9$r07$&$?$a$G$9!#%5!\<%P%=%1%C%H$r8F=P85$KJVe(B  
e$B$9e(B TCPServer.open e$B$N7A$@$H!“e(BIPv4 e$B$He(B IPv6  
e$B$rJ,$1$F07$&$3$H$,e(B  
e$B$G$-$^$;$s!#JL!9$K%=%1%C%H$r:n$C$FJ,$1$F07$&$H$$$&$N$,e(B  
RFC 4038 e$B$N$_A&$a$J$h$&$G$9!#$J$_!”\>-MhE\*$K$O!"0z?t$r3HD%$7e(B  
e$B$Fe(B port e$B$rJ#?t;XDj$G$-$k$H$+$7$F$b$$$$$+$b$7$l$^$;$s!#e(B

e$B$“$H!“e(BSocket.unix\_server\_loop e$B$O4{B8$N%=%1%C%H%U%!%$%k$re(B  
(owner e$B$N8!::$r$7$?\>e$Ge(B) e$B\>C$9$h$&$K$7$F$”$j$^$9!#e(Bone liner  
e$B$G%F%9%H$9$k$H$-$K$O$=$C$A$N$[$&$,JXMx$G$9$7!”$^$8$a$K%5!\<%Pe(B  
e$B$r=q$/$J$i$I$&$;B\>$N%5!\<%P$,@8$-$F$$$k$+$I$&$+$OJL$N\<jCJ$G3Ne(B  
e$BG’$7$J$1$l$P$J$i$:!"B\>$K%5!\<%P$,$$$J$$\>u67$G8F$S=P$9$H$9$l$Pe(B  
e$B\>C$7$A$c$C$FLdBj$J$$$H;W$&$N$G!#e(B

e$B\<BAu$Oe(B Ruby e$B$G=q$$$F$"$j$^$9$N$G!"e(Bsocket.rb  
e$B$N?7@\_$K$J$j$^$9!#e(B

# % svn diff --diff-cmd diff -x ‘-u -p’ Index: ext/socket/lib/socket.rb

— ext/socket/lib/socket.rb (revision 0)  
+++ ext/socket/lib/socket.rb (revision 0)  
@@ -0,0 +1,262 @@  
+require ‘socket.so’  
+  
+class AddrInfo

- 
# iterates over the list of AddrInfo objects obtained by

AddrInfo.getaddrinfo.

- 
# 
- 
# Example:
- 
# 
- 
# AddrInfo.foreach(nil, 80) {|x| p x }
- 
# #=\> #\<AddrInfo: 127.0.0.1:80 TCP (:80)\>
- 
# # #\<AddrInfo: 127.0.0.1:80 UDP (:80)\>
- 
# # #\<AddrInfo: [::1]:80 TCP (:80)\>
- 
# # #\<AddrInfo: [::1]:80 UDP (:80)\>
- 
# 
- def self.foreach(nodename, service, family=nil, socktype=nil,  
protocol=nil, flags=nil, &block)
- AddrInfo.getaddrinfo(nodename, service, family, socktype, protocol,  
flags).each(&block)
- end  
+end
- 

+class Socket

- 
# creates a new socket object connected to host:port using TCP.
- 
# 
- 
# If local\_host:local\_port is given,
- 
# the socket is bound to it.
- 
# 
- 
# If a block is given, the block is called with the socket.
- 
# The value of the block is returned.
- 
# The socket is closed when this method returns.
- 
# 
- 
# If no block is given, the socket is returned.
- 
# 
- 
# Example:
- 
# 
- 
# Socket.tcp(“[www.ruby-lang.org](http://www.ruby-lang.org)”, 80) {|sock|
- 
# sock.print “GET / HTTP/1.0\r\n\r\n”
- 
# sock.close\_write
- 
# print sock.read
- 
# }
- 
# 
- def self.tcp(host, port, local\_host=nil, local\_port=nil) # :yield:  
socket
- last\_error = nil
- ret = nil
- 
- local\_addr\_list = nil
- if local\_host != nil || local\_port != nil
- 

```
 local_addr_list = AddrInfo.getaddrinfo(local_host, local_port, 

```

nil, :STREAM, nil)

- end
- 
- AddrInfo.foreach(host, port, nil, :STREAM).each {|ai|
- 

```
 begin

```

- 

```
   sock = self.new(ai.pfamily, ai.socktype, ai.protocol)

```

- 

```
 rescue SystemCallError

```

- 

```
   last_error = $!

```

- 

```
   next

```

- 

```
 end

```

- 

```
 if local_addr_list

```

- 

```
   if local_addr = local_addr_list.find {|local_ai| 

```

local\_ai.afamily == ai.afamily }

- 

```
     begin

```

- 

```
       sock.bind(local_addr)

```

- 

```
     rescue SystemCallError

```

- 

```
       last_error = $!

```

- 

```
       next

```

- 

```
     end

```

- 

```
   else

```

- 

```
     next

```

- 

```
   end

```

- 

```
 end

```

- 

```
 begin

```

- 

```
   sock.connect(ai)

```

- 

```
 rescue SystemCallError

```

- 

```
   last_error = $!

```

- 

```
   sock.close

```

- 

```
   next

```

- 

```
 end

```

- 

```
 ret = sock

```

- 

```
 break

```

- }
- if !ret
- 

```
 if last_error

```

- 

```
   raise last_error

```

- 

```
 else

```

- 

```
   raise SocketError, "no appropriate local address"

```

- 

```
 end

```

- end
- if block\_given?
- 

```
 begin

```

- 

```
   yield ret

```

- 

```
 ensure

```

- 

```
   ret.close if !ret.closed?

```

- 

```
 end

```

- else
- 

```
 ret

```

- end
- end
- 
- 
# creates a TCP server on _port_ and calls the block for each

connection accepted.

- 
# The block is called with a socket and a client\_address as an

AddrInfo object.

- 
# 
- 
# If _host_ is specified, it is used with _port_ to determine the

server addresses.

- 
# 
- 
# The socket is _not_ closed when the block returns.
- 
# So application should close it explicitly.
- 
# 
- 
# This method calls the block sequentially.
- 
# It means that the next connection is not accepted until the block

returns.

- 
# So concurrent mechanism, thread for example, should be used to

service multiple clients at a time.

- 
# 
- 
# Note that AddrInfo.getaddrinfo is used to determine the server

socket addresses.

- 
# When AddrInfo.getaddrinfo returns two or more addresses,
- 
# IPv4 and IPv6 address for example,
- 
# all of them are used.
- 
# Socket.tcp\_server\_loop succeeds if one socket can be used at least.
- 
# 
- 
# Example:
- 
# 
- 
# # Sequential echo server.
- 
# # It services only one client at a time.
- 
# Socket.tcp\_server\_loop(16807) {|sock, client\_addrinfo|
- 
# begin
- 
# IO.copy\_stream(sock, sock)
- 
# ensure
- 
# sock.close
- 
# end
- 
# }
- 
# 
- 
# # Threaded echo server
- 
# # It services multiple clients at a time.
- 
# Socket.tcp\_server\_loop(16807) {|sock, client\_addrinfo|
- 
# Thread.new {
- 
# begin
- 
# IO.copy\_stream(sock, sock)
- 
# ensure
- 
# sock.close
- 
# end
- 
# }
- 
# }
- 
# 
- def self.tcp\_server\_loop(host=nil, port) # :yield: socket,  
client\_addrinfo
- last\_error = nil
- sockets = []
- AddrInfo.foreach(host, port, nil, :STREAM, nil, Socket::AI\_PASSIVE)  
{|ai|
- 

```
 begin

```

- 

```
   s = self.new(ai.pfamily, ai.socktype, ai.protocol)

```

- 

```
 rescue SystemCallError

```

- 

```
   last_error = $!

```

- 

```
   next

```

- 

```
 end

```

- 

```
 sockets << s

```

- 

```
 s.setsockopt(:SOCKET, :REUSEADDR, 1)

```

- 

```
 s.setsockopt(:IPV6, :V6ONLY, 1) if ai.ipv6?

```

- 

```
 begin

```

- 

```
   s.bind(ai)

```

- 

```
 rescue SystemCallError

```

- 

```
   last_error = $!

```

- 

```
   next

```

- 

```
 end

```

- 

```
 begin

```

- 

```
   s.listen(5)

```

- 

```
 rescue SystemCallError

```

- 

```
   last_error = $!

```

- 

```
   next

```

- 

```
 end

```

- }
- if sockets.empty?
- 

```
 raise last_error

```

- end
- loop {
- 

```
 readable, _, _ = IO.select(sockets)

```

- 

```
 readable.each {|r|

```

- 

```
   begin

```

- 

```
     sock, addr = r.accept_nonblock

```

- 

```
   rescue Errno::EWOULDBLOCK

```

- 

```
     next

```

- 

```
   end

```

- 

```
   yield sock, addr

```

- 

```
 }

```

- }
- ensure
- sockets.each {|s|
- 

```
 s.close if !s.closed?

```

- }
- end
- 
- 
# creates a new socket connected to path using UNIX socket socket.
- 
# 
- 
# If a block is given, the block is called with the socket.
- 
# The value of the block is returned.
- 
# The socket is closed when this method returns.
- 
# 
- 
# If no block is given, the socket is returned.
- 
# 
- 
# Example:
- 
# 
- 
# # talk to /tmp/sock socket.
- 
# Socket.unix(“/tmp/sock”) {|sock|
- 
# t = Thread.new { IO.copy\_stream(sock, STDOUT) }
- 
# IO.copy\_stream(STDIN, sock)
- 
# t.join
- 
# }
- 
# 
- def self.unix(path) # :yield: socket
- addr = AddrInfo.unix(path)
- sock = self.new(:UNIX, :STREAM, 0)
- begin
- 

```
 sock.connect(addr)

```

- ensure
- 

```
 sock.close if $!

```

- end
- if block\_given?
- 

```
 begin

```

- 

```
   yield sock

```

- 

```
 ensure

```

- 

```
   sock.close if !sock.closed?

```

- 

```
 end

```

- else
- 

```
 sock

```

- end
- end
- 
- 
# creates a UNIX socket server on _path_.
- 
# It calls the block for each socket accepted.
- 
# 
- 
# If _host_ is specified, it is used with _port_ to determine the

server ports.

- 
# 
- 
# The socket is _not_ closed when the block returns.
- 
# So application should close it.
- 
# 
- 
# This method deletes the socket file pointed by _path_ at first if
- 
# the file is a socket file and it is owned by the user of the

application.

- 
# 
- 
# Example:
- 
# 
- 
# # Sequential echo server.
- 
# # It services only one client at a time.
- 
# Socket.unix\_server\_loop(“/tmp/sock”) {|sock, client\_addrinfo|
- 
# begin
- 
# IO.copy\_stream(sock, sock)
- 
# ensure
- 
# sock.close
- 
# end
- 
# }
- 
# 
- def self.unix\_server\_loop(path) # :yield: socket, client\_addrinfo
- begin
- 

```
 st = File.lstat(path)

```

- rescue Errno::ENOENT
- end
- if st && st.socket? && st.owned?
- 

```
 File.unlink path

```

- end
- serv = self.new(:UNIX, :STREAM, 0)
- serv.bind(AddrInfo.unix(path))
- serv.listen(5)
- loop {
- 

```
 sock, addr = serv.accept

```

- 

```
 yield sock, addr

```

- }
- ensure
- serv.close if serv && !serv.closed?
- end
- 

+end  
+

---

<div class="post-metadata">

**Author:** ![Tanaka\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c67d28/32.png) [@Tanaka\_A](https://www.ruby-forum.com/u/Tanaka_A)\
**Post date:** [January 17, 2009, 2:06pm UTC](https://www.ruby-forum.com/t/socket-rb/157385/2 "2009-01-17T14:06:56Z")

</div>

In article [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid),  
Tanaka A. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) writes:

> Socket e$B$r;H$C$F$$$k$H!"@8@.$9$k$H$3$m$,e(B TCPSocket e$BEy$KHf$Y$Fe(B  
> e$BITJX$J$N$G!"0J2\<$N$h$&$J%a%=%C%I$r$D$1$F$b$$$$$G$7$g$&$+!#e(B

e$BFC$KH?1~$O$“$j$^$;$s$,!”;d$,M\_$7$$$N$GF~$l$F$\_$^$7$?!#e(B
