【问题标题】:Basic network chat app in PerlPerl 中的基本网络聊天应用程序
【发布时间】:2015-09-24 04:11:50
【问题描述】:

出于学习目的,我正在尝试用 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;

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;

【问题讨论】:

  • 这实际上对您的经验水平非常有利。我要立即说的唯一一件事是,您应该确保您的学习材料是最新的。命令行或shebang线上的-w早已被上级use warnings取代。您还应该在while 内的服务器代码中声明$child,紧接在if 之前

标签: perl network-programming


【解决方案1】:

单个客户端到单个服务器并不太困难 - 您正在使用代码执行的操作 - 有效地创建了一对一的关系。您的forked 服务器正在专门与您的客户对话。

要让信息在多个客户端之间传播(通过服务器),您将不得不变得更复杂一些 - 因为您有单独的进程,这些进程现在需要相互通信。这是一个足够大的问题,perl 文档中有一整段关于它:perlipc

这实际上会大大增加您的代码的复杂性,因为您的通信正在转向一对多的关系,并且它们都将异步发生。

基于套接字的通信是进程间通信 (IPC) 的一种形式,您已经在这样做了。但是你在这里的“问题”是你正在从 1 到 1 个通信移动到 1 到多个通信。您需要能够广播,而这种通信模式并不能很好地支持这一点。

我的建议是查看IO::Pipe - 我在这里有一些示例代码:How to extract data from Parallel::ForkManager in perl

然后使用IO::Selectcan_read 异步判断管道上是否有任何数据进入。你可能需要一组管道——每个客户端一个——否则你可能会得到并发的东西重叠。

例如:

(来自IO::Pipe文档页面:

 my $pipe = IO::Pipe->new();
if($pid = fork()) { # Parent
        $pipe->reader();
        while(<$pipe>) {
        ...
        }
    }
    elsif(defined $pid) { # Child
        $pipe->writer();
        print $pipe ...
    }

不幸的是,这里有一个小问题 - 您的管道将由分叉过程创建,但这反过来意味着您需要弄清楚如何处理管道数组并检查它们是否可读。

这意味着您不能再围绕接受套接字进行while 循环-这会阻塞,因此您将排队等待另一个客户端连接(这实际上不是您想要的)。所以你还需要再次使用select 来检查是否有东西准备好首先accept

【讨论】:

  • 感谢您的回答,说实话,我仍然对如何实现这一点感到非常困惑。我是否仍然可以使用每个连接仅一个进程来执行此操作,还是需要再次分叉?我如何将从一个连接收到的数据包传递给所有打开的连接?不过我有个主意;假设我让每个分叉的进程都在监听数据,一旦收到一些数据,我可以使用包含所有活动连接的所有进程之间共享的列表(尽管我不确定如何执行此操作,或者是否可能),并且只是遍历所有连接并像这样发送它。想法?
  • 比听起来更难,因为通过 fork 共享列表很难。线程可以。
  • 我得出了同样的结论,你能否稍微详细说明一下我如何在这里使用线程?
  • 太多,无法放入 cmets。但是threads::shared 可以让你共享一个数据结构。
  • 不容易——但我可以给你举个例子:perldoc.perl.org/IO/Select.html 滚动到底部,你可以看到一个基于套接字的场景。
猜你喜欢
  • 1970-01-01
  • 2019-01-05
  • 1970-01-01
  • 1970-01-01
  • 2014-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-28
相关资源
最近更新 更多