Perl Socket 程式設計

Perl Socket 程式設計

Socket又稱"套接字",應用程式通常通過"套接字"向網路發出請求或者應答網路請求,使主機間或者一臺計算機上的程序間可以通訊。

本章節我們為大家接收 Perl 語言中如何使用 Socket 服務。


建立服務端

  • 使用 socket 函式來建立 socket服務。

  • 使用 bind 函式繫結埠。

  • 使用 listen 函式監聽埠。

  • 使用 accept 函式接收客戶端請求。

建立客戶端

  • 使用 socket 函式來建立 socket 服務。

  • 使用 connect 函式連線到 socket 服務端。

以下圖表演示了客戶端與服務端之間的通訊流程:


服務端 socket 函式

socket 函式

Perl 中,我們用 socket()函式來建立套接字,語法格式如下:

socket( SOCKET, DOMAIN, TYPE, PROTOCOL );

引數解析:

  • DOMAIN 建立的套接字指定協議集。 例如:

    • AF_INET 表示IPv4網路協議
    • AF_INET6 表示IPv6
    • AF_UNIX 表示本地套接字(使用一個檔案)

  • TYPE 套接字型別可以根據是面向連線的還是非連線分為SOCK_STREAM或SOCK_DGRAM

  • PROTOCOL 應該是 (getprotobyname('tcp'))[2]。指定實際使用的傳輸協議。

所以 socket 函式呼叫方式如下:

use Socket     # 定義了 PF_INET 和 SOCK_STREAM

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

bind() 函式

使用 bind() 為套接字分配一個地址:

bind( SOCKET, ADDRESS );

SOCKET 一個socket的描述符。 ADDRESS 是 socket 地址 ( TCP/IP ) 包含了三個元素:

  • 地址簇 (TCP/IP, 是 AF_INET, 在你係統上可能是 2)

  • 埠號 (例如 21)

  • 網路地址 (例如 10.12.12.168)

使用socket()建立套接字後,只賦予其所使用的協議,並未分配地址。在接受其它主機的連線前,必須先呼叫bind()為套接字分配一個地址。

簡單例項如下:

use Socket        # 定義了 PF_INET 和 SOCK_STREAM

$port = 12345;    # 監聽的埠
$server_ip_address = "10.12.12.168";
bind( SOCKET, pack_sockaddr_in($port, inet_aton($server_ip_address)))
   or die "無法繫結埠! \n";

or die 在繫結地址失敗後執行。

通過設定 setsockopt() 可選項 SO_REUSEADDR 設定埠可立即重複使用。

pack_sockaddr_in() 函式將地址轉換為二進位制格式。

listen() 函式

當socket和一個地址繫結之後,listen()函式會開始監聽可能的連線請求。然而,這隻能在有可靠資料流保證的時候使用:

listen( SOCKET, QUEUESIZE );

SOCKET : 一個socket的描述符。

QUEUESIZE : 是 一個決定監聽佇列大小的整數,當有一個連線請求到來,就會進入此監聽佇列;當一個連線請求被accept()接受,則從監聽佇列中移出;當佇列滿後,新的連線請求會返回錯誤。

一旦連線被接受,返回0表示成功,錯誤返回-1。

accept() 函式

accept() 函式接受請求的socket連線。如果成功則返回壓縮形式的網路地址,否則返回FALSE:

accept( NEW_SOCKET, SOCKET );

NEW_SOCKET : 一個socket的描述符。

SOCKET : 一個socket的描述符。

accept() 通常應用在無限迴圈當中:

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

以上例項可以實時監聽客戶端的請求。


客戶端函式

connect() 函式

connect()系統呼叫為一個套接字設定連線,引數有檔案描述符和主機地址。

connect( SOCKET, ADDRESS );

以下建立一個連線到服務端 socket 的例項:

$port = 21;    #  ftp 埠
$server_ip_address = "10.12.12.168";
connect( SOCKET, pack_sockaddr_in($port, inet_aton($server_ip_address)))
    or die "無法繫結埠! \n";

完整例項

接下來我們通過一個完整例項來了解下所有 socket 函式的應用:

服務端 server.pl 程式碼:

例項

#!/usr/bin/perl -w # Filename : server.pl use strict; use Socket; # 使用埠 7890 作為預設值 my $port = shift || 7890; my $proto = getprotobyname('tcp'); my $server = "localhost"; # 設定本地地址 # 建立 socket, 埠可重複使用,建立多個連線 socket(SOCKET, PF_INET, SOCK_STREAM, $proto) or die "無法開啟 socket $!\n"; setsockopt(SOCKET, SOL_SOCKET, SO_REUSEADDR, 1) or die "無法設定 SO_REUSEADDR $!\n"; # 繫結埠並監聽 bind( SOCKET, pack_sockaddr_in($port, inet_aton($server))) or die "無法繫結埠 $port! \n"; listen(SOCKET, 5) or die "listen: $!"; print "訪問啟動:$port\n"; # 接收請求 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 "我是來自服務端的資訊"; print "Connection recieved from $name\n"; close NEW_SOCKET; }

開啟一個終端,執行以下程式碼:

$ perl sever.pl
訪問啟動:7890

客戶端 client.pl 程式碼:

例項

#!/usr/bin/perl -w # Filename : client.pl use strict; use Socket; # 初始化地址與埠 my $host = shift || 'localhost'; my $port = shift || 7890; my $server = "localhost"; # 主機地址 # 建立 socket 並連線 socket(SOCKET,PF_INET,SOCK_STREAM,(getprotobyname('tcp'))[2]) or die "無法建立 socket $!\n"; connect( SOCKET, pack_sockaddr_in($port, inet_aton($server))) or die "無法連線:port $port! \n"; my $line; while ($line = <SOCKET>) { print "$line\n"; } close SOCKET or die "close: $!";

開啟另外一個終端,執行以下程式碼:

$ perl client.pl
我是來自服務端的資訊