Ruby Socket

Lightweight Introduction

Ruby Socket Class Hierarchy

To know the socket hierarchy in ruby here a simple tree explains it.

  1. IO # The basis for all input and output in Ruby
  2. └── BasicSocket # Abstract base class for all socket classes
  3. ├── IPSocket # Super class for protocols using the Internet Protocol (AF_INET)
  4. ├── TCPSocket # Class for Transmission Control Protocol (TCP) sockets
  5. ├── SOCKSSocket # Helper class for building TCP socket servers applications
  6. └── TCPServer # Helper class for building TCP socket servers
  7. └── UDPSocket # Class for User Datagram Protocol (UDP) sockets
  8. ├── Socket # Base socket class that mimics that BSD Sockets API. It provides more operating system specific functionality
  9. └── UNIXSocket # Class providing IPC using the UNIX domain protocol (AF_UNIX)
  10. └── UNIXServer # Helper class for building UNIX domain protocol socket servers

I’ll verbosely mention some of Socket::Constants here since I didn’t find an obvious reference listing it except Programming Ruby1.9 The Pragmatic Programmers’ Guide; Otherwise you’ve to ri Socket::Constants from command line which is a good way to get the description of each constant.

Socket Types

  • SOCK_RAW
  • SOCK_PACKET
  • SOCK_STREAM
  • SOCK_DRAM
  • SOCK_RDM
  • SOCK_SEQPACKET

Address Families(Socket Domains)

  • AF_APPLETALK
  • AF_ATM
  • AF_AX25
  • AF_CCITT
  • AF_CHAOS
  • AF_CNT
  • AF_COIP
  • AF_DATAKIT
  • AF_DEC
  • AF_DLI
  • AF_E164
  • AF_ECMA
  • AF_HYLINK
  • AF_IMPLINK
  • AF_INET(IPv4)
  • AF_INET6(IPv6)
  • AF_IPX
  • AF_ISDN
  • AF_ISO
  • AF_LAT
  • AF_LINK
  • AF_LOCAL(UNIX)
  • AF_MAX
  • AF_NATM
  • AF_NDRV
  • AF_NETBIOS
  • AF_NETGRAPH
  • AF_NS
  • AF_OSI
  • AF_PACKET
  • AF_PPP
  • AF_PUP
  • AF_ROUTE
  • AF_SIP
  • AF_SNA
  • AF_SYSTEM
  • AF_UNIX
  • AF_UNSPEC

Socket Protocol

  • IPPROTO_SCTP
  • IPPROTO_TCP
  • IPPROTO_UDP

Protocol Families

  • PF_APPLETALK
  • PF_ATM
  • PF_AX25
  • PF_CCITT
  • PF_CHAOS
  • PF_CNT
  • PF_COIP
  • PF_DATAKIT
  • PF_DEC
  • PF_DLI
  • PF_ECMA
  • PF_HYLINK
  • PF_IMPLINK
  • PF_INET
  • PF_INET6
  • PF_IPX
  • PF_ISDN
  • PF_ISO
  • PF_KEY
  • PF_LAT
  • PF_LINK
  • PF_LOCAL
  • PF_MAX
  • PF_NATM
  • PF_NDRV
  • PF_NETBIOS
  • PF_NETGRAPH
  • PF_NS
  • PF_OSI
  • PF_PACKET
  • PF_PIP
  • PF_PPP
  • PF_PUP
  • PF_ROUTE
  • PF_RTIP
  • PF_SIP
  • PF_SNA
  • PF_SYSTEM
  • PF_UNIX
  • PF_UNSPEC
  • PF_XTP

Socket options

  • SO_ACCEPTCONN
  • SO_ACCEPTFILTER
  • SO_ALLZONES
  • SO_ATTACH_FILTER
  • SO_BINDTODEVICE
  • SO_BINTIME
  • SO_BROADCAST
  • SO_DEBUG
  • SO_DETACH_FILTER
  • SO_DONTROUTE
  • SO_DONTTRUNC
  • SO_ERROR
  • SO_KEEPALIVE
  • SO_LINGER
  • SO_MAC_EXEMPT
  • SO_NKE
  • SO_NOSIGPIPE
  • SO_NO_CHECK
  • SO_NREAD
  • SO_OOBINLINE
  • SO_PASSCRED
  • SO_PEERCRED
  • SO_PEERNAME
  • SO_PRIORITY
  • SO_RCVBUF
  • SO_RCVLOWAT
  • SO_RCVTIMEO
  • SO_RECVUCRED
  • SO_REUSEADDR
  • SO_REUSEPORT
  • SO_SECURITY_AUTHENTICATION
  • SO_SECURITY_ENCRYPTION_NETWORK
  • SO_SECURITY_ENCRYPTION_TRANSPORT
  • SO_SNDBUF
  • SO_SNDLOWAT
  • SO_SNDTIMEO
  • SO_TIMESTAMP
  • SO_TIMESTAMPNS
  • SO_TYPE
  • SO_USELOOPBACK
  • SO_WANTMORE
  • SO_WANTOOBFLAG

Creating Socket Template

  1. Socket.new(domain, socktype [, protocol])

domain(Address\/Protocol Families): like AF_INET, PF_PACKET, etc

socktype: like SOCK_RAW, SOCK_STREAM

protocol: by default, it’s 0m it should be a protocol defined (we’ll manipulate that later)

TCP Socket

Server\/Client life cycle

  1. Client Server
  2. | |
  3. socket + + socket
  4. | |
  5. connect +--------, + bind
  6. | | |
  7. write ,--> +------, | + listen
  8. | | | | |
  9. read `----+ <--, | `-> + accept
  10. | | | |
  11. close +--, | `----> + <--, read <--,
  12. | | | | |
  13. | `--------+----' write ٨
  14. | |
  15. `----->------>------->----`

General Socket usage

Get List of local IPaddreses

  1. require 'socket'
  2. Socket.ip_address_list

Get Hostname

  1. Socket.gethostname

TCP Server

Here we’ll represent an absolute TCP server. This server will access connect from one client and send a message to it once connected then close the client and server connection

  1. require 'socket'
  2. server = TCPServer.new('0.0.0.0', 9911) # Server, binds/listens all interfaces on port 9911
  3. client = server.accept # Wait for client to connect
  4. rhost = client.peeraddr.last # peeraddr, returns remote [address_family, port, hostname, numeric_address(ip)]
  5. client.puts "Hi TCP Client! #{rhost}" # Send a message to the client once it connect
  6. client.gets.chomp # Read incoming message from client
  7. client.close # Close the client's connection
  8. server.close # Close the TCP Server

Note: if you want to list on unused and random port, set to port 0, ruby will find vacancy port then use it.
ex.

  1. require 'socket'
  2. server = TCPServer.new('0.0.0.0', 0)
  3. server.addr[1] # Shows the picked port

TCP Client

  1. require 'socket'
  2. client = TCPSocket.new('127.0.0.1', 9911) # Client, connects to server on port 9911
  3. rhost = client.peeraddr.last # Get the remote server's IP address
  4. client.gets.chomp
  5. client.puts "Hi, TCP Server #{rhost}"
  6. client.close

You can put timeout/time interval for current connection in-case the server’s response get delayed and the socket is still open.

  1. timeval = [3, 0].pack("l_2") # Time interval 3 seconds
  2. client.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, timeval # Set socket receiving time interval
  3. client.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, timeval # Set socket sending time interval
  4. client.getsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO).inspect # Optional, Check if socket option has been set
  5. client.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDTIMEO).inspect # Optional, Check if socket option has been set

There are some alternatives for puts and gets methods.You can see the difference and its classes using method method in Pry interpreter console

  1. >> s = TCPSocket.new('0.0.0.0', 9911)
  2. => #<TCPSocket:fd 11>
  3. >> s.method :puts
  4. => #<Method: TCPSocket(IO)#puts>
  5. >> s.method :write
  6. => #<Method: TCPSocket(IO)#write>
  7. >> s.method :send
  8. => #<Method: TCPSocket(BasicSocket)#send>
  1. >> s = TCPSocket.new('0.0.0.0', 9911)
  2. => #<TCPSocket:fd 11>
  3. >> s.method :gets
  4. => #<Method: TCPSocket(IO)#gets>
  5. >> s.method :read
  6. => #<Method: TCPSocket(IO)#read>
  7. >> s.method :recv
  8. => #<Method: TCPSocket(BasicSocket)#recv>

UDP Socket

UDP Server

  1. require 'socket'
  2. server = UDPSocket.new # Start UDP socket
  3. server.bind('0.0.0.0', 9911) # Bind all interfaces to port 9911
  4. mesg, addr = server.recvfrom(1024) # Receive 1024 bytes of the message and the sender IP
  5. server puts "Hi, UDP Client #{addr}", addr[3], addr[1] # Send a message to the client
  6. server.recv(1024) # Receive 1024 bytes of the message

UDP Client

  1. require 'socket'
  2. client = UDPSocket.new
  3. client.connect('localhost', 9911) # Connect to server on port 991
  4. client.puts "Hi, UDP Server!", 0 # Send message
  5. server.recv(1024) # Receive 1024 bytes of the server message

There alternative for sending and receiving too, figure it out, RubyDoc.

GServer

GServer standard library implements a generic server, featuring thread pool management, simple logging, and multi-server management. Any kind of application-level server can be implemented using this class:

  • It accepts multiple simultaneous connections from clients
  • Several services (i.e. one service per TCP port)

    • can be run simultaneously,
    • can be stopped at any time through the class method GServer.stop(port)
  • All the threading issues are handled

  • All events are optionally logged

  • Very basic GServer

  1. require 'gserver'
  2. class HelloServer < GServer # Inherit GServer class
  3. def serve(io)
  4. io.puts("What's your name?")
  5. line = io.gets.chomp
  6. io.puts "Hi, #{line}!"
  7. self.stop if io.gets =~ /shutdown/ # Stop the server if you get shutdown string
  8. end
  9. end
  10. server = HelloServer.new(1234, '0.0.0.0') # Start the server on port 1234
  11. server.audit = true # Enable logging
  12. server.start # Start the service
  13. server.join

#