消息大小因TCPServer Ruby

时间:2017-09-08 16:01:11

标签: ruby sockets messages tcpserver

我正在使用AVL(Skypatrol TT8750 +),它发送的消息(使用TCP)应该是59字节长,但它总是发送第一条消息(该消息包含有关AVL的一些信息,因此用户可以识别33bytes。

所以问题是,如何在ruby上处理那些不同大小的消息?

require 'socket'

portnumber = 12050
socketServer = TCPServer.open(portnumber)

while true
  Thread.new(socketServer.accept) do |connection|
  puts "Accepting connection from: #{connection.peeraddr[2]}"
  t = Time.now.strftime("%d-%m-%Y %H%M")
  file_name = t + '.txt'
  out_file = File.new(file_name, "w+")
  begin
    while connection
      incomingData = connection.gets()
      if incomingData != nil
        incomingData = incomingData
      end
      hex_line = incomingData.unpack('H*')[0]
      out_file.puts(hex_line)
      puts "Incoming: #{hex_line}"
    end
    rescue Exception => e
      # Displays Error Message
      puts "#{ e } (#{ e.class })"
    ensure
      connection.close
      puts "ensure: Closing"
    end
  end
end

这是我正在使用的实验代码。

2 个答案:

答案 0 :(得分:0)

解决方案非常简单

user> (let [real-print-method print-method]
        (with-redefs [print-method (fn [v w]
                                     (if (and (map? v)
                                              (:foo v))
                                       (do
                                         (real-print-method "{:foo " w)
                                         (real-print-method (:foo v) w)
                                         (real-print-method " ...}" w))
                                       (real-print-method v w)))]
          (println {:foo 42 :bar 23} {:baz 11 :quux 0})))
{:foo 42 ...} {:baz 11, :quux 0}
nil
user> 

我只需要一个额外的变量和一个if来自动增加变量,就是这样。

答案 1 :(得分:0)

我发布这个答案来解释我对安德森的回答。大多数代码都不是我的。

if移出循环

if语句在循环中时,每次循环运行时都会对它进行求值,增加CPU指令的数量和每个循环的复杂性。

您可以通过将条件语句移出循环来提高性能,如下所示:

require 'socket'
require 'celluloid/io'

portnumber = 12050
socketServer = TCPServer.open(portnumber)
incomingData = nil

while true
  Thread.new(socketServer.accept) do |connection|
  puts "Accepting connection from: #{connection.peeraddr[2]}"
  # this should probably be changed,
  # it ignores the possibility of two connections arriving at the same timestamp.
  t = Time.now.strftime("%d-%m-%Y %H%M")
  file_name = t + '.txt'
  out_file = File.new(file_name, "w+")

  begin
    if connection
      incomingData = conection.recv(33)
      if incomingData != nil
        incomingData = incomingData.unpack('H*')[0]
        out_file.puts(incomingData)
        puts "Incoming: #{incomingData}"
      end
    end
    while connection
      incomingData = connection.recv(59)
      if incomingData != nil
        incomingData = incomingData.unpack('H*')[0]
        out_file.puts(incomingData)
        puts "Incoming: #{incomingData}"
      end
    end
    rescue Exception => e
      # Displays Error Message
      puts "#{ e } (#{ e.class })"
    ensure
      connection.close
      out_file.close
      puts "ensure: Closing"
    end
  end
end

优化recv方法

我应该提到的另一个优化(但不会在这里实现)将是recv方法调用。

这既是优化,也是应该解决的错误的可能来源。

recv是系统调用,并且由于网络消息可能跨TCP / IP数据包组合(或分段),调用recv比处理数据的内部缓冲区可能变得更加昂贵解决了碎片和溢出状态。

重新考虑每个客户端的线程设计

我还建议避免每个客户端的线程设计。

一般来说,对于少数客户而言,它可能并不重要。

但是,随着客户端的繁殖和线程变得更加繁忙,您可能会发现系统在上下文切换上花费的资源多于实际任务。

另一个问题可能是每个线程需要分配的堆栈(对于Ruby线程,1Mb或2Mb,如果我没记错的话)......在最好的情况下,1,000个客户端只需要堆栈就需要超过GigaByte的内存分配(我忽略了内核结构数据表和其他资源)。

我会考虑使用EventMachine或Iodine(我是碘的作者,所以我有偏见)。

一个公平的设计可以为你节省很多资源。

例如(未经测试):

require 'iodine'
# define the protocol for our service
class ExampleProtocol
  @timeout = 10
  def on_open
    puts "New Connection Accepted."
    # this should probably be changed,
    # it ignores the possibility of two connections arriving at the same timestamp.
    t = Time.now.strftime("%d-%m-%Y %H%M")
    file_name = t + '.txt'
    @out_file = File.new(file_name, "w+")
    # a rolling buffer for fragmented messages
    @expecting = 33
    @msg = ""
  end

  def on_message buffer
    length = buffer.length
    pos = 0
    while length >= @expecting
        @msg << (buffer[pos, @expecting])
        out_file.puts(msg.unpack('H*')[0])
        length -= @expecting
        pos += @expecting
        @expecting = 59
        @msg.clear
    end
    if(length > 0)
        @msg << (buffer[pos, length])
        @expecting = 59-length
    end
  end

  def on_close
    @out_file.close
  end
end
# create the service instance
Iodine.listen 12050, ExampleProtocol
# start the service
Iodine.start