最近我遇到了一个很棒的perl模块“AnyEvent”,它允许用户进行异步/事件驱动的编程.
创建了以下代码片段,工作正常.我遇到的问题是,在打开并关闭许多套接字后,它很快耗尽了所有客户端端口(“netstat -ant”显示20,000个套接字处于TIME_WAIT状态).
$hdl = new AnyEvent::Handle ( connect => [$ip,$port],on_connect=> sub { my ($handle,$host,$port,$tmp) = @_; #print "connect routine for $handle->{ue}\r\n"; #update states. },on_read => sub { my $hdl = $_[0]; #read data #send response. });
我想知道是否可以使用IO :: Socket :: INET创建TCP套接字,然后在AnyEvent :: Handle中使用新创建的套接字:
my $sock = IO::Socket::INET->new( Proto => 'tcp',PeerAddr => $ue->{vars}->{ip},PeerPort => $ue->{vars}->{dstPort},ReusePort => 1,KeepAlive => 1 ) || die "Failed to setup outsock $@\n"; $hdl = new AnyEvent::Handle ( fh => $sock,on_read => sub { my $hdl = $_[0]; #read data #send response. });
尝试过,但它不起作用.感谢任何建议/意见.
感谢ikegami调查并提出了建议.但是,似乎SO_REUSEADDR没有生效.这是我使用的代码(基于他的建议)
use strict; use warnings; use AnyEvent qw( ); use AnyEvent::Handle qw( ); use AnyEvent::Impl::EV qw( ); use AnyEvent::Socket qw( tcp_connect ); use Socket qw( SOL_SOCKET SO_REUSEPORT SO_REUSEADDR); my $ts = 0; my $trans = 0; my $currentTS; sub transaction { my ($host,$port) = @_; tcp_connect($host,sub { my ($sock) = @_ or die "Can't connect: $!"; my $handle; $handle = AnyEvent::Handle->new( fh => $sock,on_eof => sub { $handle->destroy(); },on_read => sub { my ($handle) = @_; #print $handle->rbuf(); $trans ++; $currentTS = time(); if ($currentTS > $ts) { $ts = $currentTS; print "$trans\n"; } #printf "recved %d bytes of data\n",length($handle->rbuf); # This should continue to read until header + # Content-Length bytes have been read instead # of stopping after one read. if (length($handle->rbuf) > 0) { $handle->destroy(); } },); $handle->push_write("GET /s HTTP/1.1\r\nHost: $host\r\n\r\n"); #$handle->push_shutdown(); # Done writing. },sub { my ($sock) = @_; #setsockopt($sock,SOL_SOCKET,SO_REUSEPORT,1) or die $!; setsockopt($sock,SO_REUSEADDR,1) or die $!; # die "Failed to set linger $!\n"; return undef; }); } { my $cv = AnyEvent->condvar(); my $t = AnyEvent->timer(after=>0.001,interval=>1,cb=> sub { transaction("10.3.0.6",80 ); }); $cv->recv(); }
我的系统是Ubuntu 11.04.
在目录/ proc / sys / net / ipv4中,这是两个文件的内容:
% more tcp_tw_recycle
1
%tcp_tw_reuse
1
解决方法
我无法运行以下内容,因为Windows不提供SO_REUSEPORT,但我非常确信以下内容可以满足您的要求.
那就是说,我不确定它会有所帮助.根据我的阅读,SO_REUSEPORT允许您绑定到已经活动的端口,但是您没有绑定到任何端口.
use strict; use warnings; use AnyEvent qw( ); use AnyEvent::Handle qw( ); use AnyEvent::Impl::EV qw( ); use AnyEvent::Socket qw( tcp_connect ); use Socket qw( SOL_SOCKET SO_REUSEPORT );
sub transaction { my ($host,on_read => sub { my ($handle) = @_; print $handle->rbuf(); # This should continue to read until header + # Content-Length bytes have been read instead # of stopping after one read. $handle->destroy(); },); $handle->push_write("GET / HTTP/1.1\r\nHost: $host\r\n\r\n"); $handle->push_shutdown(); # Done writing. },sub { my ($sock) = @_; setsockopt($sock,1) or die $!; return undef; }); }
{ my $cv = AnyEvent->condvar(); my $t = AnyEvent->timer(after=>0.001,interval=>0.001,cb=> sub { transaction("localhost",$ARGV[0] // die("usage")); }); $cv->recv(); }
用于测试的服务器:
use strict; use warnings; use 5.010; use IO::Socket::INET qw( ); use Socket qw( inet_ntoa ); my $serv = IO::Socket::INET->new( Listen => 1,); say inet_ntoa($serv->sockaddr) . ":" . $serv->sockport; while (my $client = $serv->accept()) { say "Connection from ".inet_ntoa($client->peeraddr).":".$client->peerport; while (<$client>) { last if /^(?:\r?\n)?\z/; } say $client "HTTP/1.1 200 OK\r\n" . "Content-Type: text/plain\r\n" . "\r\n" . "Hello\n"; say " done."; }