Perl套接字编程



什么是套接字?

套接字是伯克利UNIX中的一种机制,用于在不同的进程之间创建虚拟双向连接。后来它被移植到所有已知的操作系统上,从而实现了在运行不同操作系统软件的不同地理位置的系统之间进行通信。如果没有套接字,大多数系统之间的网络通信将永远无法实现。

仔细观察;网络上的典型计算机系统会根据其上运行的各种应用程序的要求接收和发送信息。此信息被路由到系统,因为为此系统指定了一个唯一的IP地址。在系统上,此信息将提供给在不同端口上侦听的相关应用程序。例如,互联网浏览器在端口80上侦听从Web服务器接收到的信息。我们还可以编写自定义应用程序,这些应用程序可以在特定端口号上侦听和发送/接收信息。

现在,让我们总结一下,套接字是一个IP地址和一个端口,它可以连接到网络上发送和接收数据。

为了解释上述套接字概念,我们将以使用Perl的客户端-服务器编程为例。为了完成客户端服务器架构,我们将必须执行以下步骤:

创建服务器

  • 使用socket调用创建套接字。

  • 使用bind调用将套接字绑定到端口地址。

  • 使用listen调用侦听端口地址上的套接字。

  • 使用accept调用接受客户端连接。

创建客户端

  • 使用socket调用创建套接字。

  • 使用connect调用连接(套接字)到服务器。

下图显示了客户端和服务器用于相互通信的调用的完整序列:

Perl Socket

服务器端套接字调用

socket()调用

socket()调用是建立网络连接的第一步,即创建套接字。此调用的语法如下:

socket( SOCKET, DOMAIN, TYPE, PROTOCOL );

上述调用创建一个SOCKET,其他三个参数是整数,对于TCP/IP连接应具有以下值。

  • DOMAIN应为PF_INET。在您的计算机上可能是2。

  • TYPE对于TCP/IP连接应为SOCK_STREAM。

  • PROTOCOL

    应为(getprotobyname('tcp'))[2]。它是通过套接字使用的特定协议,例如TCP。

因此,服务器发出的socket函数调用将类似于:

use Socket     # This defines PF_INET and SOCK_STREAM

socket(SOCKET,PF_INET,SOCK_STREAM,(getprotobyname('tcp'))[2]);

bind()调用

由socket()调用创建的套接字在绑定到主机名和端口号之前是无用的。服务器使用以下bind()函数来指定将接受客户端连接的端口。

bind( SOCKET, ADDRESS );

这里SOCKET是由socket()调用返回的描述符,ADDRESS是套接字地址(对于TCP/IP),包含三个元素:

  • 地址族(对于TCP/IP,那是AF_INET,在您的系统上可能是2)

  • 端口号(例如21)

  • 计算机的互联网地址(例如10.12.12.168)

由于bind()由不需要知道自身地址的服务器使用,因此参数列表如下:

use Socket        # This defines PF_INET and SOCK_STREAM

$port = 12345;    # The unique port used by the sever to listen requests
$server_ip_address = "10.12.12.168";
bind( SOCKET, pack_sockaddr_in($port, inet_aton($server_ip_address)))
   or die "Can't bind to port $port! \n";

or die子句非常重要,因为如果服务器在没有未完成连接的情况下终止,则除非使用setsockopt()函数使用选项SO_REUSEADDR,否则端口将不会立即可重用。这里使用pack_sockaddr_in()函数将端口和IP地址打包成二进制格式。

listen()调用

如果这是一个服务器程序,则需要在指定的端口上发出对listen()的调用以进行侦听,即等待传入请求。此调用的语法如下:

listen( SOCKET, QUEUESIZE );

上述调用使用socket()调用返回的SOCKET描述符,而QUEUESIZE是允许同时存在的最大未完成连接请求数。

accept()调用

如果这是一个服务器程序,则需要发出对access()函数的调用以接受传入的连接。此调用的语法如下:

accept( NEW_SOCKET, SOCKET );

accept调用接收socket()函数返回的SOCKET描述符,并且成功完成时,将返回一个新的套接字描述符NEW_SOCKET,用于客户端和服务器之间未来的所有通信。如果access()调用失败,则返回我们在初始阶段使用的Socket模块中定义的FLASE。

通常,accept()用于无限循环中。一旦一个连接到达,服务器要么创建一个子进程来处理它,要么自己处理它,然后返回去侦听更多连接。

while(1) {
   accept( NEW_SOCKET, SOCKT );
   .......
}

现在所有与服务器相关的调用都已完成,让我们看看客户端将需要的调用。

客户端套接字调用

connect()调用

如果您要准备客户端程序,那么首先您将使用socket()调用创建套接字,然后您必须使用connect()调用连接到服务器。您已经看到了socket()调用的语法,它将与服务器socket()调用类似,但这是connect()调用的语法:

connect( SOCKET, ADDRESS );

这里SCOKET是由客户端发出的socket()调用返回的套接字描述符,而ADDRESS是一个与bind调用类似的套接字地址,只是它包含远程服务器的IP地址。

$port = 21;    # For example, the ftp port
$server_ip_address = "10.12.12.168";
connect( SOCKET, pack_sockaddr_in($port, inet_aton($server_ip_address)))
   or die "Can't connect to port $port! \n";

如果您成功连接到服务器,则可以使用SOCKET描述符开始向服务器发送命令,否则您的客户端将通过给出错误消息而退出。

客户端-服务器示例

以下是用Perl实现使用Perl套接字的简单客户端-服务器程序的Perl代码。在这里,服务器侦听传入请求,一旦建立连接,它只需回复来自服务器的问候。客户端读取该消息并将其打印到屏幕上。让我们看看它是如何完成的,假设我们的服务器和客户端在同一台机器上。

创建服务器的脚本

#!/usr/bin/perl -w
# Filename : server.pl

use strict;
use Socket;

# use port 7890 as default
my $port = shift || 7890;
my $proto = getprotobyname('tcp');
my $server = "localhost";  # Host IP running the server

# create a socket, make it reusable
socket(SOCKET, PF_INET, SOCK_STREAM, $proto)
   or die "Can't open socket $!\n";
setsockopt(SOCKET, SOL_SOCKET, SO_REUSEADDR, 1)
   or die "Can't set socket option to SO_REUSEADDR $!\n";

# bind to a port, then listen
bind( SOCKET, pack_sockaddr_in($port, inet_aton($server)))
   or die "Can't bind to port $port! \n";

listen(SOCKET, 5) or die "listen: $!";
print "SERVER started on port $port\n";

# accepting a connection
my $client_addr;
while ($client_addr = accept(NEW_SOCKET, SOCKET)) {
   # send them a message, close connection
   my $name = gethostbyaddr($client_addr, AF_INET );
   print NEW_SOCKET "Smile from the server";
   print "Connection recieved from $name\n";
   close NEW_SOCKET;
}

要在后台模式下运行服务器,请在Unix提示符下发出以下命令:

$perl sever.pl&

创建客户端的脚本

!/usr/bin/perl -w
# Filename : client.pl

use strict;
use Socket;

# initialize host and port
my $host = shift || 'localhost';
my $port = shift || 7890;
my $server = "localhost";  # Host IP running the server

# create the socket, connect to the port
socket(SOCKET,PF_INET,SOCK_STREAM,(getprotobyname('tcp'))[2])
   or die "Can't create a socket $!\n";
connect( SOCKET, pack_sockaddr_in($port, inet_aton($server)))
   or die "Can't connect to port $port! \n";

my $line;
while ($line = <SOCKET>) {
   print "$line\n";
}
close SOCKET or die "close: $!";

现在让我们在命令提示符下启动客户端,它将连接到服务器并读取服务器发送的消息,并在屏幕上显示如下:

$perl client.pl
Smile from the server

注意 - 如果您使用点表示法提供实际的IP地址,则建议在客户端和服务器中都以相同的格式提供IP地址,以避免任何混淆。

perl_function_references.htm
广告