发信人: demonalex(啊神) 
整理人: workingnow(2003-10-15 04:59:37), 站内信件
 | 
 
 
[灌水]使用perl socket api
 
 [日记文]basic i/o of perl socket[perl_sock 1]
 
 writer:demonalex
 email:demonalex_at_dark2s.org
 
 使用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=<CLIENT>;                       #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形式地址。
 
 
 最后做实验一个:
 #!usr/bin/perl -w
 #扫描器 power by demonalex_at_dark2s.org
 use Socket;
 #主机地址部分
 $host=$ARGV[1];
 $packhost=inet_aton($host);
 #主机地址部分
 
 $proto=$ARGV[0];
 $port1=$ARGV[2];
 $port2=$ARGV[3] || $port1;
 @port=($port1..$port2);
 $port3=$port2-$port1;
 
 if($proto eq '-t'){
 $sock_data=SOCK_STREAM;
 $sock_proto=6;
 $protocol='TCP';
 }
 elsif($proto eq '-u'){
 $sock_data=SOCK_DGRAM;
 $sock_proto=17;
 $protocol='UDP';
 }
 else{
 die "The second parameter must be '-t'=TCP or '-u'=UDP...\n";
 }
 
 if($port3<0){
 print"Sorry,port2 must be bigger than port1...\n";
 exit 1;}else{
 for($num=0;$num<=$port3;$num++){
 $addr[$num]=sockaddr_in($port[$num],$packhost);
 socket($sock[$num],AF_INET,$sock_data,$sock_proto);
 if(connect($sock[$num],$addr[$num])){
 print "The $protocol $port[$num] is opened!Great!!!\n";
 close $sock[$num];
 next;
 }else{
 warn "The $protocol $port[$num] may be closed!\n";
 close $sock[$num];
 next;
 }
 }
 exit 1;}
 
 #格式:perl connect.pl -t 192.168.x.x 139 445
 #扫描192.168.x.x的TCP139到TCP445的端口情况
 #增加了扫描UDP功能,不过好象有点BUG,修正中:)
 #欢迎测试,如有问题请EMAIL:demonalex_at_dark2s.org
 
 
 
 
  ---- 一穷二白三光棍:)
 http://demonalex.dark2s.org | 
 
 
 |