Skip to content

Instantly share code, notes, and snippets.

@dcki
Created May 10, 2017 06:58
Show Gist options
  • Select an option

  • Save dcki/831df1fec4595dbbbf94586677c91cf0 to your computer and use it in GitHub Desktop.

Select an option

Save dcki/831df1fec4595dbbbf94586677c91cf0 to your computer and use it in GitHub Desktop.
p2p chat (with NAT hole punching, I think)
require 'socket'
require 'io/wait' # Provides socket#ready?
require 'timeout'
if ARGV.length != 1
abort 'Usage: ruby p2p_chat.rb their_ip_address'
end
my_port = 2000
their_host = ARGV[0]
their_port = 2000
socket = nil
# Try to connect twice then listen, and repeat.
# I *think* this achieves NAT hole punching, but I don't think I've confirmed
# that yet and there are likely better hole punching algorithms.
server = nil
until socket
server.close if server
2.times do
unless socket
puts 'punch'
socket = Timeout::timeout(1, Timeout::Error) do
begin
port_available = false
until port_available
begin
begin
s = TCPSocket.new their_host, their_port, '0.0.0.0', 2000
rescue Errno::ECONNREFUSED
end
port_available = true
rescue Errno::EADDRINUSE
sleep 1
end
end
s
rescue Timeout::Error
end
end
end
end
unless socket
puts 'listen'
socket = Timeout::timeout(rand * 4 + 2, Timeout::Error) do
begin
port_available = false
until port_available
begin
begin
server = TCPServer.new my_port
s = server.accept
rescue Errno::ECONNREFUSED
end
port_available = true
rescue Errno::EADDRINUSE
sleep 1
end
end
s
rescue Timeout::Error
end
end
end
end
Thread.new do
loop do
if socket.ready?
line = socket.gets
puts ' >> ' + line
if line.chomp == 'quit'
socket.close
puts '== Peer quit =='
exit
end
else
sleep 1
end
end
end
puts "== Ready (type 'quit' to quit) =="
# I thought some kind of keep-alive loop would be needed to send messages to keep the connection through NAT open if neither peer sends a message for a while, but that doesn't seem to be necessary. Maybe TCP sends messages that keep the NAT open, or maybe I'm not actually testing with a NAT.
loop do
input = $stdin.gets
socket.puts input
break if input.chomp == 'quit'
end
puts '== You quit =='
socket.close
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment