原文:http://www.lorui.com/perl-sockets-tcpip.html
套接字允许同一台机器或网络上的程序间进行通讯。它的工作方法很简单:网络上的每台机器通过地址来标识。本教程我们将讨论TCP/IP网络,所以本文所述的网络地址是指IP地址(比如192.168.1.83)。一台机器除了有IP地址外,它还有许多端口,用于同一时段进行多个连接。
一个程序希望从另一个程序那里接收连接,要求操作系统创建一个socket并将其绑定到某个商品。然后,这个程序通过监听这个scoket来接收发送过来的连接。其它程序也建立socket来和它进行通讯。访问者需要指定接收者的IP地址和端口号。如果一切顺利的话,我们马上可以看到,这两个程序使用它们的socket来建立网络通讯。这两个程序可能会交换信息,每个程序都会写入数据到它创建的socket并从该socket中读取数据。
当然。Perl支持socket的底层API。虽然使用API并不坏,但是还有一个非常方便的模块:IO::Socket。它将原始API进行封装,为socket提供更为简单、方便的方法。我们将在本教程中的使用IO::Socket,用来演示两个简单的程序通过socket来通讯。
首先,我们需要创建一个socket。我们将用它来接收连接。下面的代码演示了如何创建一个用于接收的socket。注意,我们需要指定本地机器名和端口来绑定socket。当然,如果这个端口已经被其它程序占用,将会创建失败。同时注意“Listen”参数:这是连接的最大数,通过socket队列来等等你接受并处理它们。我们暂且只接受一个连接(就是说,当一个连接在我们已经建立了其它连接的时候尝试与我们连接,我们将返回一个类似“连接被拒绝”的错误)。最后,那个“Reuse”选项告诉系统允许在这个程序退出后,重新使用该端口。它确保我们的程序在非正常退出或者非法关闭这个socket时,重新运行这个程序还能使用相同的商品来打开新的socket。
#!/usr/bin/perl ############################################## # (c) 2011 LoRui([email protected], www.lorui.com) # ############################################## use strict; use warnings; use IO::Socket; my $socket = new IO::Socket::INET ( LocalHost => 'centos.local.lorui.com', LocalPort => '7070', Proto => 'tcp', Listen => 1, Reuse => 1, ) || die ("无法创建socket:$!\n"); my $new_socket = $socket->accept(); while(<$new_socket>) { print $_; } close $new_socket;
现在,这个socket已经就绪,可以接收请求的连接了。我们使用 accept() 方法来等待一个连接。该方法返回一个新的socket,通过它我们可以和调用程序进行通讯。通过在这个socket上读取/写入可以实现完美的信息交换。这个socket可以像普通的文件句柄那样进行操作。
通讯的另一端更加简单。我们要做的只是建立一个socket,在其中指定远程地址和端口。这样构造函数将在成功连接后返回一个socket对象,然后我们可以马上开始发送一些数据。发送数据的方法很简单:就像给其它文件句柄写入数据一样,我们只要把数据写入到socket即可。
#!/usr/bin/perl ############################################## # (c) 2011 LoRui([email protected], www.lorui.com) # ############################################## use strict; use warnings; use IO::Socket; my $socket = new IO::Socket::INET ( PeerAddr => 'centos.local.lorui.com', PeerPort => '7070', Proto => 'tcp', ) || die ("无法创建socket:$!\n"); print $socket "Hello Perl Sockets, I'm LoRui!\n"; close $socket;
你可以很容易地尝试上面的程序。你需要先运行接收程序再运行发送程序。接收完毕之后,你将在终端看到打印出的那行“Hello Perl Sockets, I'm LoRui!”。如果你在本机测试,可以使用“localhost”作为主机名。
用这种方式进行通讯时,有一个需要考虑的重要问题:两端必须遵循事先商定的方式来进行数据交换。否则的话,很容易在其中一方尝试读取或双方都尝试写入时进入僵持状态。没有办法去猜测另一端是否完成了数据发送,除非它们之间有一些通讯协议,在已传送信息的内容里指定通讯的逻辑单元。在上面的例子中,模块非常简单:调用方发送一条信息然后关闭它这端的连接,接收方只需要在它完成之后读取这些数据。
通常客户端-服务端通讯由调用者(客户端)发送一个请求,接着接收者(服务端)作出回应。为了让服务端知道数据已发送完毕,信息的结尾必须有一些标记(比如两个空白行或一个“END REQUEST”行)。服务端只有在接收到这行的时候才开始作出回应,然后关闭这个socket连接。