I have been working on a small script that allows communication though the TCPsocket command. I am stuck on a small error in my code. For some reason after running it twice the it stops running the RX loop.
I also worry that while its waiting for me to enter something for the get statement, that it won't be looking for incoming messages...
Any help is greatly appreciated. Thanks in advance guys
require 'socket'
ip = 'localhost'
port = 18000
TX = Thread.new do
loop {
Serv = TCPSocket.open(ip, port)
message = gets.chomp()
Serv.write(message)
Serv.close
}
end
RX = Thread.new do
loop {
server = TCPServer.open(port)
client = server.accept
puts client.gets
}
end
RX
TX.join
You should initialize the server outside the loop. (And to avoid warnings, you should not reassign a constant name like Serv in a loop):
require 'socket'
ip = 'localhost'
port = 18000
TX = Thread.new do
loop {
conn = TCPSocket.open(ip, port)
message = gets.chomp()
conn.write(message)
conn.close
}
end
RX = Thread.new do
server = TCPServer.open(port)
loop {
client = server.accept
puts client.gets
}
end
TX.join
If you want to serve multiple clients simultaneously, take a hint from the second example at http://ruby-doc.org/stdlib-1.9.3/libdoc/socket/rdoc/TCPServer.html and use Thread.start(server.accept) { |client| ... }.
Related
require 'socket'
socket = TCPSocket.open('stream-api.betfair.com', '443')
while line = socket.gets
puts line.chop
end
socket.close
I should receive something like {"op":"connection","connectionId":"002-230915140112-174"}
but I receive Connection reset by peer which
means the remote end would have sent a reset packet (RST) to kill the connection without an orderly shutdown (close). In that case you know it was the peer(client).
betfair included a nodejs example and also csharp/java examples
Any help is much appreciated. Thanks!
First thing, you should replace the string '443' by an integer :
TCPSocket.open('stream-api.betfair.com', 443)
Anyway, it seems to be related with the SSL negociation : the following Stackoverflow post gives a quick idea about what would work : How to establish a SSL enabled TCP/IP Connection in Ruby. Using this method, I works.
require 'socket'
require 'openssl'
host = 'stream-api.betfair.com'
port = 443
socket = TCPSocket.open(host,port)
ssl_context = OpenSSL::SSL::SSLContext.new()
ssl_context.ssl_version = :SSLv23
ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, ssl_context)
ssl_socket.sync_close = true
ssl_socket.connect
while line = ssl_socket.gets
p line
end
ssl_socket.close
Result :
"{\"op\":\"connection\",\"connectionId\":\"001-151118094105-259478\"}\r\n"
Dealing with SSL/TLS protected connection is sometime quite verbose with Ruby. In the example you gave, in NodeJS, the hint is the first line :
var tls = require('tls');
I am having an issue with a ruby server I am writing.
The server functions fine until you have more than one client attached, then it sends out the messages in a round-robin like way, when I want all clients to get the message at the same time.
The server is supposed to grab any clients that connects, but then wait till I issue a command. The problem is only one client is getting the command, when I enter a command again another client gets it and so on
SERVER
require 'socket'
mutex = Mutex.new
cv = ConditionVariable.new
server = TCPServer.open(2000)
#Comm="test"
Thread.new{
loop {
Thread.start(server.accept) do |client|
client.puts("Client accepted")
mutex.synchronize {
cv.wait(mutex)
client.puts("##Comm")
client.close
}
end
}
}
loop {
system "clear" or system "cls"
print("Enter Command\n")
#Comm = gets()
mutex.synchronize {
cv.signal
}
}
CLIENT
require 'socket' # Sockets are in standard library
hostname = 'localhost'
port = 2000
loop {
begin
s = TCPSocket.open(hostname, port)
system "clear" or system "cls"
while line = s.gets # Read lines from the socket
puts line.chop # And print with platform line terminator
end
s.close
rescue
next
end
sleep(0.5)
}
Using .signal on ConditionVariable only wakes up one thread, but .broadcast will go and wake up all that are waiting to be signaled.
I have been trying to get port forwarding to work correctly with Net::SSH. From what I understand I need to fork out the Net::SSH session if I want to be able to use it from the same Ruby program so that the event handling loop can actually process packets being sent through the connection. However, this results in the ugliness you can see in the following:
#!/usr/bin/env ruby -w
require 'net/ssh'
require 'httparty'
require 'socket'
include Process
log = Logger.new(STDOUT)
log.level = Logger::DEBUG
local_port = 2006
child_socket, parent_socket = Socket.pair(:UNIX, :DGRAM, 0)
maxlen = 1000
hostname = "www.example.com"
pid = fork do
parent_socket.close
Net::SSH.start("hostname", "username") do |session|
session.logger = log
session.logger.sev_threshold=Logger::Severity::DEBUG
session.forward.local(local_port, hostname, 80)
child_socket.send("ready", 0)
pidi = fork do
msg = child_socket.recv(maxlen)
puts "Message from parent was: #{msg}"
exit
end
session.loop do
status = waitpid(pidi, Process::WNOHANG)
puts "Status: #{status.inspect}"
status.nil?
end
end
end
child_socket.close
puts "Message from child: #{parent_socket.recv(maxlen)}"
resp = HTTParty.post("http://localhost:#{local_port}/", :headers => { "Host" => hostname } )
# the write cannot be the last statement, otherwise the child pid could end up
# not receiving it
parent_socket.write("done")
puts resp.inspect
Can anybody show me a more elegant/better working solution to this?
I spend a lot of time trying to figure out how to correctly implement port forwarding, then I took inspiration from net/ssh/gateway library. I needed a robust solution that works after various possible connection errors. This is what I'm using now, hope it helps:
require 'net/ssh'
ssh_options = ['host', 'login', :password => 'password']
tunnel_port = 2222
begin
run_tunnel_thread = true
tunnel_mutex = Mutex.new
ssh = Net::SSH.start *ssh_options
tunnel_thread = Thread.new do
begin
while run_tunnel_thread do
tunnel_mutex.synchronize { ssh.process 0.01 }
Thread.pass
end
rescue => exc
puts "tunnel thread error: #{exc.message}"
end
end
tunnel_mutex.synchronize do
ssh.forward.local tunnel_port, 'tunnel_host', 22
end
begin
ssh_tunnel = Net::SSH.start 'localhost', 'tunnel_login', :password => 'tunnel_password', :port => tunnel_port
puts ssh_tunnel.exec! 'date'
rescue => exc
puts "tunnel connection error: #{exc.message}"
ensure
ssh_tunnel.close if ssh_tunnel
end
tunnel_mutex.synchronize do
ssh.forward.cancel_local tunnel_port
end
rescue => exc
puts "tunnel error: #{exc.message}"
ensure
run_tunnel_thread = false
tunnel_thread.join if tunnel_thread
ssh.close if ssh
end
That's just how SSH in general is. If you're offended by how ugly it looks, you should probably wrap up that functionality into a port forwarding class of some sort so that the exposed part is a lot more succinct. An interface like this, perhaps:
forwarder = PortForwarder.new(8080, 'remote.host', 80)
So I have found a slightly better implementation. It only requires a single fork but still uses a socket for the communication. It uses IO#read_nonblock for checking if a message is ready. If there isn't one, the method throws an exception, in which case the block continues to return true and the SSH session keeps serving requests. Once the parent is done with the connection it sends a message, which causes child_socket.read_nonblock(maxlen).nil? to return false, making the loop exit and therefore shutting down the SSH connection.
I feel a little better about this, so between that and #tadman's suggestion to wrap it in a port forwarding class I think it's about as good as it'll get. However, any further suggestions for improving this are most welcome.
#!/usr/bin/env ruby -w
require 'net/ssh'
require 'httparty'
require 'socket'
log = Logger.new(STDOUT)
log.level = Logger::DEBUG
local_port = 2006
child_socket, parent_socket = Socket.pair(:UNIX, :DGRAM, 0)
maxlen = 1000
hostname = "www.example.com"
pid = fork do
parent_socket.close
Net::SSH.start("ssh-tunnel-hostname", "username") do |session|
session.logger = log
session.logger.sev_threshold=Logger::Severity::DEBUG
session.forward.local(local_port, hostname, 80)
child_socket.send("ready", 0)
session.loop { child_socket.read_nonblock(maxlen).nil? rescue true }
end
end
child_socket.close
puts "Message from child: #{parent_socket.recv(maxlen)}"
resp = HTTParty.post("http://localhost:#{local_port}/", :headers => { "Host" => hostname } )
# the write cannot be the last statement, otherwise the child pid could end up
# not receiving it
parent_socket.write("done")
puts resp.inspect
How can I detect that a socket is half-open? The case I'm dealing with is when the other side of a socket has sent a FIN and the Ruby app has ACKed that FIN. Is there a way for me to tell that the socket is in this condition?
Take, for example:
require 'socket'
s = TCPServer.new('0.0.0.0', 5010)
loop do
c = s.accept
until c.closed?
p c.recv(1024)
end
end
In this case, when I telnet into port 5010, I'll see all my input until I close the telnet session. At that point, it will print empty strings over and over as fast as it can.
You are using the blocking call recv, which will return nil when the other end closes. The socket won't be closed until you close it. Change
until c.closed?
p c.recv(1024)
end
to
while (s = c.recv(1024)) && s > 0
p s
end
c.close
You could combine IO#read and IO#eof? to check this.
require 'socket'
server = TCPServer.new('0.0.0.0', 5010)
loop do
client = server.accept
client.read(1024) until client.eof?
puts 'client closed connection'
client.close
end
I'm reading lines of input on a TCP socket, similar to this:
class Bla
def getcmd
#sock.gets unless #sock.closed?
end
def start
srv = TCPServer.new(5000)
#sock = srv.accept
while ! #sock.closed?
ans = getcmd
end
end
end
If the endpoint terminates the connection while getline() is running then gets() hangs.
How can I work around this? Is it necessary to do non-blocking or timed I/O?
You can use select to see whether you can safely gets from the socket, see following implementation of a TCPServer using this technique.
require 'socket'
host, port = 'localhost', 7000
TCPServer.open(host, port) do |server|
while client = server.accept
readfds = true
got = nil
begin
readfds, writefds, exceptfds = select([client], nil, nil, 0.1)
p :r => readfds, :w => writefds, :e => exceptfds
if readfds
got = client.gets
p got
end
end while got
end
end
And here a client that tries to break the server:
require 'socket'
host, port = 'localhost', 7000
TCPSocket.open(host, port) do |socket|
socket.puts "Hey there"
socket.write 'he'
socket.flush
socket.close
end
The IO#closed? returns true when both reader and writer are closed.
In your case, the #sock.gets returns nil, and then you call the getcmd again, and this runs in a never ending loop. You can either use select, or close the socket when gets returns nil.
I recommend using readpartial to read from your socket and also catching peer resets:
while true
sockets_ready = select(#sockets, nil, nil, nil)
if sockets_ready != nil
sockets_ready[0].each do |socket|
begin
if (socket == #server_socket)
# puts "Connection accepted!"
#sockets << #server_socket.accept
else
# Received something on a client socket
if socket.eof?
# puts "Disconnect!"
socket.close
#sockets.delete(socket)
else
data = ""
recv_length = 256
while (tmp = socket.readpartial(recv_length))
data += tmp
break if (!socket.ready?)
end
listen socket, data
end
end
rescue Exception => exception
case exception
when Errno::ECONNRESET,Errno::ECONNABORTED,Errno::ETIMEDOUT
# puts "Socket: #{exception.class}"
#sockets.delete(socket)
else
raise exception
end
end
end
end
end
This code borrows heavily from some nice IBM code by M. Tim Jones. Note that #server_socket is initialized by:
#server_socket = TCPServer.open(port)
#sockets is just an array of sockets.
I simply pgrep "ruby" to find the pid, and kill -9 the pid and restart.
If you believe the rdoc for ruby sockets, they don't implement gets. This leads me to believe gets is being provided by a higher level of abstraction (maybe the IO libraries?) and probably isn't aware of socket-specific things like 'connection closed.'
Try using recvfrom instead of gets