我有一个守护进程,它接受连接、通过单个 sysread 读取 JSON 请求、处理它并通过 syswrite 发回 JSON 响应。这工作正常。但我想让阅读在部分阅读的情况下更加健壮。所以我使用了常见的技术,我看到人们使用循环直到读取 0 个字节,同时指定将输入附加到缓冲区的末尾。当我执行这个简单的循环时,以下 syswrite 声称它写出了 X 个字节,但调用者认为它读回了 0 个字节。似乎在套接字上执行 1 次以上的 sysread 会导致 future 对该套接字的 syswrite 失败。

即:我已经更改了我的单个 sysread 来执行此操作:

my $done = 0 ;
while ( ! $done ) {
    $bytes = sysread( $client, $json_text, BUFSIZ, length( $json_text )) ;
    $done++ if ( $bytes == 0 ) ;
}

我已经验证在每种情况下,我收到的请求是相同的,我发送的响应是相同的。唯一改变的是额外的 sysread,它读取了 0 个字节,表明我已经阅读了所有可用的内容。

我附上了一个简单的精简程序来说明这一点。
有人可以向我解释这里发生了什么吗?
我还尝试专门告诉 syswrite 要写入的字节数和起始位置 (0),效果相同。
#!/usr/bin/env perl

use Socket ;
use JSON ;
use constant BUFSIZ => 1024 ;

my $protocol = getprotobyname( 'tcp' ) ;

my $server ;
if ( ! socket( $server, AF_INET, SOCK_STREAM, $protocol )) {
    die( "socket() failed: $!" ) ;
}
if ( ! setsockopt( $server, SOL_SOCKET, SO_REUSEADDR, 1 )) {
    die( "setsockopt(): failed: $!" ) ;
}

my $my_addr = sockaddr_in( 2007, INADDR_ANY ) ;
bind( $server, $my_addr )     or die( "bind(): failed: $!" ) ;
listen( $server, SOMAXCONN )  or die( "listen(): failed: $!" ) ;

my ( $remote_addr, $client ) ;
$remote_addr = accept( $client, $server ) ;
$_ = select( $client ); $| = 1; select $_ ;       # autoflushing
my $json_text = "" ;
my $bytes     = 0 ;

# a later syswrite() doesn't work when sysread() called more than once
my $done = 0 ;
while ( ! $done ) {
    $bytes = sysread( $client, $json_text, BUFSIZ, length( $json_text )) ;
    $done++ if ( $bytes == 0 ) ;
}

# calling sysread once works ok with following syswrite
# sysread( $client, $json_text, BUFSIZ, length( $json_text )) ;

print "Got a JSON request: $json_text\n" ;

# just make up some dummy response to whatever we received
my $out_struct = {
    'id'        => 'server6-alive-test',
    'output'    => [ 'FOOBAR' ],
    'errors'    => [],
    'status'    => 0,
    'served-by' => 'server6',
};

$json_text = to_json( $out_struct, { utf8 => 1, pretty => 1 } ) ;
my $num = syswrite( $client, $json_text ) ;
print "wrote $num bytes to server\n" ;

这个调试输出是从调用者的角度来看的:
# NO while loop
debug: main::set_up_systems: testing if server6 is alive
debug: main::send_cmd_to_daemon: sending command 'alive' to remote
debug: main::check_if_system_alive: Got: 'FOOBAR'

# with while loop version
debug: main::set_up_systems: testing if server6 is alive
debug: main::send_cmd_to_daemon: sending command 'alive' to remote
debug: main::send_cmd_to_daemon: empty JSON string returned
debug: main::process_cmd: server6: No output sent back.

最佳答案

你说守护进程中的 syswrite 失败了,但你也说它返回了一个正数。这表明它没有失败。 syswrite 出错时返回 undef

守护进程中的 sysread 循环会一直读取,直到遇到 EOF,此时 sysread 返回 0 。我猜测客户端通过关闭整个套接字来发出 EOF 信号,导致客户端中的 sysread 失败(返回 undef 并将 $! 设置为 EBADF(错误的文件描述符))。相反,只使用一半关闭编写器

use Socket qw( SHUT_WR );

shutdown($sock, SHUT_WR)
   or die($!);

关于perl - 多个 sysreads 导致以下 syswrite 在 Perl 中失败,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41463996/

10-12 00:44