Client-Server 2路通信失败(Perl)

时间:2013-04-09 00:05:15

标签: perl sockets network-programming client-server

REQ FLOW:

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

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

CODE:

服务器

$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;
}

现在输出:

enter image description here

期望的输出:

客户端

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$        

SERVER

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$

2 个答案:

答案 0 :(得分:3)

accept用于接受来自listen(服务器)套接字的连接。客户端套接字由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 :(得分:3)

修订版的答案

此代码包含3个脚本:

  1. serverJ.pl这是一个迭代服务器,一次处理一个请求,但继续处理多个请求;
  2. clientJ.pl这是一个简单的客户端,它从Login.txt文件发送登录请求,并回应从服务器接收的内容;和
  3. clientQ.pl向服务器发送QUIT消息。
  4. 我使用旧式文件句柄而不是升级到现代词汇文件句柄,但我改为open的3参数形式。另一个变化是大多数消息都以$$:为前缀,即Perl进程ID。除了INNER:循环的对称性之外,我不会标记OUTER:循环,这需要一个标签。目前尚不清楚send_login()中需要abc()clientJ.pl这两个函数;它们是对原始代码的尊重。

    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;和{{1}和其他小清理。

    server.pl

    use warnings;

    client.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";   
            }
        }
    }
    

    的login.txt

    #!/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;
    }
    

    login_ack.txt

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

    示例输出

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

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