I want to implement a while-loop that runs as long as a Client is connected at my socket.
It's gonna be Thread based so i want to make shure that the Thread gets closed once the Client disconnected.
question: How do I check if the Client is still connected to my socket?
question: Is the Thread already getting closed when connection closes if I startet it like this: Thread.start(socket.accept) do |client| ...
Check this out,
require 'socket'
th=[]
server = TCPServer.open(2000)
loop do
Thread.start(server.accept) do |client|
client.puts "server started.."
# Something
th = Thread.list
th.each do |t|
if t.status == "sleep" # checks all client status
f = t.kill # kills thread if client is disconnected
end
end
end
end
You may kill the thread or you can store the IP addres of clients using that thread in an array and make a compare with that for exact client.(like, t.addr)
I don't have experience programming ruby. If it was .Net, what you pretend is not possible without some kind of keep-alive protocol. If the server does not get data from client after x seconds it assumes the client disconnected. The client must have a timer to send something to the server when not communicating.
server = TCPServer.open 12345
loop {
Thread.start(server.accept) do |client|
...
puts "client is offline!" if client.closed?
...
client.close
end
}
You may check "closed?" in "client".
Related
While running a TCPServer in ruby 2.7.0, I want to see when my client has closed the connection (or is unable to continue reading). However, when I check on the server, I never see that the connection has closed.
I've tried using a bunch of the different ruby socket primitives but nothing seems to work here. I've tried writing to the socket as well in hopes of forcing an error but that doesn't seem to help.
I'm including an example here:
# main.rb
require_relative 'server'
PORT = 9000
server_thread = Server.thread
socket = TCPSocket.open("localhost", PORT)
socket.puts '5'
server_thread.join(1)
socket.close
puts socket.closed?
server_thread.join(2)
# server.rb
require 'socket'
class Server
def self.thread
Thread.new do
server = TCPServer.open(PORT)
while true
server.accept do |socket|
while true
socket.puts '1'
# why doesn't this ever happen?
puts 'closed' if socket.closed?
end
end
end
end
end
end
When running ruby main.rb, this code outputs
true
Whereas I expect it to output:
true
closed
The block that you pass to server.accept is ignored because Socket#accept does not accept a block argument.
When you call socket.close in main.rb, you close the client side of the connection. The server side of the connection will remain open.
You could call IO#read to wait until the client closes the connection.
Thread.new do
server = TCPServer.open(PORT)
loop do
socket = server.accept
socket.read
puts 'client closed connection'
socket.eof? #=> true
socket.close
end
end
The only response provided did not work. Attempting to read from the closed socket did not error like I expected it to.
I have come to the conclusion that what I was asking is simply not possible. You must either:
Send a keep-alive from the client and close when you do not receive it or
Face the consequences of not knowing whether or not your writes have
succeeded.
Personally, I was able to live with 2 since this was for a prototype.
I've read some things suggesting that because of the design of TCP this might not be possible (such as: Java socket API: How to tell if a connection has been closed?), but I'm trying to find explicit confirmation. I have a basic TCP server that accepts connections, and a client that initiates a connection, sends a message, and then closes the connection. Is there a way for the server to know that the client closed the connection?
I found some suggestions to look into checking the file descriptors for the sockets (source: How to check if a given file descriptor stored in a variable is still valid?), using the kernel select command (source: https://bytes.com/topic/c/answers/866296-detecting-if-file-descriptor-closed) as well as using recv to check if the client returns 0 (source: http://man7.org/linux/man-pages/man2/recv.2.html#RETURN_VALUE), but these do not seem to work, at least not when called by Ruby. To test this, I wrote a basic server and client:
test_server.rb
require 'socket'
require 'fcntl'
TIMEOUT = 5
server = TCPServer.new('localhost', 8080)
puts "Starting server"
loop do
client = server.accept
puts "New client: #{client}"
puts "** before closed #{Time.now.to_i} closed=#{client.closed?}"
result = IO.select([client], nil, nil, TIMEOUT)
puts "select result=#{result}"
fd = client.fcntl(Fcntl::F_GETFD, 0)
puts "client fd=#{fd}"
stuff = client.recv(30)
puts "received '#{stuff}'"
begin
r = client.recv(1)
rescue => e
end
puts "received #{r} nil?=#{r.nil?}"
sleep 3
puts "** after closed #{Time.now.to_i} closed=#{client.closed?}"
result = IO.select([client], nil, nil, TIMEOUT)
puts "select result=#{result}"
fd = client.fcntl(Fcntl::F_GETFD, 0)
puts "client fd=#{fd}"
begin
r = client.recv(1)
rescue => e
end
puts "received #{r} nil?=#{r.nil?}"
puts "done!"
end
test_client.rb
require 'socket'
class Client
def initialize
#socket = tcp_socket
end
def tcp_socket
Thread.current[:socket] = TCPSocket.new("localhost", 8080)
end
def send(s, args={})
puts "sending str '#{s}'"
nbytes = #socket.send(s, 0)
puts "received #{nbytes} bytes"
sleep 1
#socket.close
puts "done at #{Time.now.to_i}: #{#socket.closed?}"
end
end
msg = 'hello world this is my message'
server = Client.new
server.send(msg)
The client sends a 30-byte message, waits 1s, then closes the connection.
The server accepts the connection, calls select and fcntl on it to check its status, receives the message, tries to read 1 more byte, sleeps for 3 seconds, then calls select and fcntl and again tries to read 1 byte. The intent here is to check if anything changes that the server can see before and after the client closed the connection (hence the 3-second sleep). The result I get from running the server and then the client code is:
Starting server
New client: #<TCPSocket:0x00007fa0930f0880>
** before closed 1578005539 closed=false
select result=[[#<TCPSocket:fd 10>], [], []]
client fd=1
received 'hello world this is my message'
received nil?=false
** after closed 1578005543 closed=false
select result=[[#<TCPSocket:fd 10>], [], []]
client fd=1
received nil?=false
done!
Before and after the client closed the connection, select still sees the socket as readable, the underlying file descriptor does not change, and recv returns empty string (It's possible the kernel call is returning 0 as specified in the man-page but Ruby is capturing that, and if so I don't know how to see it.). Thus none of these seem to be a reliable indicator of whether the connection was closed from the other side. Is there something I'm missing?
I have seen some other suggestions to incorporate a regular heartbeat back to the client, but I'm wondering if there's a way to avoid that. Reason is that I'm trying to accommodate a case where the client may be sending a message in several pieces separated by a delay (e.g. 100 bytes at 1 second each byte). If the server sends a heartbeat message in the middle of that operation and listens for an OK, I presume the client has to be listening for the heartbeat as well and send its OK back, separate from the ongoing message send, and in my test case, I can't change the client to do that.
I have seen some other suggestions to incorporate a regular heartbeat back to the client, but I'm wondering if there's a way to avoid that.
A heartbeat (ping) is the only viable solution.
There is no way to reliably know if the connection is live except by trying to send data over the wire.
Since TCP/IP doesn't require any traffic when data isn't being sent (or received), there's no way for the TCP stack (not even in the OS kernel) to know if the connection is "live" without attempting to exchange data over the wire.
Some connections will close gracefully, allowing the TCP stack to recognize that the connection was closed - but this isn't always true (you can read more about "half-open" or "half-closed" connections).
For this reason, all servers implement a timeout / ping mechanism to test for lost connectivity.
I'm trying to accommodate a case where the client may be sending a message in several pieces separated by a delay (e.g. 100 bytes at 1 second each byte)...
Remember that TCP/IP is a stream based protocol, not a message based protocol.
This means that your 100 bytes might arrive fragmented or they might be combined with a previous message.
If you're sending messages (rather than streaming data), you need - by design - to mark message boundaries.
Since these message boundaries must be marked, it becomes relatively easy to add a message type marker (to mark ping/pong messages).
You can observer the WebSocket protocol message format to learn more about message based protocol design using a TCP/IP (streamed) connection.
I am trying to implement a small script that will create a server, wait for a client to connect, create a new thread for the client and then process client requests. The first request from the client will be a HELO text and the server must respond with the HELO text followed by IP Address, Port Number and a Student Number. I have got this bit working.
The next request will be any random string and this must not return anything. I have got this bit working.
The last request will be a KILL_SERVICE request which must close the server. I am not sure how to do this. The other issue is that my program will only work with one command at a time. I do not know how to deal with one command and then wait for the next command from the client. My server code is below. Any help would be appreciated.
require 'socket'
port = 8888
puts "Starting Up Server"
server = TCPServer.open(port)
while (client = server.accept)
Thread.start do
input = client.gets
if input.start_with?("HELO")
client.puts "#{input}IP:#{client.peeraddr[2]}\nPort:#{port}\nStudentID:[2]\n"
elsif input == "KILL_SERVICE\n"
client.puts "KILL"
else
puts input
end
end
end
As soon as your code leaves the Thread.start block that thread is considered finished. If you want it to stick around you need to wrap it in a loop:
while (client = server.accept)
Thread.start do
client_running = true
while (client_running)
# ... Your code
end
end
end
Then, if any of those commands should stop the thread, set client_running = false and the loop will exit. To force shut-down the whole thing, exit(0) will end the process.
Update: Added block on how to handle shutting down the server itself, not just the connection.
You need to continue waiting for input from the client after accepting the connection.
while (client = server.accept)
Thread.start do
while input = client.gets
# process input
end
end
end
When you receive KILL_SERVICE you need to close the client using client.close and break out of the while loop using break. Once you've broken out of the while loop, the thread handling that client will exit.
So, I'm trying to simulate some basic HTTP persistent connections using sockets and Ruby - for a college class.
The point is to build a server - able to handle multiple clients - that receives a file path and gives back the file content - just like an HTTP GET.
The current server implementation loops listening for clients, fires a new thread when there's an incoming connection and reads the file paths from this socket. It's very dumb, but it works fine when working with non-presistent connections - one request per connection.
But they should be persistent.
Which means the client shouldn't worry about closing the connection. In the non-persistent version the servers echoes the response and close the connection - goodbye client, farewell.
But being persistent means the server thread should loop and wait for more incoming requests until... well until there's no more requests. How does the server knows that? It doesn't! Some sort of timeout is needed. I tried to do that with Ruby's Timeout, but it didn't work.
Googling for some solutions - besides being thoroughly advised to avoid using Timeout module - I've seen a lot of posts about the IO.select method, that should handle this socket waiting issue way better than using threads and stuff (which really sounds cool, considering how Ruby threads (don't) work). I'm trying to understand here how IO.select works, but still wasn't able to make it work in the current scenario.
So I aske basically two things:
how can I efficiently work this timeout issue on the server-side, either using some thread based solution, low-level socket options or some IO.select magic?
how can the client side know that the server has closed its side of the connection?
Here's the current code for the server:
require 'date'
module Sockettp
class Server
def initialize(dir, port = Sockettp::DEFAULT_PORT)
#dir = dir
#port = port
end
def start
puts "Starting Sockettp server..."
puts "Serving #{#dir.yellow} on port #{#port.to_s.green}"
Socket.tcp_server_loop(#port) do |socket, client_addrinfo|
handle socket, client_addrinfo
end
end
private
def handle(socket, addrinfo)
Thread.new(socket) do |client|
log "New client connected"
begin
loop do
if client.eof?
puts "#{'-' * 100} end connection"
break
end
input = client.gets.chomp
body = content_for(input)
response = {}
if body
response.merge!({
status: 200,
body: body
})
else
response.merge!({
status: 404,
body: Sockettp::STATUSES[404]
})
end
log "#{addrinfo.ip_address} #{input} -- #{response[:status]} #{Sockettp::STATUSES[response[:status]]}".send(response[:status] == 200 ? :green : :red)
client.puts(response.to_json)
end
ensure
socket.close
end
end
end
def content_for(path)
path = File.join(#dir, path)
return File.read(path) if File.file?(path)
return Dir["#{path}/*"] if File.directory?(path)
end
def log(msg)
puts "#{Thread.current} -- #{DateTime.now.to_s} -- #{msg}"
end
end
end
Update
I was able to simulate the timeout behaviour using the IO.select method, but the implementation doesn't feel good when combining with a couple of threads for accepting new connections and another couple for handling requests. The concurrency makes the situation mad and unstable, and I'm probably not sticking with it unless I can figure out a better way of using this solution.
Update 2
Seems like Timeout is still the best way to handle this. I'm sticking with it till find a better option.
I still don't know how to deal with zombie client connections.
Solution
I endend up using IO.select (got inspired when looking at the webrick code). You cha check the final version here (lib/http/server/client_handler.rb)
You should implement something like heartbeat packets.Client side should send special packets to after few secs/mins to ensure that server doesn't time out the connection on the client end.You just avoid doing anything in this call.
I'm making a TCP socket server(ruby). A thread is created for each connected client. At some point I try to send data to all connected clients. The thread aborts on exception while trying.(ruby 1.8.7)
require 'socket'
# I test it home right now
server = TCPServer.new('localhost', 12345);
while(session = server.accept)
#Here is the thread being created
Thread.new(session) do |s|
while(msg = s.gets)
#Here is the part that causes the error
Thread.list.each { |aThread|
if aThread != Thread.current
#So what I want it to do is to echo the message from one client to all others
#But for some reason it doesn't, and aborts on the following string
aThread.print "#{msg}\0"
end
}
end
end
Thread.abort_on_exception = true
end
What am I doing wrong?
The following PDF discusses socket programming in ruby in detail. It includes asynchronous handling and goes through a functioning demonstration of a small chat program.
IBM guide to ruby socket programming