Chinaunix首页 | 论坛 | 博客
  • 博客访问: 7919716
  • 博文数量: 701
  • 博客积分: 2150
  • 博客等级: 上尉
  • 技术积分: 13233
  • 用 户 组: 普通用户
  • 注册时间: 2011-06-29 16:28
个人简介

天行健,君子以自强不息!

文章分类

全部博文(701)

文章存档

2019年(2)

2018年(12)

2017年(76)

2016年(120)

2015年(178)

2014年(129)

2013年(123)

2012年(61)

分类: PERL

2013-03-22 22:16:24

一、IO::Socket简介
IO::Socket, 它是socket通信的面向对象的Perl接口。
IO::Socket下又有两个子类IO::Socket::INET与IO::Socket::UNIX.

现在用的是IO::Socket::INET。它的步骤是:
先调用new方法,
然后就可以进行基本I/O操作(使用print与getline等基本I/O方法)了,
最后调用close方法结束会话,那么整个SOCKET会话就算完成了。

方法:
1. new()方法:
语法:
  SOCKET对象变量 = IO::Socket::INET->new(SOCKET变量值);
实例:
  $sock=IO::Socket::INET->new('192.168.1.2:23');
讲解:     
所有的PERL对象编程都把对象‘形象化’为某个变量,
这里的SOCKET句柄对象也不例外,调用此方法的返回值便为SOCKET对象变量了。
这里使用参数为简单参数模式,
在双引号或但引号内的socket地址结构为:
        '主机IP或域名:端口号或服务名称',
或      '主机IP或域名:服务名称(端口号)'。
     
除了最简单的单参数调用外,new方法还有很多参数可以选择性调用的,
下面就对这些参数的简单概括:
***********************************************************************
参数              描述                                      值类型
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
PeerAddr          远程主机的地址                主机地址[:端口或服务]
PeerHost          与PeerAddr相同
PeerPort          远程端口或服务                 端口或服务
LocalAddr         本地地址                          主机地址[:端口或服务]
LocalHost         与LocalAddr相同
LocalPort         本地端口                            端口或服务
Proto               所使用的协议                    协议名或协议号
Type                套接字类型                       SOCK_STREAM/SOCK_DGRAM...
Listen               监听的队列长度                整形数
Reuse              用于避免重启时BIND时间间隙    布尔值
Timeout           超时值                               整形数
MultiHomed     用于连接多IP地址              布尔值
***********************************************************************
     
PeerAddr(远程主机地址)与
PeerHost(远程主机名)基本相同,
  调用方式也相同,其值格式除了标准的格式外,还可以加':'号后再加端口或服务,
  这样的的话,后面的参数PeerPort(远程主机端口或服务)的值就无效了。
     
PeerPort(远程主机端口或服务),
  其值的格式可以是端口,还可以是服务名,
  更可以是‘组合’,
  如:"telnet(23)";
  当PeerAddr(远程主机地址)或PeerHost(远程主机名)的值格式中指明了端口,
  再调用此参数时,此参数的值无效。
     
LocalAddr(本地主机地址)、
LocalHost(本地主机名)、
LocalPort(本地主机端口或服务)之间的关系与调用方式
  与上面介绍的三个参数
  PeerAddr(远程主机地址)、
  PeerHost(远程主机名)、
  PeerPort(远程主机端口或服务)相当。
     
还有一种情况,就是如果
只定义了LocalPort(本地主机端口或服务),
而没有定义LocalAddr(本地主机地址)或LocalHost(本地主机名),
那IO::Socket会将本地机器的地址的值默认为INADDR_ANY通配符,
也就是不定义本地主机的地址值的话就定义为允许所有接口。
     
Proto(协议类型)的值可以用两种方式表示。
  一种是直接的字符串表示方式,
    如:
      proto=>"tcp"
    表示该协议类型为TCP。
  第二种方式就是直接使用协议号了,
    EGP---8、
    HMP---20、
    ICMP---1、
    RAW---255、
    RDP---27、
    RVD---66、
    TCP---6、
    UDP---17、
    XNS-IDP---22、
    其他---22、
    ALL---0;
  也可以使用getprotobyname函数加协议名为参数调用获的该值,
    如:
      proto=>getprotobyname('tcp')
    该形式也表示该协议的类型为TCP。
建议还是使用第一种方式比较方便。
     
Type(套接字类型)的值通常为
  SOCK_STREAM(流套接字)、
  SOCK_DGRAM(数据报套接字)、
  SOCK_RAW(原始套接字)等,
不用说大家都知道,
  TCP用的是流套接字,
  UDP用的是数据报套接字,
  构造IP包用的是原始套接字。
     
如果上面的参数
  Proto(协议类型)
与Type(套接字类型)的值都不定义的话,
  IO::Socket::INET就会通过程序中上下‘文’部分猜估它们的值,
  猜估不到的话就会默认为'tcp'。
     
Listen(监听队列的长度)的值是一个整形数。
  它代表能接受的连接主机数量。
  如果您要构造服务端的话,Listen这个步骤是必不可少的。
     
调用Reuse(在绑定前设置SO_REUSEADDR)
  可以免去服务器在终止到重启之间的所停留的时间。
     
Timeout(超时值)以秒计算,
  用于连接中的connect与accept这两个步骤,
  调用目的是为了在连接远程主机不可到达时限制连接的挂起时间。
     
MultiHomed(用于连接多IP地址)的值是一个布尔值,
  当其值为真时,如果要连接的主机拥有多个IP地址,
  则本机的new方法调用gethostbyname()穷举其所有IP地址,直到能成功调用为止。
     
从楼上的列表中可以看到IO::Socket与传统C库的Socket API接口在调用上有什么不同了:
1)控制范围不同。
  C库              提供的接口在生成SOCKET句柄时只能控制的只有域、套接字类型、协议这几个参数。
  IO::Socket   接口的创建语句(调用new方法)几乎能决定这个套接字的所有参数。
2)调用所使用的‘协议’定义部分不同。
  IO::Socket接口调用new方法中的参数'Proto'的值可以直接定义为'tcp',
  这比传统C库的Socket定义更为简便。
3)IO::Socket在定义时能直接定义本地主机地址、本地端口与远程主机地址、远程端口在一个Socket中,
  如果是这种情况的服务端就无需调用accept了,
  在I/O读写部分可以直接向这个Socket进行读写操作,而无需再定义远程客户端的Socket了。


2. accept()方法:
语法:
  远程连接套接字对象变量 = 服务端套接字对象变量->accept();
实例:
  $remote_sock=$sock->accept();
讲解:
此方法的调用环境与传统C中SOCKET库调用原理一样,用于服务端的等待监听过程。
无参数,
返回值为远程连接的套接字对象变量。
调用此方法也是一个生成套接字的过程,只不过此套接字为远程连接的套接字而已,
它以对象变量方式存在,据有与本地套接字变量相同的属性与方法。

accept()方法在IO::Socket包里还提供另一种双返回值的调用方法:
语法:
  (远程连接套接字对象变量,远程主机压缩地址变量)=服务端对象变量->accept();
实例:
  ($remote_sock,$remote_addr)=$sock->accept();
讲解:     
  与上一个返回值的调用方式基本相同,只是返回值中多了一个变量而已,
  返回值中多了个变量------远程主机压缩地址变量。

3. bind()方法:
语法:
  返回值变量=服务端套接字对象变量->bind(本地端口号,本地主机网络地址);
实例:
  $result=$sock->bind(80,'127.0.0.1');
讲解:
bind方法用于在服务器端绑定主机的地址与端口。
它使用的两个参数都为未压缩值,
  第一个为端口,
  第二个为主机的网络适配器接口地址
  (可以使用默认的保留字INADDR_ANY,
   此保留字包括了主机的所有网络适配器接口地址,
   调用它时,它会以穷举的方法穷举所有的网络适配器接口地址,直到找到为止);
返回值为布尔值,用于检测这次调用是否成功。

4. connect()方法:
语法:
  返回值变量 = 套接字对象变量->connect(压缩地址变量);
实例:
  $result = $sock->connect($pack_addr);
讲解:
常用于TCP连接(也可用于UDP,不过不常用),调用将向远程主机发送连接请求。
参数‘压缩地址变量’为sockaddr_in形式值,
返回值为布尔值。
若调用此方法则建立IO::Socket::INET对象时不能赋予参数'PeerAddr'或'PeerHost'、'PeerPort',
否则就会出现程序逻辑错误。

connect()方法也有双参数调用方式,使用起来更简单:
语法:
  返回值变量=套接字对象变量->connect(远程端口号,远程主机地址);
实例:
  $result = $sock->connect($remote_port,$remote_host);
讲解:     
调用的目的与上面单参数的调用方式相当。
第一个参数为远程需要连接的主机的端口(等于new方法的参数'PeerPort'),
第二个参数为需要连接的主机地址(等于new方法的参数'PeerAddr'或'PeerHost'),
返回值为布尔值。

5. listen()方法:
语法:
  返回值变量 = 套接字对象变量->listen(请求队列的最大长度值);
实例:
  $result=$sock->listen(20);
讲解:     
TCP服务端不可缺少的方法。
单参数,参数为此服务端接受远端请求队列的最大长度值,
返回值为布尔值。
调用此方法等同于在建立IO::Socket::INET对象时定义参数'Listen'的值,
所以若在new方法中定义了参数'Listen'再调用此方法的话就会出现‘程序定义冲突’这样的逻辑错误了。

6. shutdown()方法:
语法:
  返回值变量 = 套接字对象变量->shutdown(控制参数);
实例:
  $result=$sock->shutdown(2);
讲解:
此方法是除了close外的另一个关闭套接字对象的方法。
单参数,参数值为外加参数定义,下为此方法的外加参数列表:
***********************************************************************
参数值                       描述
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
0                            关闭对象套接字的读操作
1                            关闭对象套接字的写操作
2                            关闭对象套接字的所有操作
***********************************************************************
其返回值为布尔值。

7. send()方法:
语法:
  成功发送的数据值变量 = 套接字对象变量->send(发送数据,标志值,目标地址值);
实例:
  $succ_bytes=$sock->send('hihi\n',0,$pack_host);
讲解:
send方法是专门为SOCKET发送数据的特殊方法,调用格式与参数格式也基本与C库的SOCKET API中的send函数相同。
第一个参数是需要发送的数据;
第二参数是标志值,不添的话默认为0;
第三个参数通常只用于UDP连接,是需要连接的sockaddr_in格式地址值
(注意:当第三个参数有必要一定要写时,第二个参数也一定要加上);
返回值为成功发送的数据值大小(以byte为单位)。

8. recv()方法:
语法:
  压缩远程地址地址 = 套接字对象变量->recv(接收数据变量,接收数据值长度,标志值);
实例:
  $remote_pack_address=$sock->recv($mem,100,0);
讲解:
recv方法是专门为SOCKET接收数据的特殊方法,
调用格式与参数格式也与C库的SOCKET API基本一样。
第一个参数是存放接收后的数据的变量值;
第二个参数是接收的数据的长度值;
第三个参数是标志值,默认为0就可以了(省略此值不填,系统默认也为0)。

二、IO::Select :
IO::Select包是一个select系统调用的面向对象的Perl接口。
它实现了类似于系统函数select的调用,允许用户了解到有哪些句柄 就绪可读 / 可写 / 未知的例外

方法:
1. new([HANDLE])
  创建新的对象,并且可随意用一组句柄来初始化

2. add(HANDLES) 
向对象中增加一个句柄的列表,它是一个事务发生时将要返回的值,
IO::Select将这些值保存在高速缓冲区内,并以句柄的fileno建立索引,
因此如果多于一个的句柄被指定相同的fileno,那么只将最后一个存入缓冲区。
每一个句柄能够成为一个IO::Select对象,一个整数或第一元素为IO::Handle或整数的数组的指针。

3. remove(HANDLES) 
从对象中移除所给的句柄,同样针对句柄的fileno工作,所增加的具有相同fileno的句柄将不被通过。

4. exits(HANDLES) 
如果句柄现在是存在的,返回一个真值(实际上是句柄本身),其他情况返回未定义。

5. handles 
返回一个数组,元素包括所有已注册的数组。

6. can_read([TIMEOUT]) 
返回一个数组,元素包括已就绪的可读的句柄。
TIMEOUT是在返回一个空数组前的等待的最大时间数,
若未指定TIMEOUT并且所有句柄已注册,则调用将阻塞。

7. can_write([TIMEOUT]) 
除了是返回可写的句柄外其它与can_read相同。

8. has_exception([TIMEOUT]) 
检测句柄的异常状态。

9. count() 
返回句柄的数目,
当调用一个can_方法或对象通过select静态方法时被检测的句柄的数目。

10. bit() 
Return the bit string suitable as argument to the core select() call


11. select(READ,WRITE,EXCEPTION[,TIMEOUT]) 
select是一个静态方法,调用它要像调用new()那样带上包名。
READ, WRITE 和EXCEPTION 可以是undef或IO::Select对象。
TIMEOUT是可选的,具有与核心selcet调用相同的作用。
返回的结果
  是一个三元素的数组,
  每个元素是分别指向存储着已就绪可读,可写和未知状态的句柄所组成的数组的指针。
  如果遇到错误返回一个空列表。

三、发送端示例代码
#! /usr/bin/perl
###############################################################################
# \File
#  tcp_client.pl
# \Descript
#  send message to server
###############################################################################
use IO::Socket;
use IO::Select;

#hash to install IP Port
%srv_info =(
  "srv_ip"  => "192.168.1.73",
  "srv_port"=> "5277",
);

my $srv_addr = $srv_info{"srv_ip"};
my $srv_port = $srv_info{"srv_port"};

my $sock = IO::Socket::INET->new(
      PeerAddr => "$srv_addr",
      PeerPort => "$srv_port",
      Type     => SOCK_STREAM,
      Proto    => "tcp",
  )
or die "Can not create socket connect. $@";

$sock->send("Client OK!\n", 0) or warn "send failed: $!, $@";
$sock->autoflush(1);

my $sel = IO::Select->new($sock);
while(my @ready = $sel->can_read)
{
  foreach my $fh(@ready)
  {
    if($fh == $sock)
    {
      while(<$fh>)
      {
        print $_;
      }
      $sel->remove($fh);
      close $fh;
      close FILE;
    }
  }
}
$sock->close();

四、接收端示例代码
#! /usr/bin/perl
###############################################################################
# \File 
# tcp_server.pl
# \Descript
# listen to local port
###############################################################################
use IO::Socket;
use IO::Select;

#hash to install IP Port
%srv_info = (
      "clt_ip"   => "192.168.1.72",
      "clt_port" => "5277",
      "srv_ip"   => "192.168.1.73",
      "srv_port" => "5277",
      );
$SIG{INT} = $SIG{TERM} = sub{
  $sock->close() or warn "Close Socket failed. $!, $@";
}

my $clt_addr = $srv_info{"clt_ip"};
my $clt_port = $srv_info{"clt_port"};
my $srv_addr = $srv_info{"srv_ip"};
my $srv_port = $srv_info{"srv_port"};

my $sock = IO::Socket::INET->new(
      PeerAddr => "$clt_addr",
      PeerPort => "$clt_port",
      LocalAddr=> "$srv_addr",
      LocalPort=> "$srv_port",
      Type     => SOCK_STREAM,
      ReuseAddr=> SO_REUSEADDR,           # Recycling port and reusing
      Proto    => "tcp",
      Listen   => 20,
     )
or die "Can not create socket connect: $!,  $@";

my $sel = IO::Select->new($sock);
while(my @ready = $sel->can_read)
{
  foreach my $fh(@ready)
  {
    if($fh == $sock)
    {
      my $new = $sock->accept();
      $sel->add($new);
    }
    else
    {
      $len = $fh->recv($buffer, 1024, 0);
      print "$buffer\n";
      $fh->send("Server OK!\n",0);
      $fh->autoflush(1);
      $sel->remove($fh);
      $fh->close();
    }
  }
}
$sock->close() or warn "Close Socket failed. $!, $@";

参考文章:
http://www.cnblogs.com/hanleilei/archive/2012/03/23/2413995.html
http://www.cnblogs.com/hanleilei/archive/2012/03/23/2413989.html
阅读(3005) | 评论(0) | 转发(1) |
0

上一篇:命令行参数解析精粹

下一篇:Perl线程综述

给主人留下些什么吧!~~