当前位置: 首页 > 知识库问答 >
问题:

已建立TCP连接但套接字不发送/recv数据

尉迟正平
2023-03-14

我正在编写一个简单的TCP客户端和服务器Perl脚本。到现在为止,我使用wireshark验证了三路TCP握手,并且建立了连接。但当我尝试发送或恢复数据时,什么也不会发生。

1)客户端和服务器之间的主要区别只是服务器增加了一个LISTEN参数,使它能够侦听传入的连接?

2)在recv和显示数据之间是否缺少任何步骤?

3)当程序第一次执行while循环时,难道不应该至少发送硬编码字符串“$response”吗?

#!/usr/bin/perl 
use IO::Socket;
use Getopt::Long;
#$IP_addr = $ARGV[0];
#$tgt_port = $ARG[1];

#netcat client

$port = 9040;
$sock = IO::Socket::INET->new( PeerAddr => 'localhost',
                               PeerPort => $port,
                               LocalPort => 9000,               
                               Proto     => 'tcp')
                               or die "\nunable to bind on localhost : $port...";

while ($sock){
    #Get the clients IP and port number 
        $client_IP = $sock -> peerhost();
    #$client_IP = 'localhost';
        $client_port = $sock -> peerport();
        print "\n Connected to $client_IP $client_port \n";

    #Reading from socket
    $data;  
    $sock ->recv($data, 1024);
    print $data;

    #writing to  socket
    $sock->autoflush(1);
    $response = "response: OK recvd\n" ;
    $sock->send($response);
    shutdown($sock,1);

}

$sock -> close();

服务器如下:

#!/usr/bin/perl 
use IO::Socket;
use Getopt::Long;

#$IP_addr = $ARGV[0];
#$tgt_port = $ARG[1];

#netcat server
$port = 9040;
$sock = IO::Socket::INET->new( Listen    => 1,
                               LocalAddr => 'localhost',
                               LocalPort => $port,
                               Proto     => 'tcp')
                               or die "\nunable to bind on localhost : $port...";

while ($sock){
    print "\nListening on port $port ...\n";
        $sock  = $sock -> accept();
    #Get the clients IP and port number 
        $client_IP = $sock->peerhost();
        $client_port = $sock->peerport();
        print "\n Connected from $client_IP $client_port \n";

    #Reading from socket
    $data ->recv($sock, 1024);
    print $data;

    #writing to  socket
    $sock->autoflush(1);
    $response = "oohlalala" ;
    $sock -> send($response);
    shutdown($sock, 1);

}

$sock -> close();

共有1个答案

陆畅
2023-03-14

来自IO::套接字:

从1.18版开始,所有IO::Socket对象都默认开启自动刷新。早期版本的情况并非如此。

Perl 2.0版于1988年发布。您最有可能使用的是Perl5.x版本。

https://perldoc.perl.org/io/socket.html

这里有一个很好的,简短的,关于插座的基本描述。

您的服务器代码可能隐藏了一些正在发生的事情。如果是这样写的话,就更能说明问题了:

$server_socket = IO::Socket::INET(.....);
...
...
my $client_socket = $server_socket->accept();
...
...
use strict;  
use warnings; 
use 5.020;
use autodie;
use Data::Dumper;

use IO::Socket::INET;
use Socket qw( :crlf );  # "\x0D\x0A" constants CRLF and $CRLF

my $host = 'localhost';
my $port = 15_678;

my $server_socket = IO::Socket::INET->new(
        Listen    => 5,
        LocalPort => $port,
        LocalAddr => $host,
        Proto     => 'tcp',
        ReuseAddr => 1,
        ReusePort => 1
);

say "Server listening on port: $port\n";

while (my $client_socket = $server_socket->accept() ) {

    my $client_ip = $client_socket->peerhost();
    my $client_port = $client_socket->peerport();
    say "Connection from $client_ip:$client_port";

    {
        local $/ = CRLF; # $/ is the input record separator, which is "\n" 
                         #by default. Both <$INFILE> and getline() read up to
                         #and including the input record separator.
        while(my $line = $client_socket->getline) { #Blocks until CRLF is read 
                                                 #from the socket or the other 
                                                 #side closes the socket.
            chomp $line; #chomp() removes input record separator from end of line.
            say "Server received: $line";

            my $response = reverse $line;
            $client_socket->send("$response$CRLF");

            say "Server sent: $response";
        }

    }  #Here $/ is restored to whatever it was before this parenthesized block. 
       #That is what declaring a variable as local does.

    say "-" x 30;

    #Execution arrives here after the client closes the socket:
    $client_socket->shutdown(2);  #Send signals to other side of socket.
                                  #Not necessary in this example because other threads  
                                  #aren't also reading from the socket.
    $client_socket->close();  #Close the filehandle associated with the socket.
}

注意,就像读取文件一样,getline将在接收到eof信号时返回到目前为止读取的所有内容,并且对于while循环的下一次迭代,getline将返回undef,从而终止while循环。

client.pl:

use strict;  
use warnings; 
use 5.020;
use autodie;
use Data::Dumper;

use IO::Socket::INET;
use Socket qw( :crlf ); #\x0D\x0A constants CRLF and $CRLF 

my $port = 15_678;

my @lines = (
    "hello world",
    "goodbye mars",
);

my $sock = IO::Socket::INET->new("localhost:$port");

for my $line(@lines){

    my $server_ip = $sock->peerhost();
    my $server_port = $sock->peerport();
    say "Connected to $server_ip:$server_port";

    $sock->send("$line$CRLF");
    say "Client sent: $line";

    my $response;
    {
        local $/ = CRLF;
        $response = $sock->getline();
        chomp $response;
    }  

    say "Client received: $response";
    say '-' x 30;

}

#Tell the server that no more data is coming from this client:
$sock->shutdown(2);  #Send signals to other side of socket.
$sock->close();  #Close the filehandle associated with the socket.

运行客户端程序两次后...

Server listening on port: 15678

Connection from 127.0.0.1:56085
Server received: hello world
Server sent: dlrow olleh
Server received: goodbye mars
Server sent: sram eybdoog
------------------------------
Connection from 127.0.0.1:56096
Server received: hello world
Server sent: dlrow olleh
Server received: goodbye mars
Server sent: sram eybdoog
------------------------------
$ perl client.pl 
Connected to 127.0.0.1:15678
Client sent: hello world
Client received: dlrow olleh
------------------------------
Connected to 127.0.0.1:15678
Client sent: goodbye mars
Client received: sram eybdoog
------------------------------
$ perl client.pl 
Connected to 127.0.0.1:15678
Client sent: hello world
Client received: dlrow olleh
------------------------------
Connected to 127.0.0.1:15678
Client sent: goodbye mars
Client received: sram eybdoog
------------------------------
$ 

4)shutdown($sock,1)和sleep(1)在此实现中有何不同?让套接字Hibernate可以吗?还是应该使用shutdown($sock,1)向客户机/服务器发出数据已发送的信号?

我不确定shutdown()和sleep()在任何方面是如何相关的。sleep()会在指定的时间内停止代码的执行,而shutdown()会向套接字的另一端发送一些内容。

“关闭套接字”,即调用shutdown(),以标记数据的结束,这是您可以采用的另一种协议。这使得事情变得很简单:一方只是继续以某种read语句从套接字读取,当另一方关闭套接字时,read将返回。这里有一个例子:

use strict;  
use warnings; 
use 5.020;
use autodie;
use Data::Dumper;

use IO::Socket::INET;

my $host = 'localhost';
my $port = 15_678;

my $server_socket = IO::Socket::INET->new(
        Listen    => 5,
        LocalPort => $port,
        LocalAddr => $host,
        Proto     => 'tcp',
        ReuseAddr => 1,
        ReusePort => 1
);

say "Server listening on port: $port\n";

while (my $client_socket = $server_socket->accept() ) {

    my $client_ip = $client_socket->peerhost();
    my $client_port = $client_socket->peerport();
    say "Connection from $client_ip:$client_port";

    my $data;
    {
        local $/ = undef;  #This input record separtor will never be found...
        $data = <$client_socket>;  #...so this reads everything--including newlines--until it gets an eof signal.
    }

    say "Server received: $data";
    my $response = reverse $data;
    $client_socket->send($response);

    $client_socket->shutdown(2); #Doesn't close filehandle -- merely sends signals.
    $client_socket->close();  ##Close the filehandle associated with the socket.

    say "Server sent: $response";
    say "-" x 30;
}
use strict;  
use warnings; 
use 5.020;
use autodie;
use Data::Dumper;

use IO::Socket::INET;

my $port = 15_678;

my @data = (
    "hello \n world",   #Now newlines are in the data
    "goodbye \n mars",
);

for my $data (@data){
    my $sock = IO::Socket::INET->new("localhost:$port");
    my $server_ip = $sock->peerhost();
    my $server_port = $sock->peerport();
    say "Connected to $server_ip:$server_port";

    $sock->send($data);
    say "Client sent: $data";
    $sock->shutdown(1);

    my  $response;
    { 
        local $/ = undef;  #This input record separtor will never be found...
        $response = <$sock>;  ##...so this reads everything--including newlines--until it gets an eof signal.
    }
    $sock->shutdown(0);

    $sock->close();

    say "Client received: $response";
    say '-' x 30;

}

服务器输出:

Server listening on port: 15678

Connection from 127.0.0.1:53139
Server received: hello 
 world
Server sent: dlrow 
 olleh
------------------------------
Connection from 127.0.0.1:53140
Server received: goodbye 
 mars
Server sent: sram 
 eybdoog
------------------------------

客户端输出:

Connected to 127.0.0.1:15678
Client sent: hello 
 world
Client received: dlrow 
 olleh
------------------------------
Connected to 127.0.0.1:15678
Client sent: goodbye 
 mars
Client received: sram 
 eybdoog
------------------------------
 类似资料:
  • 我的问题是为什么接收端没有得到发送的数据包? 注意:我的目标是在建立的连接上发送带有错误校验和的TCP数据包,并由不scapy的TCP服务器接收,提前谢谢!!

  • OpenSSL版本:OpenSSL 1.1.0g 要创建证书,我运行以下命令: openssl genrsa-des3-out server.key 2048 openssl x509-请求-天数365-输入server.csr-签名密钥server.key-输出server.crt ...新的TLSV1.2密码是AES256-GCM-SHA384 服务器公钥为2048位 支持安全重新协商 ...

  • 问题内容: 我的GO版本是1.1.1 连接关闭后服务器收到的消息,但设置了NoDelay。 有什么不对 问题答案: 您的代码似乎没有任何根本性的错误,因此我猜错误是在服务器端。 如果在端口5432上创建本地TCP服务器,则可以对此进行测试。 尝试运行下面的服务器代码,然后针对它测试客户端代码。它只是将所有接收到的数据回显到stdout。 按下回车键后,您应该看到发送给客户端的每一行都已打印(没有换

  • 我使用Android应用程序通过TCP套接字与同一局域网上的PC java应用程序进行通信、发送和接收消息。下面是我在android中使用的Asynctask的代码,用于发送消息并从PC接收回复: } 我在onPostExcecute中的祝酒词中显示PC的回复。 Android通过BufferedWriter发送消息,而PC上的java应用程序在BufferedReader中接收消息。 PC在收到

  • 在套接字最终接受另一端消失的情况下,什么指定了这个超时?是操作系统(Ubuntu 11.04),还是来自TCP/IP规范,还是套接字配置选项?

  • 我使用tcpdump从客户端捕获数据包,每个数据包都是分段到最大1448字节的数据,但是head只有5字节,为什么我可以成功读取head,但是下面的data recv()操作又会返回ee?当数据已经到达recv缓冲区时,recv是否可能返回EAGAIN?在我看来,我可以读取前5个字节,所以必须有更多的数据在recv缓冲区读取。 可能与TCP/IP协议栈中的组装过程有关。代码如下所示,每个pdu r