I am looking for possibility to have ruby-based webserver communicating by pipe, not by TCP/IP. So I will send HTTP request by pipe and I want to read response by pipe. It should be used as bundled/internal webserver (RPC or something) for desktop application. I don't want to handle ports configuration when there will be more instances of my application running on same machine.
Any ideas?
Thank you in advance.
Try a UNIXSocket You use a local path to specify where the socket connection is,
not a port, and you can easily handle multiple simultaneous connections.
# server.rb
require 'socket'
File.delete( filename ) if File.exists? filename
server = UNIXServer.open( filename )
server.listen( queuesize )
puts "waiting on client connection"
while client= server.accept
puts "got client connection #{client.inspect}"
child_pid = fork do
puts "Asking the client what they want"
client.puts "Welcome to your server, what can I get for you?"
until client.eof?
line = client.gets
puts "The client wants #{line.chomp.inspect}"
end
client.close
end
puts "running server (#{child_pid})"
client.close
Process.detach(child_pid)
end
server.close
# client.rb
require 'socket'
puts "requesting server connection"
server = UNIXSocket.new( filename )
puts "got server connection #{server}"
line = server.gets
puts "The server said: #{line.chomp.inspect}"
%w{ a-pony a-puppy a-kitten a-million-dollars }.each do |item|
server.puts item
end
server.close
Pipe is for one-way communication, so there is no way you can set up webserver on that. You might try with unix socket. But really simplest solution is to use loopback (127.0.0.1). It's highly optimized, so the speed won't be a problem.
Not an answer to your question. However, if you do end up having to use a TCP/IP HTTP Server, you should ensure that it's only listening on 127.0.0.1. Listening on the local host address should be quite fast, as it won't hit the network, and will also make it a tad more secure to stop people connecting from the outside.
Thin supports unix sockets.
Related
I have two network connected computers sending and receiving UDP data. Sending computer is Windows using SocketTest v 3.0.0
Receiving one is Macbook using this Ruby code for echo server:
require 'eventmachine'
EM.run do
puts "EM.run"
EM.open_datagram_socket '0.0.0.0', 9100 do |server|
puts "socket open"
def server.receive_data(data)
puts "data received: #{data}"
send_data("sending back: #{data}")
end
end
end
When I launch this program and send data from Windows computer, nothing happens. But, when I run this program for a second next to eventmachine echo:
require 'socket'
s = UDPSocket.new
while 1 do
puts "sending..."
s.send "hi", 0, "localhost", 9100
end
Eventmachine prints several "hi" messages as intended, and from now it also receives data from network-connected computer properly (I see "sending back" response on Windows computer).
Why is that? My understanding is that UDP is connectionless, so it should take everything from given port. How signal from "localhost" triggers socket to listen from network here?
OK, so I gave up investigating this and did a workaround:
require 'eventmachine'
require 'socket'
EM.run do
puts "EM.run"
EM.open_datagram_socket '0.0.0.0', 9100 do |server|
# send first packet from localhost to trigger network receiving (bug on my Macbook)
s = UDPSocket.new
s.send Time.now.to_s, 0, "localhost", 9100
def server.receive_data(data)
puts data
send_data("OK")
# forward data to another UDP port
s = UDPSocket.new
s.send data, 0, "localhost", 9101
end
end
end
Ugly, but works. s probably shouldn't be created two times but whatever. Now I can receive network packets in every program on port 9101.
I've implemented a very simple kind of server in Ruby, using TCPServer. I have a Server class with serve method:
def serve
# Do the actual serving in a child process
#pid = fork do
# Trap signal sent by #stop or by pressing ^C
Signal.trap('INT') { exit }
# Create a new server on port 2835 (1 ounce = 28.35 grams)
server = TCPServer.new('localhost', 2835)
#logger.info 'Listening on http://localhost:2835...'
loop do
socket = server.accept
request_line = socket.gets
#logger.info "* #{request_line}"
socket.print "message"
socket.close
end
end
end
and a stop method:
def stop
#logger.info 'Shutting down'
Process.kill('INT', #pid)
Process.wait
#pid = nil
end
I run my server from the command line, using:
if __FILE__ == $0
server = Server.new
server.logger = Logger.new(STDOUT)
server.logger.formatter = proc { |severity, datetime, progname, msg| "#{msg}\n" }
begin
server.serve
Process.wait
rescue Interrupt
server.stop
end
end
The problem is that, sometimes, when I do ruby server.rb from my terminal, the server starts, but when I try to make a request on localhost:2835, it fails. Only after several requests it starts serving some pages. In other cases, I need to stop/start the server again for it to properly serve pages. Why is this happening? Am I doing something wrong? I find this very weird...
The same things applies to my specs: I have some specs defined, and some Capybara specs. Before each test I create a server and start it and after each test I stop the server. And the problem persists: tests sometimes pass, sometimes fail because the requested page could not be found.
Is there something fishy going on with my forking?
Would appreciate any answer because I have no more place to look...
Your code is not an HTTP server. It is a TCP server that sends the string "message" over the socket after receiving a newline.
The reason that your code isn't a valid HTTP server is that it doesn't conform to the HTTP protocol. One of the many requirements of the HTTP protocol is that the server respond with a message of the form
HTTP/1.1 <code> <reason>
Where <code> is a number and <reason> is a human-readable "status", like "OK" or "Server Error" or something along those lines. The string message obviously does not conform to this requirement.
Here is a simple introduction to how you might build a HTTP server in ruby: https://practicingruby.com/articles/implementing-an-http-file-server
I'm trying to go through this particular code example from "The Well Grounded Rubyist" regarding TCPServer and threads. The code is below:
require 'socket'
server = TCPServer.new(3939)
connect = server.accept
connect.puts "Hi. Here's the date."
connect.puts 'date'
connect.close
server.close
How do I know what port is on my Macbook? The docs has 2000 in the example. However, when I try both of these numbers the code doesn't execute, it continues to hang indefinitely.
How can I check if these numbers are verified ports? I tried telnetting to the port number and the connection is refused everytime.
server.accepts waits for a client to connect to the server. If that does not happen, it just keeps waiting. Run the code, then open terminal and type:
require 'socket'
s = TCPSocket.new 'localhost', 3939
At this point you will create TCPSocket, which will connect with your server. This will cause the rest of the code to execute. You can check it with your socket:
while line = s.gets # Read lines from socket
puts line # and print them
end
I'm on Fedora 20 and use ruby 2.1.0. I've the following code from ruby-doc.
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
Ruby throws the following error:
client.rb:3:in `initialize': Connection refused - connect(2) for "localhost" port 2000 (Errno::ECONNREFUSED)
from client.rb:3:in `new'
from client.rb:3:in `<main>'
What could be the reason for this failure? I mean, the code should definitively work, it's dead simple and from a recognized tutorial web page for ruby. I guess that the problem is my operating system, but how do I get it working properly?
Sockets are the endpoints of a bidirectional communications channel. Sockets may communicate within a process, between processes on the same machine, or between processes on different continents.
A Simple Client:
require 'socket' # Sockets are in standard library
hostname = 'localhost'
port = 2000
s = TCPSocket.open(hostname, port)
while line = s.gets # Read lines from the socket
puts line.chop # And print with platform line terminator
end
s.close
Now call TCPServer.open hostname, port function to specify a port for your service and create a TCPServer object.
A Simple Server:
require 'socket' # Get sockets from stdlib
server = TCPServer.open(2000) # Socket to listen on port 2000
loop { # Servers run forever
client = server.accept # Wait for a client to connect
client.puts(Time.now.ctime) # Send the time to the client
client.puts "Closing the connection. Bye!"
client.close # Disconnect from the client
}
read doc
I'm attempting to create a script in ruby that connects to a Minecraft server via TCP and fetches the current number of players much like the PHP script at http://www.webmaster-source.com/2012/07/05/checking-the-status-of-a-minecraft-server-with-php/
When running the code below I get �Took too long to log in
require 'socket'
server = TCPSocket.new '192.241.174.210', 25565
while line = server.gets
puts line
end
server.close
What am I doing wrong here?
you're not sending this:
fwrite($sock, "\xfe");
from the script you linked. You have to send that before you call read, like they do.
Basically the server is waiting for you to send data and when you don't after a timeout, you are disconnected.