TCP Server

Hi,

I just found an example in talk archive about using thread with tcp server.
If anyone have a solution that does not use thread, please tell me,
otherwise, please accept my apology that I should check the talk-archive
first… :stuck_out_tongue:

Thanks
Shannon

···

Protect your PC - get McAfee.com VirusScan Online
http://clinic.mcafee.com/clinic/ibuy/campaign.asp?cid=3963

Hi Shannon,

I just found an example in talk archive about using thread with tcp server.
If anyone have a solution that does not use thread, please tell me,
otherwise, please accept my apology that I should check the talk-archive
first… :stuck_out_tongue:

I prefer to use select() than threads, for a couple reasons, in Ruby.
My sense is that, since the whole process is blocked (all your threads)
if one of your IO calls stalls (in any thread), then you have to find
a way to do non-blocking IO whether you’re using threads or not. And I
personally find select() to be sufficient to the task without bothering
with threads. (It may just be personal opinion, or preference. :slight_smile:

If it helps, here’s a snippet from a TCP-based chat-like server I
wrote recently. I’ve tried to delete the parts of the code that are
relevant only to my specific application. Thus the code below is
untested, but is drawn from working code (for whatever that’s worth
.)

def accept_and_process_clients(timeout)
begin
# This select call is the key to the whole operation. In my program,
# I only care which clients are ready to be read from (or whether the
# server socket is ready to accept a new connection from a client.)
# You’ll notice I pass in ‘nil’ for asking which sockets are ready
# to be written to. In my case, when I have something to write to
# a socket, I can get away with just doing a blocking write, because
# for my purposes it’s fast enough. You may or may not actually want
# to check which sockets are “ready” to be written to, to avoid any
# blocking at all in your writes.
ios = select([@tcp_server, *@tcp_clients], nil, @tcp_clients, timeout)
if ios
# disconnect any clients with errors
ios[2].each {|sock| ios[0].delete(sock); disconnect_client(sock, false) }
# accept new clients or process existing client input
ios[0].each do |sock|
if sock == @tcp_server
accept_client(sock)
else
process_client_input(sock) # whatever this means in your app.
end
end
end
rescue IOError, SystemCallError
end
end

def accept_client(sock)
client = sock.accept
if client
begin
# Initialize client however makes sense in your application.
# In my case I extend the client socket with some extra
# features from a login-session/terminal-server module.
# I also get the client’s IP and Port numbers from peeraddr,
# which can throw exceptions if there’s a problem, thus the
# rescue clause below…
rescue IOError, SystemCallError
client.close
else
@tcp_clients << client
end
end
end

def disconnect_client(client)
@tcp_clients.delete client
client.close
end

def run
@log_file = File.open(@log_filename, File::WRONLY | File::CREAT | File::APPEND)
@tcp_server = TCPServer.new(@local_port)
begin
loop {
# I do other periodic tasks in the loop besides accept and
# respond to clients - so I have a slightly more complicated
# way of keeping track of the timeout/next-update interval
# which I’ve deleted in this example…
timeout = WhateverYouNeed
accept_and_process_clients(timeout)
}
rescue Interrupt, IRB::Abort, NoMemoryError, SystemExit => ex
puts “Caught exception: #{ex.inspect} at #{ex.backtrace[0]} - saving and shutting down…”
rescue Exception => ex
puts “Caught exception: #{ex.inspect} at #{ex.backtrace[0]} - ignoring and continuing…”
sleep 1
retry
ensure
disconnect_all_clients
@tcp_server.close; @tcp_server = nil
@log_file.close; @log_file = nil
end
end

For completeness, here’s routines where I actually end up doing send &
recv on the client sockets. They’re part of a buffered terminal IO
module which I’ve extended onto each client socket returned from the
@tcp_server’s accept. (Thus the “self.recv”, etc.)

def terminal_buffered_read
begin
if Kernel.select([self], nil, nil, 0)
dat = self.recv(65536)
if !dat || dat.empty?
@term_eof = true
else
@term_read_buf << dat
end
end
rescue IOError, SystemCallError
@term_eof = true
end
end

def send_string(str)
str = str.gsub(/\n/, “\r\n”)
begin
while str.length > 0
sent = self.send(str, 0)
str = str[sent…-1]
end
rescue IOError, SystemCallError
@term_eof = true
end
end

If the recv returns EOF, or if something goes wrong with the
send or recv, I just set a @term_eof flag, because that’s sufficient
for my application… You might want to handle errors differently.
(Note, again, that my send is a blocking-send. But you can make yours
non-blocking using select() if you need to.)

Finally, I should mention that in theory, since Windows doesn’t
support true non-blocking IO on its sockets (as far as I know)
that there is a possibility that the accept() call could block.
[Note, the above code, in the context of my application, has been
used without problem on Windows and Linux.] The possibility for
accept() blocking happens when a client tries to connect, and
select() says “read me!”, but the client hurriedly disconnects,
so that by the time we actually get to the accept(), the client
is gone, and thus the accept() call blocks until a new client
connects. This has not yet happened in my application, but it
should be possible. To avoid this in Unix, I would put the
@tcp_server socket into true non-blocking mode, where in the
above condition, it would return the error EWOULDBLOCK instead
of blocking.

Again, however, in Ruby, using threads would not be a solution to
the above. Because if accept() blocks, your whole process (all
your threads) are blocked. So I’d suggest using threads only if
that model is more convenient to how your application processes
its data. For mine, threads seemed like extra work.

Hope this helps,

Bill

I just found an example in talk archive about using thread with tcp server.
If anyone have a solution that does not use thread, please tell me,
otherwise, please accept my apology that I should check the talk-archive
first… :stuck_out_tongue:

I prefer to use select() than threads, for a couple reasons, in Ruby.
My sense is that, since the whole process is blocked (all your threads)
if one of your IO calls stalls (in any thread), then you have to find
a way to do non-blocking IO whether you’re using threads or not. And I
personally find select() to be sufficient to the task without bothering
with threads. (It may just be personal opinion, or preference. :slight_smile:

I prefer to use a combination of both select and threads,
I’m a bit thread-crazy though due to the extreme ease of thread-use in ruby :slight_smile:

class SimpleTCPServer
def initialize(port, interface, max_users = 4, timeout = 10)
@server, @thread_limit = TCPServer.new(interface, port), max_users
@thread_count = 0
@timeout = timeout
end

def handle_request( request )
	# perform the actual request, return nil if the client should be closed
end

def handle_user( socket )
	while not socket.closed?
		a = select([socket], nil, nil, @timeout)
		unless a == nil
			unless (b = handle_request( socket.readline.chop )) == nil
				socket.print b.to_s
			else 
				socket.end
			end
		else
			socket.close
		end
	end
end

def listen
	while (session = server.accept)
		if (@thread_count < @thread_limit)
			Thread.new{
				ref = session
				begin
					@thread_count += 1
					handle_user( ref )
				rescue IOError
				end
				
				ref.close unless ref.closed?
				@thread_count -= 1
			}
end

end

Bill Kelly wrote:

I prefer to use select() than threads, for a couple reasons, in Ruby.
My sense is that, since the whole process is blocked (all your threads)
if one of your IO calls stalls (in any thread), then you have to find
a way to do non-blocking IO whether you’re using threads or not. And I
personally find select() to be sufficient to the task without bothering
with threads. (It may just be personal opinion, or preference. :slight_smile:

I have not encountered such situation when using threads.

My application have a set of worker threads, which either serve a
client, waiting on a Mutex protecting accept(), or waiting at accept(),
so there is always one thread waiting on accept(), and the application
does not block.

I suppose Ruby does some tricks to overcome the blocking.

Idan.

Bill Kelly wrote:

I prefer to use select() than threads, for a couple reasons, in Ruby.
My sense is that, since the whole process is blocked (all your threads)
if one of your IO calls stalls (in any thread), then you have to find
a way to do non-blocking IO whether you’re using threads or not. And I
personally find select() to be sufficient to the task without bothering
with threads. (It may just be personal opinion, or preference. :slight_smile:

I have not encountered such situation when using threads.

My application have a set of worker threads, which either serve a
client, waiting on a Mutex protecting accept(), or waiting at accept(),
so there is always one thread waiting on accept(), and the application
does not block.

I suppose Ruby does some tricks to overcome the blocking.

Whoa. Interesting. Well, my apologies if my post was incorrect.
I’ve read accounts on ruby-talk in the past of the whole process
(all Ruby-threads) being blocked during IO. I had taken this to
mean this was the general case, rather than some specific exception
being discussed.

Hmm, it’s nice when Ruby is smarter than me (seems to keep happening :slight_smile:

In my particular application, avoiding threads still seems convenient,
as I can disregard reentrancy issues.

Thanks,

Bill

···

From: “Idan Sofer” idan@idanso.dyndns.org

From: “Idan Sofer” idan@idanso.dyndns.org

Bill Kelly wrote:

I prefer to use select() than threads, for a couple reasons, in Ruby.
My sense is that, since the whole process is blocked (all your threads)
if one of your IO calls stalls (in any thread), then you have to find
a way to do non-blocking IO whether you’re using threads or not. And I
personally find select() to be sufficient to the task without bothering
with threads. (It may just be personal opinion, or preference. :slight_smile:

I have not encountered such situation when using threads.

My application have a set of worker threads, which either serve a
client, waiting on a Mutex protecting accept(), or waiting at accept(),
so there is always one thread waiting on accept(), and the application
does not block.

I suppose Ruby does some tricks to overcome the blocking.

Whoa. Interesting. Well, my apologies if my post was incorrect.
I’ve read accounts on ruby-talk in the past of the whole process
(all Ruby-threads) being blocked during IO. I had taken this to

What might be ment is that when you write your own C extention and you call
a C function from Ruby, all Ruby threads are blocked until the function
returns. So if you block on IO operation (say, calling read()) in that
function, no Ruby threads will have a chance to execute. However if you do
IO via pure Ruby API, everything works fine (threads are not blocked).

···

----- Original Message -----
From: “Bill Kelly” billk@cts.com
To: “ruby-talk ML” ruby-talk@ruby-lang.org
Sent: Friday, December 13, 2002 12:54 PM
Subject: Re: TCP Server

mean this was the general case, rather than some specific exception
being discussed.

Hmm, it’s nice when Ruby is smarter than me (seems to keep happening :slight_smile:

In my particular application, avoiding threads still seems convenient,
as I can disregard reentrancy issues.

Thanks,

Bill