使用AnyEvent::Socket连接UNIX域套接字无法读取客户端消息
问题原因及修复方案
核心问题1:服务端Handle对象被GC回收
AnyEvent::Handle 依赖外部强引用维持生命周期,你在连接回调内定义的$hdl是局部变量,回调执行完成后无任何引用指向它,会被Perl垃圾回收机制自动销毁,对应连接直接被关闭,无法读取后续发送的数据。
核心问题2:客户端提前退出导致数据未发送
$hdl->push_write只是将数据写入AnyEvent的用户态发送缓冲区,并未立刻同步到socket。你写完直接调用$done->send触发进程退出,缓冲区数据还没发出去就被释放了。
修复后的代码
服务端修复版
#!/usr/bin/perl use strict; use warnings; use feature 'say' ; use AnyEvent ; use AnyEvent::Socket ; use AnyEvent::Handle ; # 全局池保存Handle引用,避免被GC回收 our @ACTIVE_HDLS; tcp_server 'unix/', '/tmp/.mysocket', sub { my ( $fh, @args ) = @_ ; say "Received request" ; my $hdl = AnyEvent::Handle->new( fh => $fh, # 连接断开时清理引用 on_eof => sub { my ($hdl) = @_; $hdl->destroy; @ACTIVE_HDLS = grep { $_ != $hdl } @ACTIVE_HDLS; }, on_error => sub { my ($hdl, $fatal, $msg) = @_; say "Connection error: $msg"; $hdl->destroy; @ACTIVE_HDLS = grep { $_ != $hdl } @ACTIVE_HDLS; } ) ; # 把Handle加入全局池保持引用 push @ACTIVE_HDLS, $hdl; $hdl->push_read( line => sub { my ( $hdl, $line ) = @_ ; say "Received $line" ; } ) ; }, sub { my ( $fh, @sock ) = @_ ; say "Bound to @sock" ; } ; AnyEvent->condvar->recv ; exit ;
客户端修复版
#!/usr/bin/perl use strict; use warnings; use feature 'say' ; use AnyEvent ; use AnyEvent::Socket ; use AnyEvent::Handle ; my $done = AnyEvent->condvar ; tcp_connect "unix/", "/tmp/.mysocket", sub { my ( $fh ) = @_ or die "unable to connect: $!" ; say "Connected" ; my $hdl = AnyEvent::Handle->new( fh => $fh ) ; $hdl->push_write( "Hello world!\n" ) ; # 等待缓冲区所有数据写入完成后再退出 $hdl->on_drain(sub { my $hdl = shift; $hdl->destroy; $done->send ; }); } ; $done->recv ; say $! if $!; exit ;
验证方法
- 先启动服务端,会打印
Bound to /tmp/.mysocket - 再运行客户端,会打印
Connected - 服务端会依次打印
Received request和Received Hello world!
内容的提问来源于stack exchange,提问作者Hannibal
相关产品推荐
相关产品推荐

