如何从一个文件描述符创建一个可用的IO::Socket::INET套接字对象?

后端开发 2026-07-09

问题是 Bad arg length for Socket::unpack_sockaddr_in,见下文。

重现步骤

文件 /tmp/demo/app.psgi

use 5.042;
use strictures;
use Plack::Request qw();
use experimental 'signatures';

my $app = sub($env) {
    my $req = Plack::Request->new($env);
    return $req->new_response(200, ['Content-Type' => 'text/plain'], ['Hello world'])->finalize;
};

文件 ~/.config/systemd/user/demo.service,将 … 替换为指向 plackup 的绝对路径

[Unit]
Description=Socket-activated hello world service
Requires=demo.socket
After=network.target

[Service]
ExecStart=…perl/5.42.2/bin/plackup -a /tmp/demo/app.psgi
Type=simple

文件 ~/.config/systemd/user/demo.socket

[Unit]
Description=Socket for hello world service activation
PartOf=demo.service

[Socket]
ListenStream=5000
NoDelay=true
Backlog=128

[Install]
WantedBy=sockets.target

对Plack-1.0054应用补丁

diff --git a/lib/HTTP/Server/PSGI.pm b/lib/HTTP/Server/PSGI.pm
index 225d4ca..f060463 100644
--- a/lib/HTTP/Server/PSGI.pm
+++ b/lib/HTTP/Server/PSGI.pm
@@ -90,6 +90,12 @@ sub prepare_socket_class {
 sub setup_listener {
     my $self = shift;

+    # TO DO: also handle LISTEN_PID LISTEN_PIDFDID
+    if ($ENV{LISTEN_FDS} && 1 == $ENV{LISTEN_FDS}) {
+        $self->{listen_sock} ||= IO::Socket::INET->new_from_fd(3, 'r+');
+        goto DONE;
+    }
+
     $self->{listen_sock} ||= do {
         my %args = (
             Listen    => SOMAXCONN,
@@ -104,6 +110,7 @@ sub setup_listener {
             or die "failed to listen to port $self->{port}: $!";
     };

+DONE:
     $self->{server_ready}->({ %$self, proto => $self->{ssl} ? 'https' : 'http' });
 }
  1. 应用补丁,并把修补后的Plack安装在 app.psgi 可以使用的路径上
  2. 让端口5000可用(也可能是已有的Plack应用占用了端口?)
  3. 运行 systemctl --user daemon-reload
  4. 运行 systemctl --user enable --now demo.socket

观察到的问题

运行 xh -v :5000,或使用任意其他HTTP客户端向 GET http://localhost:5000 发送请求,均无响应。

journalctl --user -e -u demo -f 显示错误:

systemd: Started Socket-activated hello world service.
plackup: HTTP::Server::PSGI: Accepting connections at http://0:5000/
plackup: Bad arg length for Socket::unpack_sockaddr_in, length is 28, should be 16 at …perl/5.42.2/lib/5.42.2/x86_64-linux-ld/Socket.pm line 858.
systemd: demo.service: Main process exited, code=exited, status=29/n/a
systemd: demo.service: Failed with result 'exit-code'.

重新开始:systemctl --user stop demo.servicesystemctl --user restart demo.socket

预期结果

HTTP请求完成。

相关信息

在dist IOnew_from_fd 文档new_from_fd 测试

清理系统

systemctl --user disable --now demo.socket
rm ~/.config/systemd/user/demo.{service,socket} /tmp/demo/app.psgi
# rm -rf …Plack-1.055
systemctl --user daemon-reload

解决方案

... Bad arg length for Socket::unpack_sockaddr_in, length is 28, should be 16

这看起来不是你得到的IPv4套接字,而是一个IPv6套接字。这意味着你需要使用IO::Socket::IP,而不是IO::Socket::INET。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章