2017-01-04 22 views
3

我有一個守護進程接受連接,通過單個sysread讀取JSON請求,處理它,並通過syswrite發回一個JSON響應。這工作正常。但是我希望在部分閱讀的情況下使閱讀更健壯。所以我使用了常見的技術,我看到人們使用循環直到讀取0字節,同時指定將輸入附加到緩衝區的末尾。當我做這個簡單的循環時,以下syswrite聲明它寫出X字節,但調用者認爲它讀回0字節。看來在套接字上執行一個以上的sysread會導致該套接字將來的syswrite失敗。多個sysread導致下面的syswrite在Perl中失敗

即:我改變了我的單sysread執行要做到這一點,而不是:

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

我已經證實,在每一種情況下,我收到請求是相同的,那我送的反應是相同。唯一改變的是額外的讀取0字節的sysread,以表明我已經讀取了所有可用的東西。

我附上了一個簡單的簡化程序,說明了這一點。 有人可以向我解釋這裏發生了什麼? 我也試過特別告訴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. 
+0

您說'syswrite'失敗,但您沒有指定它返回的錯誤。請提供'syswrite'('0'?'undef'?)返回的值以及任何可用的錯誤消息(如果返回undef,則返回'$!')。 – ikegami

+0

您的循環讀取直到遇到EOF(sysread == 0)。客戶如何告知EOF?是否關閉整個套接字而不是關閉寫入器一半? – ikegami

+0

@ikegami:syswrite沒有收到錯誤,它返回了它寫入的字節數,但是它們沒有出現在調用程序中。然而,你的第二個問題讓我想到了答案。我現在正在執行關機($ sock,1)來關閉來自調用者的寫入並修復它。謝謝! –

回答

4

你說的守護進程的syswrite失敗了,但你也可以說,它返回一個正數。這表明它沒有失敗。 syswrite返回undef出錯。

守護程序中的sysread循環讀取,直到它遇到EOF,這是sysread返回0時。我猜測客戶端通過關閉整個套接字來發信號通知EOF,導致客戶端中的sysread失敗(返回undef,$!設置爲EBADF(錯誤文件描述符))。相反,只關閉使用的一半寫入器

use Socket qw(SHUT_WR); 

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