From: Brian Candler Date: 2004-09-21T00:22:21+09:00 Subject: Re: TCP Socket read and write On Sun, Sep 19, 2004 at 09:24:41PM +0900, Brian Schr?der wrote: > Hello Group, > > I'm writing a simple chat client-server as an introductory example, and > now I'm wondering about some things. > > First: Do I have to make TCPSocket#puts TCPSocket#gets thread save by > using mutexes? Not unless you have two threads which are talking on the same TCP socket at once (which would be unusual). The generic TCP server skeleton I use looks like this: ------------------------------------------------------------------------- require 'socket' module MyServer def run print "Hello, world!\r\n" line = gets print "You said #{line}\r\n" end end port = ARGV[0] || 9876 bind = ARGV[1] || '0.0.0.0' server = TCPserver.new(bind, port) while s = server.accept s.extend MyServer Thread.new(s) do |session| begin session.run rescue Exception => e $stderr.puts "Caught exception: #{e}\n\t#{e.backtrace.join("\n\t")}" ensure session.close end end end ------------------------------------------------------------------------- A new thread is started for each session, and any local variables instantiated in the 'run' method (like 'line' in this case) are separate for each thread, so you don't have to worry about them interfering with each other. The only thing to beware of is the local variable 's' in the main loop. As soon as the main loop goes back up to the top and a new connection is accepted, 's' changes. That's why we pass 's' in as a parameter to Thread.new, where it is copied into the block-local variable 'session' for use later when we close the connection. If you don't like the way I add a 'run' method to TCPSocket's singleton class, you can always put the code in-line: ... while s = server.accept Thread.new(s) do |session| begin session.print "Hello, world!\r\n" line = session.gets session.print "You said #{line}\r\n" rescue Exception => e ... > Second: Is there a possibility do use push rather than poll for reading > the Socket. (If it is already threadsafe, then there is nothing to do > here, but if not, I'd have to peek the TCPSocket, and send afterwards if > nothing was in the pipe). Otherwise I could just have a reading and a > sending thread. I'm not really sure what you mean here. You read the socket by calling TCPSocket#read or TCPSocket#gets. If you want to check whether there's data available, then you can call select first - although that only guarantees there's one byte available, so you'd have to do read(1) to guarantee that you never blocked. If you want to stream data out at the same time as streaming data in (rather than lock-step command-response-command-response), then yes you'd use two threads, one reading and one writing. As long as one only does 'reads' and one only does 'writes', then you don't need to mutex them. But you may need to signal between them so that if one side detects the socket has been closed, the other terminates as well. Regards, Another Brian.