2010-09-10 29 views
0

我正在嘗試編寫通過Unix套接字進行雙向通信的Perl代碼。它需要做以下事情:Socket消息掛起,直到線程在Perl中完成

  1. 客戶端代碼發送過來
  2. 服務器代碼讀取請求
  3. 服務器執行任何操作應該發生立即
  4. 服務器套接字的請求創建一個新的線程來這樣做可能需要很長的時間
  5. 服務器發送響應在插座的附加動作
  6. 客戶端接收響應
  7. 新線程在響應發送後繼續工作

我已經獲得了大部分此功能,但有一個問題。步驟1 - 5正常工作,但在步驟6,客戶端無法讀取響應,直到線程退出後。 (儘管立即發送響應或多或少)任何想法我做錯了什麼?

我在Ubuntu Lucid上使用Perl 5.10.1。

下面是一個例子:

客戶端代碼:

#!/usr/bin/perl 
use strict; 
use Socket; 

my $socket_name = 'catsock'; 
my $client_message = "Hello server, this is the client."; 

my $SOCK; 
socket($SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!"; 
connect($SOCK, sockaddr_un($socket_name)) or die "connect: $!"; 

$| = 1, select $_ for select $SOCK; # turn on autoflush 
print $SOCK $client_message; # send the message 
shutdown($SOCK,1); # finished writing 
print "sent:  $client_message\n"; 

my $server_message = do { local $/; <$SOCK> }; # get the response 
print "recieved: $server_message\n\n"; 

Server代碼

#!/usr/bin/perl 
use strict; 
use Socket; 
use threads; 
use threads::shared; 

sub threadfunc 
{ 
    print " waiting 5 seconds in new thread...\n"; 
    sleep(5); 
    print " exiting thread...\n\n"; 
} 

my $server_message = "Hello client, this is the server."; 
my $socket_name = 'catsock'; 

my $SERVER; 
my $CLIENT; 
socket($SERVER, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!"; 
unlink($socket_name); 
bind($SERVER, sockaddr_un($socket_name)) or die "bind: $!"; 
chmod(0660, $socket_name); 
listen($SERVER, SOMAXCONN) or die "listen: $!"; 

print "server started on $socket_name\n\n"; 

while(1) { 
    accept($CLIENT, $SERVER); 

    my $client_message = do { local $/; <$CLIENT> }; # grab entire message 
    print "recieved: $client_message\n"; 

    print "creating new thread...\n"; 
    my $thr = threads->create(\&threadfunc); 
    $thr->detach(); 

    print $CLIENT $server_message; # send the response 
    print "sent: $server_message\n\n"; 

    close $CLIENT; 
} 

當我運行此,下面的事情發生:

  1. 客戶端發送「Hello服務器,這是客戶端。」
  2. 服務器收到客戶端的消息
  3. 服務器創建一個新線程
  4. 服務器發送「你好客戶端,這是服務器。」
  5. 客戶端應該現在收到消息,但它沒有。
  6. 在服務器上的新線程休眠5秒
  7. 線程退出
  8. 現在客戶端收到服務器的消息,即使它在5秒前發出。爲什麼?
+0

設置'$ CLIENT'在接受後自動刷新? – 2010-09-10 02:43:43

+0

是否有可能服務器線程在$ thread-> detach方法中被阻塞,並且因此$ CLIENT上的寫入沒有執行直到工作線程退出? – feroze 2010-09-10 04:05:28

+0

@feroze - 額外的線程保持連接打開5秒(雖然我不確定如何 - 線程有一個'$ CLIENT'的私人副本,並且連接不會關閉,直到所有副本都沒有範圍,也許),但它不會延遲寫入'$ CLIENT'。 – mob 2010-09-10 04:28:35

回答

3
my $server_message = do { local $/; <$SOCK> } 
在客戶端腳本

氣味好笑給我。通過將$/設置爲undef,您要求<$SOCK>語句從句柄$SOCK讀取所有輸入,直到文件結束。在這種情況下,文件結束意味着套接字的另一端關閉了連接。

服務器提供的消息,因爲你可以看到,如果你改變了線之上,以類似

print "Received message: "; 
print while $_ = getc($SOCK); 

(這也將掛起5秒時,輸入流已耗盡,但它會在同時最少顯示輸入中的每個字符)。

這是一個「功能」,TCP套接字連接的一端永遠不會真正知道連接的另一端是否仍然存在。網絡程序員會採取其他的約定 - 在每條消息的開始編碼消息長度,使用特殊標記結束每條消息,實現心跳 - 確定可以安全地從套接字讀取多少輸入(您可能還想看看到select的4參數版本)

對於這個問題,一種可能性是應用約定,即來自服務器的所有消息應以換行符結束。更改服務器腳本說

print $CLIENT $server_message, "\n"; 

,改變客戶端腳本說

my $server_message = do { local $/="\n"; <$SOCK> }; # if you're paranoid about $/ setting 
my $server_message = <$SOCK>;      # if you're not 

,你會得到的結果更接近你的期望。

+0

是的,這是有道理的。謝謝! – plasticinsect 2010-09-10 16:51:08

+1

我通過發送消息開頭的編碼長度解決了這個問題。現在工作正常。 (我不能用換行符終止我的消息,因爲這需要處理可能嵌入換行符的消息) – plasticinsect 2010-09-10 16:57:15

相關問題