使用PERL SOCKET API首先需要載入SOCKET模組。 use Socket; ====================================================================== socket(檔案控制代碼,AF_INET,資料類型,協議類型); #建立通訊端 檔案控制代碼隨便找個詞就可以了。 AF_INET為域類型,也可以寫為PF_INET。 資料類型,通常用有兩種:SOCK_STREAM、SOCK_DGRAM。 協議類型,可以用協議號代替,EGP---8、HMP---20、ICMP---1、 RAW---255、RDP---27、RVD---66、TCP---6、UDP---17、XNS-IDP---22、 其他---22、ALL---0;也可以用getprotobyname()函數作此參數。 例子:socket(SOCK,AF_INET,SOCK_STREAM,getprotobyname('tcp')); 語柄為SOCK,通訊端以TCP方式傳輸。 socket(SS,AF_INET,SOCK_DGRAM,17); 語柄為SS,通訊端以UDP方式傳輸。 ======================================================================= connect(檔案控制代碼,sockaddr_in結構體); #串連主機 ----------------------------------------------- sockaddr_in結構體: $address=inet_aton(地址); $port=連接埠號碼; $result=sockaddr_in($port,$address); #上面的$result就是sockaddr_in結構體,執行個體: $address=inet_aton(127.0.0.1); $port=80; $result=sockaddr_in($port,$address); ----------------------------------------------- 例子:connect(SOCK,$result); ======================================================================= bind(通訊端,sockaddr_in結構體); #綁定伺服器主機地址與連接埠(用於服務端) 例子:bind(SOCK,$result); ======================================================================= listen(通訊端,等待串連最大隊列數); #設定連接埠狀態為監聽(用於服務端) 例子:listen(SOCK,10); ======================================================================= accept(遠程通訊端,服務端監聽通訊端) #接收遠端資料請求,建立串連(用於服務端) 例子:accept(SESSION,SOCK); ======================================================================= close(檔案控制代碼); 或 close 檔案控制代碼; #關閉通訊端 例子:close(SOCK); close SS; ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●● 說說TCP的網路活動順序: ======================================================================= Client(用戶端): 建立通訊端socket()->串連到目標主機connect()->開啟autoflush模式autoflush()-> I/O操作->關閉通訊端close() Server(伺服器): 建立通訊端socket()->綁定伺服器位址與連接埠bind()->設定監聽狀態listen()->接受遠程通訊端accept()-> 開啟autoflush模式autoflush()->I/O操作->關閉通訊端close() ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●● ◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎ PERL SOCKET API編程執行個體: 附:I/O操作暫時只使用print()與<>符號。 ======================================================================= #!usr/bin/perl #用戶端 use IO::Handle; #掛起IO::Handle use Socket; #調用SOCKET $port=80; #串連遠程主機的80連接埠 $host='localhost'; #使用迴路位址 $packhost=inet_aton($host); #壓縮IP地址 $address=sockaddr_in($port,$packhost); #壓成sockaddr_in格式 socket(CLIENT,AF_INET,SOCK_STREAM,6); #通訊端為CLIENT,使用TCP協議 connect(CLIENT,$address); #串連 CLIENT->autoflush(1); #開啟AUTOFLUSH模式 $msg_in= #INPUT print "IN:$msg_in/n"; #OUTPUT close CLIENT; #關閉通訊端 exit 1; #退出程式 ======================================================================= #!usr/bin/perl #服務端 use IO::Handle; #掛起IO::Handle use Socket; #調用SOCKET $port=80; #綁定的伺服器主機連接埠為80 $address=sockaddr_in($port,INADDR_ANY); #壓成sockaddr_in格式,使用INADDR_ANY萬用字元 socket(SERVER,AF_INET,SOCK_STREAM,getprotobyname('tcp')); #通訊端為SERVER,使用TCP協議 bind(SERVER,$address); #綁定 listen(SERVER,10); #設定監聽狀態 while(1){ #進入I/O交換迴圈體 next unless (accept(CLIENT,SERVER)); CLIENT->autoflush(1); print CLIENT "WHAT DO YOU WANT?/n"; close CLIENT;} close SERVER; #關閉通訊端 exit 1; #退出程式 ======================================================================= 執行個體註解: 1)TCP的client與server代碼中有一行'SOCK->autoflush(1);',正常情況下下面的I/O代碼是會先進入緩衝, 再輸出的,但加了上面的代碼就可以跳過這一步直接輸出了。此代碼需要積極式載入IO::Handle。 2)INADDR_ANY萬用字元的值在Socket模組中已經定義了,其值為本網適配的所有網路介面(包括迴路位址、 廣播位址、多播地址等)。 ◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎ 頭有點痛,寫到這裡,下回介紹send()與recv()........:P [日記文]'send()' and 'recv'[perl_sock 2] writer:demonalex email:demonalex_at_dark2s.org 附接上文的某些內容,最後使用的兩個C/S程式中的資料交換部分使用了PERL I/O, 現在介紹一下PERL語言分配給通訊端的‘原裝’網路資料交換函數:send()、recv()。 (這兩個函數對UDP協議的作用很大,但對TCP來說其實只能說是等於syswrite()、sysread()。) ====================================================================== 位元組變數=send(通訊端,傳送資料變數,標誌參數); send()函數用於在通訊端進程中發送資料。 send()返回的值是儲存所發送的位元組大小值的變數;傳送資料變數為傳輸資料的內容; 標誌參數為0(預設值就可以了)。 例子:$bytes=send(SOCK,$data,0); ====================================================================== 地址變數=recv(通訊端,接收後的資料所儲存的變數,接收資料的長度,標誌參數); recv()函數用於在通訊端進程中接收資料。 recv()返回遠程主機的地址變數;第二個參數為接收後的資料所儲存的變數;第三個 參數為所接收資料的長度;標誌參數同樣為預設值0就可以了。 例子:$address=recv(SOCK,$buffer,$length,0); ====================================================================== 實驗1 #!usr/bin/perl #用戶端 use IO::Handle; use Socket; $port=80; $host='localhost'; $packhost=inet_aton($host); $address=sockaddr_in($port,$packhost); socket(CLIENT,AF_INET,SOCK_STREAM,6); connect(CLIENT,$address); CLIENT->autoflush(1); recv(CLIENT,$msg_in,length($msg_in),0); print "IN:$msg_in/n"; close CLIENT; exit 1; ======================================================================= #!usr/bin/perl #服務端 use IO::Handle; use Socket; $port=80; $host='localhost'; $packhost=inet_aton($host); $address=sockaddr_in($port,$packhost); socket(SERVER,AF_INET,SOCK_STREAM,getprotobyname('tcp')); bind(SERVER,$address); listen(SERVER,10); while(1){ next unless (accept(CLIENT,SERVER)); CLIENT->autoflush(1); $msg_out="WHAT DO YOU WANT?/n"; send(CLIENT,$msg_out,0); close CLIENT;} close SERVER; exit 1; [日記文]udp of perl socket[perl_sock 3] writer:demonalex email:demonalex_at_dark2s.org 繼續上文談到的send()與recv(),這次談一下它們在udp socket中的應用以及如果使用 perl socket API來調用UDP。 先看看在UDP中的send()、recv()應用: ========================================================================== 位元組變數=send(通訊端,傳送資料變數,標誌參數,發送地址); send()函數用於在通訊端進程中發送資料。 send()返回的值是儲存所發送的位元組大小值的變數;傳送資料變數為傳輸資料的內容; 標誌參數為0(預設值就可以了);send()在udp中就多了最後一個參數,‘發送地址’, 此地址的資料形式為sockaddr_in格式,表示把第二參數‘傳送資料變數’發送到此地址中。 例子:$bytes=send(SOCK,$data,0,$address); 樓上例子中的$address為sockaddr_in格式。 ========================================================================== 地址變數=recv(通訊端,接收後的資料所儲存的變數,接收資料的長度,標誌參數); recv()函數用於在通訊端進程中接收資料。 recv()返回遠程主機的地址變數;第二個參數為接收後的資料所儲存的變數;第三個 參數為所接收資料的長度;標誌參數同樣為預設值0就可以了。 例子:$address=recv(SOCK,$buffer,$length,0); ========================================================================== 從樓上的講解可以知道,在UDP調用中send()比TCP調用時多了一個參數,而recv()與在TCP調用時的 使用方法完全一致。 ------------------------------------------------------------------------ UDP網路活動順序: Client(用戶端): 建立通訊端socket()->發送資料send()->接受資料recv()->關閉通訊端close() Server(服務端): 建立通訊端socket()->綁定地址bind()->接受資料recv()->發送資料send()->關閉通訊端close() ------------------------------------------------------------------------ 從樓上的流程不難發現UDP中的用戶端與服務端的不同之處有兩點:1)服務端在建立通訊端後多添了一個 綁定bind()程式,用於使用戶端能分辨出服務端的網路地址與連接埠;2)在send()與recv()步驟上順序倒過 來了。 〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓 最後可以看看例子,琢磨琢磨: ━━━━━━━━━━━━━━━━━━━━━━━━━━━ #!use/bin/perl -w #udp client use Socket; #匯入Socket庫 $host=$ARGV[0]; #第一參數為主機變數 $port=$ARGV[1]; #第二參數為連接埠變數 $packhost=inet_aton($host); #壓縮主機地址 $address=sockaddr_in($port,$packhost); #壓為sockaddr_in模式 socket(CLIENT,AF_INET,SOCK_DGRAM,17); #建立UDP通訊端 send(CLIENT,"hi,body!/n",0,$address); #向通訊端發送字串變數 recv(CLIENT,$buff,100,0); #接收資料 print"$buff/n"; #把接收後的資料打入STDOUT close CLIENT; #關閉通訊端 exit 1; #退出程式 ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ #!use/bin/perl -w #udp server use Socket; #匯入Socket庫 $localhost=sockaddr_in(4000,INADDR_ANY);#壓入sockaddr_in模式,使用了全域本地壓縮地址INADDR_ANY保留字 socket(SERVER,AF_INET,SOCK_DGRAM,17); #建立UDP通訊端 bind(SERVER,$localhost); #綁定通訊端 while(1){ #進入伺服器迴圈體 next unless $client=recv(SERVER,$buff,100,0); #如果接收到資料就把資料壓入$buff,保留遠程地址在$client chop($buff); #減去$buff最後的輸入符號 print "$buff/n"; #在$buff變數打入STDOUT send(SERVER,"$buff/n",0,$client); #把$buff發送給用戶端 } close SERVER; #關閉通訊端 exit 1; #退出程式 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━ [日記文]Summary[perl_sock 4] writer:demonalex email:demonalex_at_dark2s.org 此文為前三篇文章的總結文。 tcp的服務端I/O結構體: ----------------------------------------------- while(1){ next unless (accept(CLIENT,SERVER)); CLIENT->autoflush(1); print CLIENT "WHAT DO YOU WANT?/n"; close CLIENT;} ----------------------------------------------- udp的服務端I/O結構體: ----------------------------------------------- while(1){ next unless $client=recv(SERVER,$buff,100,0); chop($buff); print "$buff/n"; send(SERVER,"$buff/n",0,$client); } ----------------------------------------------- 從上面的執行個體可以看出SERVER的I/O體都是迴圈體,有一特定條件進行迴圈 (我們這裡用了死迴圈while(1)),為了就是使服務端能不停的在監聽。 TCP I/O的特徵就是在accept()中產生一個用戶端的通訊端,所有I/O操作都 在此通訊端中進行,當I/O完成後,先把用戶端的通訊端關閉,最後才在程式 的末端部分關閉服務端的通訊端。 UDP I/O的特徵就是recv()部分,由於在介紹UDP的那篇文中的執行個體為通過UDP 把輸入的資料返回到用戶端的程式,在服務端中的下一步就應該調用send()了, 在send()中的最後一個參數為sockaddr_in形式地址,又因為在UDP中不能調用 accept(),所以無法得知對方的對象在哪裡,只能通過recv()的傳回值。recv() 的傳回值剛好為對方發送資料的sockaddr_in形式地址。 |