To know the socket hierarchy in ruby here a simple tree explains it.
IO # The basis for all input and output in Ruby└── BasicSocket # Abstract base class for all socket classes├── IPSocket # Super class for protocols using the Internet Protocol (AF_INET)│ ├── TCPSocket # Class for Transmission Control Protocol (TCP) sockets│ │ ├── SOCKSSocket # Helper class for building TCP socket servers applications│ │ └── TCPServer # Helper class for building TCP socket servers│ └── UDPSocket # Class for User Datagram Protocol (UDP) sockets├── Socket # Base socket class that mimics that BSD Sockets API. It provides more operating system specific functionality└── UNIXSocket # Class providing IPC using the UNIX domain protocol (AF_UNIX)└── 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.
SOCK_RAW
SOCK_PACKET
SOCK_STREAM
SOCK_DRAM
SOCK_RDM
SOCK_SEQPACKET
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
IPPROTO_SCTP
IPPROTO_TCP
IPPROTO_UDP
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
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
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 0
m it should be a protocol defined (we'll manipulate that later)
Server\/Client life cycle
Client Server| |socket + + socket| |connect +--------, + bind| | |write ,--> +------, | + listen| | | | |read `----+ <--, | `-> + accept| | | |close +--, | `----> + <--, read <--,| | | | || `--------+----' write ٨| |`----->------>------->----`
require 'socket'Socket.ip_address_list
Socket.gethostname
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
require 'socket'server = TCPServer.new('0.0.0.0', 9911) # Server, binds/listens all interfaces on port 9911client = server.accept # Wait for client to connectrhost = client.peeraddr.last # peeraddr, returns remote [address_family, port, hostname, numeric_address(ip)]client.puts "Hi TCP Client! #{rhost}" # Send a message to the client once it connectclient.gets.chomp # Read incoming message from clientclient.close # Close the client's connectionserver.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.
require 'socket'server = TCPServer.new('0.0.0.0', 0)server.addr[1] # Shows the picked port
require 'socket'client = TCPSocket.new('127.0.0.1', 9911) # Client, connects to server on port 9911rhost = client.peeraddr.last # Get the remote server's IP addressclient.gets.chompclient.puts "Hi, TCP Server #{rhost}"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.
timeval = [3, 0].pack("l_2") # Time interval 3 secondsclient.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, timeval # Set socket receiving time intervalclient.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, timeval # Set socket sending time intervalclient.getsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO).inspect # Optional, Check if socket option has been setclient.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
>> s = TCPSocket.new('0.0.0.0', 9911)=> #<TCPSocket:fd 11>>> s.method :puts=> #<Method: TCPSocket(IO)#puts>>> s.method :write=> #<Method: TCPSocket(IO)#write>>> s.method :send=> #<Method: TCPSocket(BasicSocket)#send>
>> s = TCPSocket.new('0.0.0.0', 9911)=> #<TCPSocket:fd 11>>> s.method :gets=> #<Method: TCPSocket(IO)#gets>>> s.method :read=> #<Method: TCPSocket(IO)#read>>> s.method :recv=> #<Method: TCPSocket(BasicSocket)#recv>
require 'socket'server = UDPSocket.new # Start UDP socketserver.bind('0.0.0.0', 9911) # Bind all interfaces to port 9911mesg, addr = server.recvfrom(1024) # Receive 1024 bytes of the message and the sender IPserver puts "Hi, UDP Client #{addr}", addr[3], addr[1] # Send a message to the clientserver.recv(1024) # Receive 1024 bytes of the message
require 'socket'client = UDPSocket.newclient.connect('localhost', 9911) # Connect to server on port 991client.puts "Hi, UDP Server!", 0 # Send messageserver.recv(1024) # Receive 1024 bytes of the server message
There alternative for sending and receiving too, figure it out, RubyDoc.
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
require 'gserver'class HelloServer < GServer # Inherit GServer classdef serve(io)io.puts("What's your name?")line = io.gets.chompio.puts "Hi, #{line}!"self.stop if io.gets =~ /shutdown/ # Stop the server if you get shutdown stringendendserver = HelloServer.new(1234, '0.0.0.0') # Start the server on port 1234server.audit = true # Enable loggingserver.start # Start the serviceserver.join