Simple socket program in Ruby - ruby

I want to write a simple server socket in Ruby, which, when a client connects to it, prints a message and closes the client connection. I came up with:
require 'socket'
server = TCPServer.open('localhost',8800)
loop {
client = server.accept
Thread.start do
s = client
s.puts "Closing the connection. Bye!"
s.close
end
}
However, when I access "localhost:8800" in my browser, I am not getting that message, instead, it says page not found.. What am I doing wrong here?

It is quite likely that your browser is expecting something on the remote end that talks Http.
This is dependant upon your browser and also the exact URI you typed in. It is also possible that your browser is connecting getting the connection close and then displaying an error page.
If you want to see the server working then use telnet from a command prompt. So in one window type ruby ./myfilename.rb and then in another type telnet localhost 8800

Related

Ruby TCPSocket communication

I am learning TCPSocket and have a simple server written:
require 'socket'
server = TCPServer.open(2000)
loop {
client = server.accept
p client.gets
client.print("bar")
client.close
}
and simple client written:
require 'socket'
hostname = 'localhost'
port = 2000
socket = TCPSocket.open(hostname, port)
socket.print("foo")
p socket.gets
When I run these in separate terminals with either the server or client communicating one way (i.e. one "prints" and the other "gets") I get the expected string on the other side. When I run these as written, with the client first "print"-ing a message to the server and then the server "gets"ing it to then "print" a string to the client, it just hangs. What is causing this issue?
Your program does following:
The connection is established between client and server.
Client side
Calls print("foo") - exactly 3 bytes are transmitted to the server.
Calls gets - waits for data from server but server never send any.
Server side
Calls gets - The ruby function gets parse stream data and it always return the whole line. But the server received only "foo" and the it has no idea whether it is whole line or not. So it is waiting forever for new line character which client never send.

How to get other computer to connect to my localhost server?

I recently started learning Ruby Sockets and decided to research the topic. I came across the ruby-doc which had some example code that ran smoothly:
This is the example code for the server:
require 'socket'
server = TCPServer.new 2000 # Server bound to port 2000
loop do
client = server.accept # Wait for a client to connect
client.puts "Hello !"
client.puts "Time is #{Time.now}"
client.close
end
And the example code for the client:
require 'socket'
s = TCPSocket.new 'localhost', 2000
while line = s.gets # Read lines from socket
puts line # and print them
end
s.close # close socket when done
So this ran well but I was wondering how I would get the client to connect if it is running from a different computer. So I attempted to replace the "'localhost'" in the client code with my public IP address courtesy of whatismyip.com, however, when I tried running the new client code on a different computer I merely got a timeout error. I even attempted running the new client code on the same machine running the server but still I got a timeout error.
Does anyone know how I can get this to work properly?
Any help would be much appreciated!
Greg Hewgill helped me figure this out:
My first problem was that I was using the wrong address. Greg suggested I check my actual address through the cmd command "ipconfig". The command gave me the actual address that the server was being hosted on. Through this I changed the "'localhost'" in the client code and changed it to the actual IP address. Upon running, I received an error that stated that the server had actively refused the connection. This was fixed by also changing the 'localhost' in the server code to the IP address of the server's machine.
Thank you Greg for the help!

Keeping TCPSocket open: server or client's responsibility?

I have been reading examples online about Ruby's TCPSocket and TCPServer, but I still don't know and can't find what's the best practice for this. If you have a running TCPServer, and you want to keep the socket open across multiple connections/clients, who should be responsible in keeping them open, the server or the clients?
Let's say that you have a TCPServer running:
server = TCPServer.new(8000)
loop do
client = server.accept
while line = client.gets
# process data from client
end
client.puts "Response from server"
client.close # should server close the socket?
end
And Client:
socket = TCPSocket.new 'localhost', 8000
while line = socket.gets
# process data from server
end
socket.close # should client close the socket here?
All of the examples I have seen have the socket.close at the end, which I would assume is not what I want as that would close the connection. Server and clients should maintain open connection as they will need to send data back and forth.
PS: I'm pretty a noob on networking, so just kindly let me know if my question sounds completely dumb.
The server is usually responsible for keeping the connections open because the client (being the one connecting to the server) can break the connection at anytime.
Servers are usually in charge of everything that the client doesn't care about. A video game doesn't really care about the connection to the server as long as it's there. It just wants its data so it can keep running.

HTTP streaming connection (SSE) client disconnect not detected with Sinatra/Thin on Heroku

I am attempting to deploy a Sinatra streaming SSE response application on the Cedar stack. Unfortunately while it works perfectly in development, once deployed to Heroku the callback or errback never get called when a connection is called, leading to the connection pool getting filled up with stale connections (that never time out because data is still being sent to them on the server side.)
Relvant info from the Heroku documentation:
Long-polling and streaming responses
Cedar supports HTTP 1.1 features such as long-polling and streaming responses. An application has an initial 30 second window to respond with a single byte back to the client. However, each byte transmitted thereafter (either received from the client or sent by your application) resets a rolling 55 second window. If no data is sent during the 55 second window, the connection will be terminated.
If you’re sending a streaming response, such as with server-sent events, you’ll need to detect when the client has hung up, and make sure your app server closes the connection promptly. If the server keeps the connection open for 55 seconds without sending any data, you’ll see a request timeout.
This is exactly what I would like to do -- detect when the client has hung up, and close the connection promptly. However, something about the Heroku routing layer seems to prevent Sinatra from detecting the stream close event as it would normally.
Some sample code that can be used to replicate this:
require 'sinatra/base'
class MyApp < Sinatra::Base
set :path, '/tmp'
set :environment, 'production'
def initialize
#connections = []
EM::next_tick do
EM::add_periodic_timer(1) do
#connections.each do |out|
out << "connections: " << #connections.count << "\n"
end
puts "*** connections: #{#connections.count}"
end
end
end
get '/' do
stream(:keep_open) do |out|
#connections << out
puts "Stream opened from #{request.ip} (now #{#connections.size} open)"
out.callback do
#connections.delete(out)
puts "Stream closed from #{request.ip} (now #{#connections.size} open)"
end
end
end
end
I've put a sample app up at http://obscure-depths-3413.herokuapp.com/ using this code that illustrates the problem. When you connect, the amount of connections will increment, but when you disconnect they never go down. (Full source of demo with Gemfile etc is at https://gist.github.com/mroth/5853993)
I'm at wits end trying to debug this one. Anyone know how to fix it?
P.S. There appears to have been a similar bug in Sinatra but it was fixed a year ago. Also this issue only occurs on production in Heroku, but works fine when run locally.
P.S.2. This occurs when iterating over the connections objects as well, for example adding the following code:
EM::add_periodic_timer(10) do
num_conns = #connections.count
#connections.reject!(&:closed?)
new_conns = #connections.count
diff = num_conns - new_conns
puts "Purged #{diff} connections!" if diff > 0
end
Works great locally, but the connections never appear as closed on Heroku.
An update: after working directly with the Heroku routing team (who are great guys!), this is now fixed in their new routing layer, and should work properly in any platform.
I would do this check by hand sending, in a periodic time, alive signal where the client should respond if the message was received.
Please, look at this simple chat implementation https://gist.github.com/tlewin/5708745 that illustrate this concept.
The application communicates with the client using a simple JSON protocol. When the client receive the alive: true message, the application post back a response and the server store the last communication time.

How to build a web-based chat system using ruby Gserver

I am trying to build a web based chat system and I am going to user ruby gserver. I have looked at this example . However my question is when I get the user input from the web and in the controller I have the user input. Now how does client connect to server to pass this user input value to the server.
The server after getting the value will populate a database. So the client will do all read operations from database. However I was wondering how will client connect to server. It is a simple question but I could not figure it out.
Now, I'm making some massive assumptions, because your question is as vague as hell.
Assumption 1: You are running the chatserver pretty much unmodified
Assumption 2: You are running the web service and the chat server on the same host
In that case, you can connect to the chat server using the socket libs, and send it data that way.
require 'socket'
include Socket::Constants
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in( 1234, 'localhost' )
socket.connect( sockaddr )
socket.write( "foo\nquit\n" )
puts socket.read
socket.close
This will send "foo" to the chat server, and then close the connection

Resources