Last active
August 25, 2023 19:26
-
-
Save Aupajo/055eef576bc9cbe4918ce9719d08afb2 to your computer and use it in GitHub Desktop.
Sockets in Ruby
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'socket' | |
# 1. Create | |
# AF_INET means IPv4 (xxx.xxx.xxx.xxx) | |
# SOCK_STREAM means communicating with a stream (TCP) | |
# | |
# Can be simplified to symbols :INET and :STREAM, respectively | |
server = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM) | |
# 2. Bind | |
# 4481 is the port number – pick one between 1,025-48,889 | |
# Want to choose “the port” for your service? Check the list of IANA reserved | |
# ports first: https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.txt | |
# | |
# '0.0.0.0' is the IP address to bind to. '127.0.0.1' means your socket can be | |
# connected to on your local machine, but not from the outside. '192.168.0.5' | |
# will only work for clients that can access that IP over the network, but not | |
# your local machine. '0.0.0.0' means anyone, anywhere can connect. | |
address = Socket.pack_sockaddr_in(4481, '0.0.0.0') | |
server.bind(address) | |
# 3. Listen | |
# 5 is the maximum number of pending connections your socket is willing to | |
# tolerate. Socket::SOMAXCONN is a constant that represents the maximum allowed | |
# listen size. Hitting this limit can result in an Errno::ECONNREFUSED. | |
server.listen(5) | |
# 4. Accept | |
# Accept blocks until a message is received, and returns a tuple | |
connection, addr_info = server.accept | |
# In Unix, everything is treated as a file | |
# Sockets are no exception – here's the server's “fileno” (file descriptor | |
# number) that the kernel uses to keep track of which files are open in the | |
# current process | |
print 'Server fileno: ' | |
p server.fileno | |
# A “connection” is actually another instance of the Socket class | |
print 'Connection class: ' | |
p connection.class | |
# This “connection” Socket has a different file descriptor than the server socket | |
print 'Connection fileno: ' | |
p connection.fileno | |
# The local address is the endpoint on the machine | |
print 'Local address: ' | |
p connection.local_address | |
# The remote address | |
print 'Remote address: ' | |
p connection.remote_address # this is the same as addr_info | |
# 5. Close | |
# Listen to Alec Baldwin. Always be closing. | |
# There are also methods called `close_write` and `close_read`, which prevent | |
# the connection from being able to read or write any longer. | |
connection.close |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Same as before, with the comments stripped | |
require 'socket' | |
# 1. Create | |
server = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM) | |
# 2. Bind | |
address = Socket.pack_sockaddr_in(4481, '0.0.0.0') | |
server.bind(address) | |
# 3. Listen | |
server.listen(5) | |
# 4. Accept | |
connection, addr_info = server.accept | |
# 5. Close | |
connection.close |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# The previous code, simplified! | |
require 'socket' | |
# Almost identical to Socket, most notable difference being that `accept` | |
# returns the connection rather than the [connection, addr_info] tuple | |
server = TCPServer.new(4481) | |
# Create two sockets, one each for IPV4 and IPV6 | |
# servers = Socket.tcp_server_sockets(4481) | |
connection = server.accept | |
connection.close |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Processing more than one message | |
require 'socket' | |
server = TCPServer.new(4481) | |
loop do | |
connection = server.accept | |
connection.close | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# `loop` sucks | |
require 'socket' | |
server = TCPServer.new(4481) | |
Socket.accept_loop(server) do |connection| | |
connection.close | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Compacting even further | |
require 'socket' | |
# Simplify everything | |
Socket.tcp_server_loop(4481) do |connection| | |
connection.close | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Reading from the remote socket | |
require 'socket' | |
Socket.tcp_server_loop(4481) do |connection| | |
# Connection is an IO, so `read`, `flush`, etc. are all supported | |
puts connection.read | |
connection.close | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Connecting with a client | |
require 'socket' | |
client = TCPSocket.new('localhost', 4481) | |
client.write('hello!') | |
# 192.168.201.172 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Reading streams (e.g. `tail -f /var/log/syslog | nc localhost 4481`) | |
require 'socket' | |
server = TCPServer.new(4481) | |
one_kb = 1024 | |
Socket.accept_loop(server) do |connection| | |
# Read 1KB at a time and `puts` as we go! | |
while data = connection.read(one_kb) do | |
puts data | |
end | |
connection.close | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Sending EOF on the client | |
require 'socket' | |
client = TCPSocket.new('localhost', 4481) | |
client.write('hello!') | |
# Close the socket to send an EOF | |
client.close |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Partial reads > reads | |
require 'socket' | |
one_hundred_kb = 1024 * 100 | |
Socket.tcp_server_loop(4481) do |connection| | |
begin | |
# Read data in chunks of *up to* 1 hundred kb or less | |
while data = connection.readpartial(one_hundred_kb) do | |
puts data | |
end | |
# How much to read? There's no silver bullet, but Mongrel, Unicorn, Puma, | |
# Passenger, and Net::HTTP use readpartial(1024 * 16). redis-rb uses 1KB. | |
# `readpartial` raises an EOFError when EOF is encountered | |
rescue EOFError | |
end | |
connection.close | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'socket' | |
Socket.tcp_server_loop(4481) do |connection| | |
# Simplest way to write data to a connection. | |
# This returns immediately after passing the data to the kernel, which figures | |
# out how to send most optimally. Behind the scenes, the kernel can collect | |
# all the pending writes, group them and optimize when they're sent for | |
# maximum performance to avoid flooding the network. | |
connection.write('Welcome!') | |
connection.close | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# A basic HTTP server | |
require 'socket' | |
Socket.tcp_server_loop(4481) do |connection| | |
request = connection.gets | |
p request | |
response = "Hello world!" | |
connection.print "HTTP/1.1 200 OK\r\n" + | |
"Content-Type: text/plain\r\n" + | |
"Content-Length: #{response.bytesize}\r\n" + | |
"Connection: close\r\n" + | |
"\r\n" + | |
response | |
connection.close | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment