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 );
SOCKET : 一個socket的描述符。
ADDRESS : ADDRESS 是socket 地址( TCP/IP ) 包含了三個元素:
地址簇(TCP/IP, 是AF_INET, 在你係統上可能是2)
端口號(例如21)
網絡地址(例如10.12.12.168)
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 我是来自服务端的信息