From: Pierre Barbier de Reuille Date: 2005-09-12T17:36:33+09:00 Subject: Problem with sockets Hello there ! I was trying to code a scriptable MUD client (following the RubyQuiz challenge). For the moment, I implemented a scriptable telnet which works fine with my mail server and also with the test server provided by James Edward Gray II on the RubyQuiz website. However, when I tried it on a real MUD server, it just stopped receiving data after a few lines (I tested it on imperian.com:23). The connection is not lost however, as I can send commands (simple: if you can send "3", it will exit your session :) ). I join my code here, all the connection stuff are in the two methods "connect" and "idle" of the MUD class. Thanks, Pierre Code : ======8<============8<===========8<===========8<============8<========= #!/usr/bin/ruby -w require "socket" class MUD attr_reader :url, :port, :prefix, :io attr_reader :commands, :answers def initialize(url, port) @url = url @port = port @answers = MUDFilters.new @commands = MUDFilters.new @prefix = "!" @io = MUD::Io.new register_defaults end def connect @io.send ||= lambda { |line| @io.server_socket.write line } @io.report ||= lambda { |line| $stdout.write line } @socket = TCPSocket.new(@url, @port) @io.report["Connected to #{@url}:#{@port}\n"] @io.server_socket = @socket @io.freeze @url.freeze @prefix.freeze @thread = Thread.new do idle end end def disconnect @io.server_socket.close unless @io.server_socket.closed? @thread.join puts "Disconnected ..." end def idle f = File.new("log_server", "wb") begin while line = @io.server_socket.gets f.write line process_answer line end rescue end @io.server_socket.close unless @io.server_socket.closed? return 0 end def process_command(line) process(@commands, line) end alias_method :send, :process_command def process_answer(line) process(@answers, line) end def add_command(regexp=//, priority=0, &block) register(@commands, Rule.new(regexp, priority, &block)) end def remove_command(id) unregister(@commands, id) end def add_answer(regexp=//, priority=0, &block) register(@answers, Rule.new(regexp, priority, &block)) end def remove_answer(id) unregister(@answers, id) end protected def register_defaults # Echo the server commands if nothing else add_answer do |io,line| io.report[line] end # Send the commands to the server add_command do |io,line| io.send[line] end # Load a new definition file add_command(/^#{@prefix}load/i, 10000) do |io,line| if line =~ /^#{@prefix}load (.*)$/i config = $1 if config and FileTest.exists? config and FileTest.readable?(config) io.report["Loading file #{config} ...\n"] load(config,true) end end nil end end def process(filters,line) selected = [] filters.each do |regexp, rules| if line =~ regexp selected += rules end end selected.sort! selected.each do |rule| #puts "Launching rule #{rule.regexp.inspect} with priority #{rule.priority}" line = rule.block[@io,line] if line == nil return end end end def register(filters, rule) filters[rule.regexp] << rule filters.ids[rule.object_id] = rule.regexp rule.object_id end def unregister(filters, id) regexp = filters.ids[id] filters[regexp].delete_if do |rule| rule.object_id == id end end class Rule attr_accessor :priority, :block attr_reader :regexp def initialize(regexp, priority, &block) @regexp = regexp @priority = priority @block = block end def <=>(other) other.priority <=> @priority end end class Io attr_accessor :send, :report attr_accessor :server_socket end class MUDFilters attr_accessor :ids include Enumerable def initialize @filters = Hash.new { |h,k| h[k] = [] } @ids = Hash.new end def [](regexp) @filters[regexp] end def []=(regexp, value) @filters[regexp] = value end def each(&block) @filters.each(&block) end end end if $0 == __FILE__ require "getoptlong" opts = GetoptLong.new( [ "--port", "-p", GetoptLong::OPTIONAL_ARGUMENT ], [ "--config", "-c", GetoptLong::OPTIONAL_ARGUMENT ], [ "--prefix", GetoptLong::OPTIONAL_ARGUMENT], [ "--prompt", GetoptLong::OPTIONAL_ARGUMENT] ) config = nil port = 23 prefix = "!" opts.each do |opt, arg| case opt when "--port" port = arg.to_i when "--config" config = arg when "--prefix" prefix = Regexp.escape arg end end if ARGV.length != 1 $stderr.write <<-EOF Usage: #{$0} [--port PORT] [--config FILE] [--prefix PREFIX] [--] HOST PORT port of the host to connect (default: #{port.to_s}) FILE ruby file imported with a main function called with the MUD object (default:none) HOST host to connect PREFIX prefix for local commands (default:#{prefix}) EOF exit 2 end host = ARGV[0] $mud = MUD.new(host, port) $mud.prefix.replace(prefix) if config and FileTest.exists? config and FileTest.readable? config load(config,true) end $mud.connect begin loop do line = $stdin.readline $mud.send line end rescue EOFError $mud.disconnect end end ======>8============>8===========>8===========>8============>8=========