【问题标题】:Perl socket parse packets from a network streamPerl 套接字从网络流中解析数据包
【发布时间】:2016-05-15 16:22:13
【问题描述】:

我正在尝试找出一种使用 perl 解析数据流的正确方法。 我已经阅读了许多示例、文档和问题,但无法找到我基本上如何从数据流中剪切一个“包”并对其进行处理。 这是这种情况: - 从某个 IP 到 IP 和端口的数据流 - 流包含一些乱码,然后是一些内容,其中的数据以分号分隔

到目前为止,我的尝试是让 Socket 监听端口并处理 $data var:

#!/usr/bin/perl
    use IO::Socket::INET;
    # auto-flush on socket
    $| = 1;

# creating a listening socket
my $socket = new IO::Socket::INET (
    LocalHost => '127.0.0.1',
    LocalPort => '7070',
    Proto => 'tcp',
    Listen => 5,
    Reuse => 1
);
die "cannot create socket $!\n" unless $socket;
print "server waiting for client connection on port 7070 \n";

while(1)
{
    # waiting for a new client connection
    my $client_socket = $socket->accept();

    # get information about a newly connected client
    my $client_address = $client_socket->peerhost();
    my $client_port = $client_socket->peerport();
    print "connection from $client_address:$client_port\n";

    # read up to 1024 characters from the connected client
    my $data = "";
    $client_socket->recv($data, 1024);
    print "received data: $data\n";

    @data_array = split(/;/,$data);
    foreach (@data_array) {
      print "$_\n";
    }

    # write response data to the connected client
    $data = "ok";
    $client_socket->send($data);

    # notify client that response has been sent
    shutdown($client_socket, 1);
}

$socket->close();

这可行,但据我了解,这会将整个流调整到最大大小,然后再进行处理。

我的问题: 如何确定我需要的部分(开始-结束),处理它然后继续下一个?

【问题讨论】:

  • 传入数据的示例有助于理解问题——我们不知道是什么定义了您需要的部分。请注意,您通常可以假设流套接字完美地中继数据,因此为了避免在接收器上出现乱码,最好的对策是避免发送它。如果您不控制源,最好的办法是查找框架数据并从那里开始解析。

标签: perl sockets parsing stream


【解决方案1】:

我一直不明白为什么人们使用recv 来读取流套接字。

通常,阅读循环如下所示:

my $buf = '';
while (1) {
   my $rv = sysread($socket, $buf, 64*1024, length($buf));
   if (!defined($rv)) {
      die("Can't read from socket: $!\n");
   }

   if (!$rv) {
      die("Can't read from socket: Premature EOF\n") if length($buf);
      last;
   }

   while (my $msg = defined(check_for_full_message_and_extract_it_from_buf($buf))) {
      process_msg($msg);
   }
}

(请记住,只要有一些数据,sysread 就会立即返回,即使数据少于请求的数据。)

例如,标记终止数据的内部循环如下所示:

   while ($buf =~ s/^(.*)\n//) {
      process_msg("$1");
   }

例如,以长度为前缀的块的内部循环如下所示:

   while (1) {
      last if length($buf) < 4;

      my $len = unpack('N', $buf);
      last if length($buf) < 4+$len;

      substr($buf, 0, 4, '');
      my $msg = substr($buf, 0, $len, '');
      process_msg($msg);
   }

如果您是特殊情况,您将从$buf 开始删除您想要忽略的任何数据,直到您到达您感兴趣的部分,然后您将开始提取其中的项目你感兴趣。这很模糊,但我对使用的协议只有一个模糊的描述。

【讨论】:

  • 感谢您的回答。恐怕我目前的 perl 知识太有限,无法使用它。同时我可能对输入做出了错误的假设,它似乎是一个包含一些关于长度的数据的 tcp 连接。因为我对我要求的解决方案非常感兴趣,所以让我们继续。为什么你更喜欢 sysread 而不是 recv?如果我理解正确,您基本上读取 64 个字节,检查数据并让单独的子负责缓冲区操作?我正在考虑处理每个字符/字节,以便我可以检查一个我有完整的“消息”。
  • Re "为什么你更喜欢 sysread 而不是 recv?",recv 是用来接收消息的,但是流没有这样的东西。充其量,recvsysread 做的事情完全相同。 /// Re "you基本上读取64字节",正如我已经提到的,它读取高达 64 KiB。 (确切的数字并不重要,但通常越大越快。) /// 不必是单独的子。只是一个描述性的占位符。 /// 回复“我正在考虑按字符/字节处理”,一次读取一个字节会非常慢。至于实际如何检查完整的消息,解决方案会因协议而异。
  • 协议应该已经在问题中定义,并且应该放置在哪里。如果你这样做,请告诉我,我会更新我的答案。
  • 您没有提供任何新信息。 (而且你完全无视我关于应该在哪里发布的评论!!!)
  • 不,您没有提供任何新信息。您只是以不同的方式发布了相同的信息。仍然不知道相关位从哪里开始以及相关在哪里结束(一般来说)。 /// 我使用了sysread,因为它是唯一有意义的工具。 recv 是“用于从套接字接收消息”,这对流没有意义。
【解决方案2】:

我通过使用原始代码并添加解决了这个问题:

if ( $data=~/<START>>/) {
    print "\nFound start\n";
    $message.=$data;
    while ($message !~/END/){
        $client_socket->recv($data, $message_length);
        $message.=$data;
        print "\nStill reading\n"; 
    };
    print "\nFound end\n"; # but may contain (part of) next START
}

我仍然需要实现我检查读取的块是否包含下一条消息的部分,但我会弄清楚这一点。 感谢您的帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-28
    • 2017-09-21
    相关资源
    最近更新 更多