【问题标题】:Merged Socket->recv with perl on Linux在 Linux 上将 Socket->recv 与 perl 合并
【发布时间】:2015-11-21 20:49:50
【问题描述】:

抱歉英语不好,这不是我的母语。

我是 perl 编程的新手,我现在面临一个乏味的问题好几个小时了。

我使用 IO::Socket::INET 编写了一个简单的客户端-服务器。它在 Windows 上可以完美运行,但在 Linux 上就坏了。

在 Linux 上,第一个 recv 获取服务器的通信,因此,第二个正在无休止地等待通信。

在 Windows 上运行命令“perl -version”会给我这个结果:

这是 perl 5, version 20, subversion 2 (v5.20.2) 为 MSWin32-x64-multi-t 线程(带有 1 个注册补丁,请参阅 perl -V for 更详细)

在 Linux 上:

这是 perl 5, version 20, subversion 2 (v5.20.2) 为 x86_64-linux-gnu-thread-multi(有 42 个注册补丁,见 perl -V 更多细节)

这是一个服务器的例子:

use IO::Socket;

my $socket= IO::Socket::INET->new( Proto => "tcp",
                                   LocalPort => 2559,
                                   Listen => SOMAXCONN,
                                   Reuse => 1);

while(1)
{
    print "Waiting for a client\n";
    my $client = $socket->accept();
    $client->send("Hello, please connect yourself");
    $client->send("Username:");
    $client->recv(my $username, 1024);
    $client->send("Password:");
    $client->recv(my $cipheredpassword, 1024);
    $client->send("Thank you, Goodbye.");
    $client->close();
    print "Connection closed\n";
}

这是一个客户的例子:

use IO::Socket;
use Digest::MD5 qw(md5_hex);

my $username = "";
my $password = "";

my $server = IO::Socket::INET->new( Proto => "tcp",
                                    PeerAddr => "localhost",
                                    PeerPort => 2559);

# Pick up both $firstServerMessage and 
# $serverAskUsernameMessage on Linux
$server->recv(my $firstServerMessage, 1024); 
print "$firstServerMessage\n";

# Hangs on Linux
$server->recv(my $serverAskUsernameMessage, 1024);

while($username eq "")
{
    print "$serverAskUsernameMessage\n";
    chomp($username = <STDIN>); 
}

$server->send($username);
$server->recv(my $serverAskPasswordMessage, 1024);

while($password eq "")
{
    print "$serverAskPasswordMessage\n";
    chomp($password = <STDIN>);
}

my $hashedPassword = md5_hex($password);
$server->send($hashedPassword);

$server->recv(my $lastServerMessage, 1024);
print $lastServerMessage;

我知道解决这个问题的简单方法是避免连续出现多个 ->recv,但我也很想知道为什么它不能在 Linux 上运行。

我尝试使用 ->flush 和 ->autoflush(1),但没有成功。

您的帮助和知识将不胜感激, 信用证

【问题讨论】:

  • 你为什么不想只使用低级 Socket?当相同的代码在不同的操作系统上工作不同时,如果你有一堆额外的包装器,就更难理解为什么会这样。也许在这种情况下(在 linux 上)它会更高效,更容易进入低级

标签: linux perl sockets


【解决方案1】:

这个问题与操作系统或语言的选择无关。问题与您使用 TCP 的方式有关。

TCP 流就是这样 - 一个字节流。对于 TCP,您如何写入这些字节并不重要——您可以将 send 分成 50 个 1 字节的块,或者一个 50 字节的块,或者介于两者之间的任何内容。 TCP 流只表示字节,没有消息边界。这与文件 IO 非常相似 - 磁盘上的文件不记得 write 调用之间的区别,只记得传输的字节总数。

所以当你这样做的时候在你的服务器上

$client->send("Hello, please connect yourself");
$client->send("Username:");

它们都可以通过电线合并成一个段,然后一口气到达另一端。这就是为什么任何基于 TCP 的协议都提供某种形式的框架——无论是换行符,还是声明正文大小的消息头,或其他任何形式。接收器可以通过某种方式再次将流分开。

例如,您可能决定使用"\n" 作为消息边界。然后你可以发送使用

$client->send("Hello, please connect yourself\n");
$client->send("Username:\n");

并且接收者可以使用常规的readline 再次读取这些行。

【讨论】:

  • 使用 readline 和 \n 效果很好。但是为什么问题中的代码在windows上运行良好呢?
  • 另外,TCP/IP 堆栈在不同操作系统中的实现方式也可能不同。发送数据包的默认优化也可能有所不同。恕我直言)
  • 出于 LeoNerd 给出的原因,请使用 printread/sysread/readline 用于 TCP。为 UDP 保存 sendrecv
  • 谢谢。有了这些信息,我决定创建两个小子程序。首先,sendTransmission 在传输结束时添加一个小的个性化字符串。其次,readTransmission 用我自己的readline 覆盖分隔符。例如local $/ = "!EOT!"; 它允许我仍然发送包含\n 的多行,并允许我一个接一个地发送多条消息,而无需将它们合并到另一侧。我仍然不知道为什么它可以在 Windows 上运行,可能是 MSMagic。
猜你喜欢
  • 2013-12-13
  • 2015-11-27
  • 2021-12-04
  • 2014-11-22
  • 1970-01-01
  • 1970-01-01
  • 2015-01-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多