perl - 多个系统读取导致以下系统写入在 Perl 中失败

标签 perl sockets

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

即:我更改了我的单个 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 - 多个系统读取导致以下系统写入在 Perl 中失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41463996/

相关文章:

regex - Perl 非贪婪匹配 - "?"字符使用正确吗?

android - 我可以在 PhoneGap 中为 native Android 和 iOS 应用程序使用 Perl 脚本吗?

perl - 使用 Text::CSV 在 perl 中解析 CSV 文件的问题

python-3.x - Python 异步 io 流

c# - 是否可以重置 ServicePointManager?

javascript - Android 中带回调的 Socket.emit - NodeJS

perl - 在继承函数中访问类变量?

perl - perl 中的 mysqldump - 返回值始终为 0

c++ - Fd 大于 1024 时 Select 和 FD_SET 的行为

javascript - "socket.io.js"文件位于哪里?