【问题标题】:Client-Server 2 way communication unsuccessful (Perl)客户端-服务器 2 路通信不成功 (Perl)
【发布时间】:2013-03-31 05:53:11
【问题描述】:

请求流:

____________      LOGIN           ___________
|          |------------------->>|          |
|  CLIENT  |      LOGIN ACK      |  SERVER  |
|__________|<<------------------ |__________|

客户端和服务器发送他们打开的文件的内容。

代码:

服务器

$socket = new IO::Socket::INET ( LocalHost => '127.0.0.1', LocalPort => '0155', Proto => 'tcp', Listen => 1, Reuse => 1 ) or die "Oops: $! \n";

print "Waiting for the Client.\n";

while($clientsocket = $socket->accept()){


print   "Connected from : ", $clientsocket->peerhost();
print   ", Port : ", $clientsocket->peerport(), "\n";

while(<$clientsocket>){
    if($_ ne "\$END\$\n"){
            print"Message received from Client : $_";
        print $clientsocket $_;
}
    else{
    print"\$END\$\n";  
}   
} last;
}

login_ack();
sub login_ack{

# Some code removed for the purpose of posting                          
open (LOGINACK, "login_ack.txt") || die "Cannot open login acknowledgment file $!\n";
my @loginack=<LOGINACK>;
close LOGINACK;                         
open(LOG, ">>logfile.txt");

foreach $loginack(@loginack) {
    if($loginack ne "\$END\$\n"){
        print $clientsocket $loginack;
        print LOG $loginack;
        print $loginack;
    }
    else{
        print"\$END\$\n";   
    }
}

客户

$socket = new IO::Socket::INET ( PeerHost => '127.0.0.1', PeerPort => '0155', Proto => 'tcp', Reuse => 1) or die "$!\n";

print "Connected to the Server.\n";

send_login();
sub send_login{

# Some code removed for the purpose of posting                              
open (LOGIN, "Login.txt") || die "Cannot open login file $!\n";
my @login=<LOGIN>;
close LOGIN;                            
open(LOG, ">>logfile.txt");

foreach $login(@login) {
     if($login ne "\$END\$\n"){
        print $socket $login;
        print LOG $login;
        print $login;
    }
    else{
    print"\$END\$\n"; 
    }
}
} 
LOGINACK : while(<$socket>){
        print"Message received from Server : $_";
        print $socket $_; 
        last LOGINACK;
}

目前的输出:

期望的输出:

客户

Connected to the Server.
this is Login
SAS4
50
SAS_ACTION LOGIN
LOGIN bss
PASSWORD cleint
$END$
Message received from Server : this is Login Ack
Message received from Server : SAS4
Message received from Server : 61
Message received from Server : SAS_ACTION LOGIN_ACK
Message received from Server : ACK_STATUS 0
Message received from Server : ACK_MESSAGE Logged In
Message received from Server : $END$        

服务器

Waiting for the Client.
Connected from : 127.0.0.1, Port : 1862
Message received from Client : this is Login
Message received from Client : SAS4
Message received from Client : 50
Message received from Client : SAS_ACTION LOGIN
Message received from Client : LOGIN bss
Message received from Client : PASSWORD cleint
Message received from Client : $END$
this is Login Ack
SAS4
61
SAS_ACTION LOGIN_ACK
ACK_STATUS 0
ACK_MESSAGE Logged In
$END$

【问题讨论】:

  • 顺便说一句,while (...) { ...; last } 是一种愚蠢的写法if (...) { ... }
  • IO::Socket::INET->new 返回$@ 中的错误,而不是$!。
  • 您是否真的在特权端口0155 上运行您的服务器(八进制;端口号是十进制的 109)?所以你以root 运行? Ick,同样糟糕!特别是在开发过程中,请使用非特权端口(大于 1023 的数字),除非必须,否则不要以 root 身份运行。

标签: perl sockets network-programming client-server


【解决方案1】:

accept 用于接受来自listening(服务器)套接字的连接。客户端套接字由connect 连接,new 为您调用。

所以,

while (my $serversocket = $socket->accept()) {
   print "\nThe line after the while loop is printing\n\n";
   while (<$serversocket>) {
      print "Message received from Server : $_";
      print $serversocket $_;
   }
}

应该是

while (<$socket>) {
   print "Message received from Server : $_";
}

现在客户端的输出是

Connected to the Server.
SAS4
50
SAS_ACTION LOGIN
LOGIN bss
PASSWORD cleint
$END$

The line before the while loop is printing.

Message received from Server : SAS4
Message received from Server : 50
Message received from Server : SAS_ACTION LOGIN
Message received from Server : LOGIN bss
Message received from Server : PASSWORD cleint
Message received from Server : $END$

然后它等待来自服务器的更多输入。服务器从不调用login_ack,因为它正在等待来自客户端的更多输入。你想什么时候打电话给login_ack?

【讨论】:

  • 代码仍然没有产生正确的输出。 1.在客户端,cmd读取:来自服务器的消息:{客户端正在读取的文件(登录)}。 2.在服务器中,cmd读取:来自客户端的消息:{客户端正在读取的文件(登录)}->这是正确的,但自相矛盾(第1点)。 3.客户端中的代码在循环中一遍遍地运行。
  • 我的矛盾:我们没有在客户端输出正确的东西。我们不是从服务器输出 i/p,而是从客户端本身输出 i/p。请看一看。 TY
  • 我们需要在收到客户端的“$END$”后立即调用它。
  • 好的,所以你需要添加一个检查来查看收到的行是否为$END$。
  • 没有这样的if。而且您绝对不应该发布添加代码! (因为我已经更新了它)发布的是最小的可运行代码来演示问题。这正是应该发布的内容。就像我刚才说的,唯一缺少的是检查$_ 是否包含"$END$\n" 你是说你不知道如何检查变量包含什么?
【解决方案2】:

答案修订版

此代码包含 3 个脚本:

  1. serverJ.pl 是一个迭代服务器,一次处理一个请求,但继续处理多个请求;
  2. clientJ.pl 是一个简单的客户端,它从Login.txt 文件发送登录请求并回显它从服务器接收到的内容;和
  3. clientQ.pl 向服务器发送 QUIT 消息。

我保留了使用老式文件句柄而不是升级到现代词法文件句柄的代码,但我更改为 open 的 3 参数形式。另一个变化是大多数消息都以$$: 为前缀,即 Perl 进程 ID。除了与确实需要标签的OUTER: 循环对称之外,我不会标记INNER: 循环。不清楚clientJ.pl中需要send_login()和abc()这两个函数;它们遵从原始代码。

serverJ.pl

#!/usr/bin/env perl
use strict;
use warnings;
use IO::Socket::INET;

my $socket = new IO::Socket::INET (
    LocalHost => '127.0.0.1',
    LocalPort => '1055',
    Proto => 'tcp',
    Listen => 1,
    Reuse => 1
) or die "Oops: $! \n";

print "$$: waiting for clients.\n";
server_loop();
$socket->close();

sub server_loop
{
OUTER:
    while (my $clientsocket = $socket->accept())
    {
        print "$$: connected from : ", $clientsocket->peerhost();
        print ", port : ", $clientsocket->peerport(), "\n";

INNER:
        while (<$clientsocket>)
        {
            print"$$: Message received from Client : $_";
            last INNER if ($_ eq "\$END\$\n");
            last OUTER if ($_ eq "\$QUIT\$\n");
            print $clientsocket $_;
        }

        my $login_ack = "login_ack.txt";
        open LOGINACK, '<', $login_ack or die "Cannot open login acknowledgment file $login_ack ($!)\n";
        my @loginack = <LOGINACK>;
        close LOGINACK;

        my $logfile = "logfile.txt";
        open LOG, ">>", $logfile or die "$$: cannot open $logfile ($!)\n";

        foreach my $loginack (@loginack)
        {
            last if ($loginack eq "\$END\$\n");
            print $clientsocket $loginack;
            print LOG "$$: $loginack";
            print "$$: server to client: $loginack";
        }

        close LOG;
        close $clientsocket;
    }
}

clientJ.pl

#!/usr/bin/env perl
use strict;
use warnings;
use IO::Socket::INET;

my $socket = new IO::Socket::INET (
    PeerHost => '127.0.0.1',
    PeerPort => '1055',
    Proto    => 'tcp',
    Reuse    => 1
) or die "$!\n";

print "$$: connected to the server.\n";

send_login();
abc();
$socket->close();

sub send_login
{
    my $login_txt = "Login.txt";
    open LOGIN, '<', $login_txt or die "Cannot open $login_txt $!\n";
    my @login = <LOGIN>;
    close LOGIN;

    my $logfile = "logfile.txt";
    open LOG, '>>', $logfile or die "Cannot open $logfile ($!)\n";

    foreach my $login (@login)
    {
        print $socket $login;
        print LOG "$$: $login";
        print "$$: sent to server: $login";
        last if ($login eq "\$END\$\n");
    }
    close LOG;
}

sub abc
{
    while (<$socket>)
    {
        print"$$: Message received from Server: $_";
    }
}

clientQ.pl

#!/usr/bin/env perl
use strict;
use warnings;
use IO::Socket::INET;

my $socket = new IO::Socket::INET(
  PeerHost => '127.0.0.1',
  PeerPort => '1055',
  Proto    => 'tcp',
  Reuse    => 1
) or die "$!\n";

print $socket "\$QUIT\$\n";

$socket->close();

样本日志

$ perl serverJ.pl 2>&1 &
[1] 87610
$ 87610: waiting for clients.

$ perl clientJ.pl | so
87610: connected from : 127.0.0.1, port : 57143
87610: Message received from Client : SAS4
87610: Message received from Client : 50
87610: Message received from Client : SAS_ACTION LOGIN
87610: Message received from Client : LOGIN bss
87610: Message received from Client : PASSWORD cleint
87610: Message received from Client : $END$
87610: server to client: SAS4
87610: server to client: 61
87610: server to client: SAS_ACTION LOGIN_ACK
87610: server to client: ACK_STATUS 0
87610: server to client: ACK_MESSAGE Logged In
87614: connected to the server.
87614: sent to server: SAS4
87614: sent to server: 50
87614: sent to server: SAS_ACTION LOGIN
87614: sent to server: LOGIN bss
87614: sent to server: PASSWORD cleint
87614: sent to server: $END$
87614: Message received from Server: SAS4
87614: Message received from Server: 50
87614: Message received from Server: SAS_ACTION LOGIN
87614: Message received from Server: LOGIN bss
87614: Message received from Server: PASSWORD cleint
87614: Message received from Server: SAS4
87614: Message received from Server: 61
87614: Message received from Server: SAS_ACTION LOGIN_ACK
87614: Message received from Server: ACK_STATUS 0
87614: Message received from Server: ACK_MESSAGE Logged In
$ perl clientJ.pl | so
87610: connected from : 127.0.0.1, port : 57144
87610: Message received from Client : SAS4
87610: Message received from Client : 50
87610: Message received from Client : SAS_ACTION LOGIN
87610: Message received from Client : LOGIN bss
87610: Message received from Client : PASSWORD cleint
87610: Message received from Client : $END$
87610: server to client: SAS4
87610: server to client: 61
87610: server to client: SAS_ACTION LOGIN_ACK
87610: server to client: ACK_STATUS 0
87610: server to client: ACK_MESSAGE Logged In
87617: connected to the server.
87617: sent to server: SAS4
87617: sent to server: 50
87617: sent to server: SAS_ACTION LOGIN
87617: sent to server: LOGIN bss
87617: sent to server: PASSWORD cleint
87617: sent to server: $END$
87617: Message received from Server: SAS4
87617: Message received from Server: 50
87617: Message received from Server: SAS_ACTION LOGIN
87617: Message received from Server: LOGIN bss
87617: Message received from Server: PASSWORD cleint
87617: Message received from Server: SAS4
87617: Message received from Server: 61
87617: Message received from Server: SAS_ACTION LOGIN_ACK
87617: Message received from Server: ACK_STATUS 0
87617: Message received from Server: ACK_MESSAGE Logged In
$ perl clientQ.pl | so
87610: connected from : 127.0.0.1, port : 57147
87610: Message received from Client : $QUIT$
[1]+  Done                    perl serverJ.pl 2>&1
$ 

后循环代码执行

在服务器脚本中,在发送确认时,foreach my $loginack (@loginack) 循环之后的代码不会被执行。我认为这是因为代码无法跳出循环。该如何处理?

serverJ.pl中的代码略有修改:

        foreach my $loginack (@loginack)
        {
            last if ($loginack eq "\$END\$\n");
            print $clientsocket $loginack;
            print LOG "$$: $loginack";
            print "$$: server to client: $loginack";
        }

        print "$$: End of Loop\n";
        close LOG;
        close $clientsocket;
        print "$$: After close\n";

示例运行:

$ perl serverJ.pl &
[1] 6913
$ 6913: waiting for clients.

$ perl clientJ.pl
6915: connected to the server.
6913: connected from : 127.0.0.1, port : 52454
6915: sent to server: This is the file Login.txt
6913: Message received from Client : This is the file Login.txt
6915: sent to server: It contains more than 1 line
6915: sent to server: It also contains this one
6915: sent to server: $END$
6913: Message received from Client : It contains more than 1 line
6913: Message received from Client : It also contains this one
6913: Message received from Client : $END$
6915: Message received from Server: This is the file Login.txt
6915: Message received from Server: It contains more than 1 line
6915: Message received from Server: It also contains this one
6913: server to client: This is the file login_ack.txt
6913: server to client: As with the other file, it contains multiple lines
6913: server to client: Several of them.
6913: server to client: This is one of them.
6915: Message received from Server: This is the file login_ack.txt
6913: End of Loop
6915: Message received from Server: As with the other file, it contains multiple lines
6915: Message received from Server: Several of them.
6915: Message received from Server: This is one of them.
6913: After close
$ perl clientQ.pl
6913: connected from : 127.0.0.1, port : 52455
6913: Message received from Client : $QUIT$
[1]+  Done                    perl serverJ.pl
$

答案原版

这段代码似乎对我有用(对于“作品”的松散定义)。它与您的代码密切相关,但使用套接字 1055 而不是 0155(我在 Unix 上运行;我必须是 root 才能使用小于 1024 的端口号),并使用 use strict; 和 use warnings; 和其他次要的清理。

server.pl

#!/usr/bin/env perl
use strict;
use warnings;
use IO::Socket::INET;

my $socket = new IO::Socket::INET( LocalHost => '127.0.0.1', LocalPort => '1055', Proto => 'tcp', Listen => 1, Reuse => 1 ) or die "Oops: $! \n";

print "Waiting for the Client.\n";

my $clientsocket;

while ($clientsocket = $socket->accept())
{
    print   "Connected from : ", $clientsocket->peerhost();
    print   ", Port : ", $clientsocket->peerport(), "\n";

    while (<$clientsocket>)
    {
        if ($_ ne "\$END\$\n")
        {
            print"Message received from Client : $_";
            print $clientsocket $_;
        }
        else
        {
            print"\$END\$\n";  
        }   
    }
    last;
}

login_ack();

sub login_ack
{
    # Some code removed for the purpose of posting                          
    open (LOGINACK, "login_ack.txt") || die "Cannot open login acknowledgment file $!\n";
    my @loginack=<LOGINACK>;
    close LOGINACK;                         
    open(LOG, ">>logfile.txt");

    foreach my $loginack (@loginack)
    {
        if ($loginack ne "\$END\$\n")
        {
            print $clientsocket $loginack;
            print LOG $loginack;
            print $loginack;
        }
        else
        {
            print"\$END\$\n";   
        }
    }
}

client.pl

#!/usr/bin/env perl
use strict;
use warnings;
use IO::Socket::INET;

my $socket = new IO::Socket::INET ( PeerHost => '127.0.0.1', PeerPort => '1055', Proto => 'tcp', Reuse => 1) or die "$!\n";

print "Connected to the Server.\n";

send_login();

sub send_login
{
# Some code removed for the purpose of posting                              
    open (LOGIN, "Login.txt") || die "Cannot open login file $!\n";
    my @login=<LOGIN>;
    close LOGIN;                            
    open(LOG, ">>logfile.txt");

    foreach my $login (@login)
    {
        if ($login ne "\$END\$\n")
        {
            print $socket $login;
            print LOG $login;
            print $login;
        }
        else
        {
            print"\$END\$\n"; 
        }
    }
} 

LOGINACK:
while (<$socket>)
{
    print"Message received from Server : $_";
    print $socket $_; 
    last LOGINACK;
}

登录.txt

This is the content of the Login.txt file.
$END$

login_ack.txt

This is the contents of the login_ack.txt file.
It has two lines of text plus the $END$ line.
$END$

样本输出

$ perl server.pl &
[1] 74838
$ Waiting for the Client.

$ perl client.pl | so
Connected from : 127.0.0.1, Port : 51109
Message received from Client : This is the content of the Login.txt file.
Message received from Client : This is the content of the Login.txt file.
Connected to the Server.
This is the content of the Login.txt file.
$END$
Message received from Server : This is the content of the Login.txt file.
This is the contents of the login_ack.txt file.
It has two lines of text plus the $END$ line.
$END$
[1]+  Done                    perl server.pl
$ 

服务器在响应单个请求后终止。

【讨论】:

  • 嗨,乔恩,感谢您查看代码,但是您的代码变体产生的输出与我提供的完全相同。就像 100 件。另外,关于使用严格,使用警告,我在实际代码中使用了它,为了在这里发布它,你教我的SSCCE东西,我把它删除了。
  • SSCCE 已完成; use 语句是完整性的一部分。人们倾向于评论use strict 和use warnings 的缺席,因此最好将它们包括在内以避免麻烦。 shebang 线可能是可选的;我会包括它 - 如图所示。
  • 在服务器脚本中,在发送确认时,'foreach my $loginack (@loginack)' 循环之后的代码不会被执行。我认为这是因为代码无法跳出循环。该如何处理?
  • 我已经测试了 serverJ.pl 脚本的修改版本并且循环终止正常。在编辑后的答案中查看跟踪信息。
  • 我不知道你在做什么,但代码对我来说工作正常。如果您期望serverJ.pl 是一个单周期程序(因此它在第一个客户端之后退出),那么您需要重新阅读我提供的注释(参见修订答案中的第 1 点),并使用@987654352 @ 使其终止。或者修改代码,使其在第一次响应后停止,但这使它成为一个毫无意义的服务器。
猜你喜欢
  • 2013-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-26
  • 2011-09-05
  • 2012-03-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多