Perl - 套接字编程



什么是套接字?

套接字是 Berkeley UNIX 创建不同进程之间虚拟双工连接的机制。后来移植到每个已知的操作系统上,从而能够在运行不同操作系统软件的不同地理位置的系统之间进行通信。如果没有套接字,系统之间的大部分网络通信将永远不会发生。

仔细观察;网络上的典型计算机系统根据其上运行的各种应用程序的需要接收和发送信息。此信息被路由到系统,因为为其指定了一个唯一的 IP 地址。在系统上,此信息被提供给相关的应用程序,这些应用程序侦听不同的端口。例如,Internet 浏览器侦听端口 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)。

  • 计算机的 Internet 地址(例如 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() 调用失败,则返回 FLASE,它在最初使用的 Socket 模块中定义。

通常,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 代码。这里服务器侦听传入请求,一旦建立连接,它就简单地回复来自服务器的问候。客户端读取该消息并在屏幕上打印。让我们看看它是如何完成的,假设我们的服务器和客户端在同一台机器上。

创建服务器的脚本

#!/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 地址,以避免任何混淆。

广告