Dem*_*ess 6 perl network-programming
我正在尝试在Perl中编写一个基本的网络聊天应用程序用于学习目的.我目前有一个服务器和客户端程序,几乎按照我的要求运行.多个客户端可以连接到服务器并向其发送消息.但是,我不确定如何将消息从一个客户端发送到另一个客户端,并希望在这里推进正确的方向.这是我到目前为止的代码,想法?
注意:这是我第一次尝试使用网络或使用Perl进行正确的项目,因此任何其他关于如何编写它的指导也将受到赞赏.
chat_server.pl
#!/usr/bin/perl -w
# chat_server.pl
use strict;
use IO::Socket::INET;
my $port = shift or die "Port required!\n";
my $socket = IO::Socket::INET->new(
LocalPort => $port,
Proto => 'tcp',
Listen => SOMAXCONN
) or die "Can't create socket: $!!\n";
my $child;
print "Listening for clients on $port...\n";
REQUEST:
while(my $client = $socket->accept) {
my $addr = gethostbyaddr($client->peeraddr, AF_INET);
my $port = $client->peerport;
if($child = fork) {
print "New connection from $addr:$port\n";
close $client;
next REQUEST;
} die "fork failed!\n" unless defined $child;
while (<$client>) {
print "[$addr:$port] says: $_";
print $client "[$addr:$port] says: $_";
}
}
close $socket;
Run Code Online (Sandbox Code Playgroud)
chat_client.pl
#!/usr/bin/perl -w
# chat_client.pl
use strict;
use IO::Socket::INET;
my $port = shift or die "No port\n";
my $server = shift or die "No server\n";
my $client_socket = IO::Socket::INET->new(
PeerPort => $port,
PeerAddr => $server,
Proto => 'tcp'
) or die "Can't create send socket: $!!\n";
my $child;
if($child = fork) {
while(1) {
sleep(1);
print scalar <$client_socket>;
}
}
die "fork failed!\n" unless defined $child;
print "Connected to $server:$port!\n";
do {
print "> ";
print $client_socket $_ if defined $_;
} while(<STDIN>);
print "Closing connection";
close $client_socket;
Run Code Online (Sandbox Code Playgroud)
单个客户端到单个服务器并不太困难 - 您正在使用代码所做的事情 - 有效地 - 创建一对一的关系。您的forked 服务器正在专门与您的客户端通信。
为了让信息在多个客户端之间传播(通过服务器),您将不得不变得更加复杂 - 因为您有单独的进程,这些进程现在需要相互通信。这是一个足够大的问题,perl 文档有一整段关于它的内容:perlipc。
这实际上会大大增加代码的复杂性,因为您将在通信上转向一对多关系,并且它们都将异步发生。
基于套接字的通信是进程间通信 (IPC) 的一种形式,您已经在这样做了。但这里的“陷阱”是,您正在从 1 到 1 个通信变为 1 到多个通信。您需要能够广播,而这种通信模式对此的支持不是特别好。
我建议看看IO::Pipe- 我这里有一些示例代码:How to extract data from Parallel::ForkManager in perl
然后使用IO::Select和can_read异步确定管道中是否有任何数据进入。您可能需要一组管道 - 每个客户端一个 - 否则您可能会遇到并发的东西重叠。
例如:
(来自IO::Pipe文档页面:
my $pipe = IO::Pipe->new();
if($pid = fork()) { # Parent
$pipe->reader();
while(<$pipe>) {
...
}
}
elsif(defined $pid) { # Child
$pipe->writer();
print $pipe ...
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,这里有一个小问题 - 您的管道将由分叉过程创建,但这反过来意味着您需要弄清楚如何处理管道数组并检查它们是否可读。
这意味着您不能while再坐在接受套接字的循环中 - 这会阻塞,因此您将在另一个客户端连接之前将消息排队(这实际上不会是您想要的)。所以你还需要select再次使用来检查是否有东西准备好accept。