PHP:pcntl_alarm()和socket_select()可以和平地存在于同一个线程中吗?
我有一个 PHP CLI 脚本,主要编写为聊天服务器,供聊天客户端连接(不要问我为什么用 PHP 来做,那是另一个故事了哈哈)。
我的脚本利用 socket_select() 函数挂起执行,直到套接字上发生某些情况,此时它会唤醒、处理事件并等待下一个事件。现在,我需要每 30 秒左右执行一些例行任务(检查临时禁止的用户是否应该取消禁止、保存用户数据库以及其他各种事情)。
据我所知,PHP 根本没有很好的多线程支持。我的第一个想法是每次套接字生成事件时比较时间戳并使程序再次流动,但这非常不一致,因为服务器很可能闲置几个小时并且不执行任何清理例程。
我遇到了 PHP pcntl 扩展,它允许我为 SIGALRM 发送分配一个时间间隔,并在每次发送时执行一个函数。这似乎是解决我的问题的理想解决方案,但是 pcntl_alarm() 和 socket_select() 彼此冲突非常严重。每次触发 SIGALRM 时,我的套接字控制代码都会发生各种疯狂的事情。
我的程序相当长,所以我无法将其全部发布在这里,但这应该不重要,因为我不相信我在代码方面做了任何错误的事情。我的问题是:有没有办法让 SIGALRM 在与等待的 socket_select() 相同的线程中处理?如果是这样,怎么办?如果没有,我有什么选择?
这是我的程序的一些输出。我的闹钟功能只是输出“滴答声!”每当它被调用时,就可以很容易地知道事情何时发生。这是允许其勾选 4 次后的输出(包括错误)(尽管有说明,但没有实际尝试连接到服务器):
[05-28-10 @ 20:01:05] 聊天服务器在 192.168.1.28 端口 4050 上启动
[05-28-10 @ 20:01:05] 已从文件加载 2 个用户
PHP 注意:未定义偏移量:/home/danny/projects/PHPChatServ/ChatServ.php 第 112 行中的 0
PHP 警告:socket_select():无法选择 [4]:第 116 行 /home/danny/projects/PHPChatServ/ChatServ.php 中的系统调用中断
[05-28-10 @ 20:01:15] 勾选!
PHP 警告:socket_accept():无法接受传入连接 [4]:第 126 行 /home/danny/projects/PHPChatServ/ChatServ.php 中的系统调用中断
[05-28-10 @ 20:01:25] 勾选! PHP 警告:socket_getpeername() 期望参数 1 为资源,布尔值在 /home/danny/projects/PHPChatServ/ChatServ.php 第 129 行给出
[05-28-10 @ 20:01:25] 接受套接字连接 PHP 注意:未定义偏移量:1 在 /home/danny/projects/PHPChatServ/ChatServ.php 第 112 行
PHP 警告:socket_select():无法选择 [4]:第 116 行 /home/danny/projects/PHPChatServ/ChatServ.php 中的系统调用中断
[05-28-10 @ 20:01:35] 勾选!
PHP 警告:socket_accept():无法接受传入连接 [4]:第 126 行 /home/danny/projects/PHPChatServ/ChatServ.php 中的系统调用中断
[05-28-10 @ 20:01:45] 勾选!
PHP 警告:socket_getpeername() 期望参数 1 为资源,布尔值在 /home/danny/projects/PHPChatServ/ChatServ.php 第 129 行给出
[05-28-10 @ 20:01:45] 接受来自
的套接字连接PHP 通知:未定义偏移量:/home/danny/projects/PHPChatServ/ChatServ.php 第 112 行中的 2
I have a PHP CLI script mostly written that functions as a chat server for chat clients to connect to (don't ask me why I'm doing it in PHP, thats another story haha).
My script utilizes the socket_select() function to hang execution until something happens on a socket, at which point it wakes up, processes the event, and waits until the next event. Now, there are some routine tasks that I need performed every 30 seconds or so (check if tempbanned users should be unbanned, save user databases, other assorted things).
From what I can tell, PHP doesn't have very great multi-threading support at all. My first thought was to compare a timestamp every time the socket generates an event and gets the program flowing again, but this is very inconsistent since the server could very well sit idle for hours and not have any of my cleanup routines executed.
I came across the PHP pcntl extensions, and it lets me use assign a time interval for SIGALRM to get sent and a function get executed every time it's sent. This seems like the ideal solution to my problem, however pcntl_alarm() and socket_select() clash with each other pretty bad. Every time SIGALRM is triggered, all sorts of crazy things happen to my socket control code.
My program is fairly lengthy so I can't post it all here, but it shouldn't matter since I don't believe I'm doing anything wrong code-wise. My question is: Is there any way for a SIGALRM to be handled in the same thread as a waiting socket_select()? If so, how? If not, what are my alternatives here?
Here's some output from my program. My alarm function simply outputs "Tick!" whenever it's called to make it easy to tell when stuff is happening. This is the output (including errors) after allowing it to tick 4 times (there were no actual attempts at connecting to the server despite what it says):
[05-28-10 @ 20:01:05] Chat server started on 192.168.1.28 port 4050
[05-28-10 @ 20:01:05] Loaded 2 users from file
PHP Notice: Undefined offset: 0 in /home/danny/projects/PHPChatServ/ChatServ.php on line 112
PHP Warning: socket_select(): unable to select [4]: Interrupted system call in /home/danny/projects/PHPChatServ/ChatServ.php on line 116
[05-28-10 @ 20:01:15] Tick!
PHP Warning: socket_accept(): unable to accept incoming connection [4]: Interrupted system call in /home/danny/projects/PHPChatServ/ChatServ.php on line 126
[05-28-10 @ 20:01:25] Tick!
PHP Warning: socket_getpeername() expects parameter 1 to be resource, boolean given in /home/danny/projects/PHPChatServ/ChatServ.php on line 129[05-28-10 @ 20:01:25] Accepting socket connection from
PHP Notice: Undefined offset: 1 in /home/danny/projects/PHPChatServ/ChatServ.php on line 112PHP Warning: socket_select(): unable to select [4]: Interrupted system call in /home/danny/projects/PHPChatServ/ChatServ.php on line 116
[05-28-10 @ 20:01:35] Tick!
PHP Warning: socket_accept(): unable to accept incoming connection [4]: Interrupted system call in /home/danny/projects/PHPChatServ/ChatServ.php on line 126
[05-28-10 @ 20:01:45] Tick!
PHP Warning: socket_getpeername() expects parameter 1 to be resource, boolean given in /home/danny/projects/PHPChatServ/ChatServ.php on line 129
[05-28-10 @ 20:01:45] Accepting socket connection from
PHP Notice: Undefined offset: 2 in /home/danny/projects/PHPChatServ/ChatServ.php on line 112
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
pcntl_alarm
和socket_select
可以共存,但您需要知道如何正确执行。特别是,如果在
socket_select()
等待时警报响起,那么在处理警报后,socket_select()
将立即返回并带有错误指示。错误是“系统调用中断”,这就是您在输出中看到的内容。您需要专门检查该错误,然后重试socket_select()
。或者,您可以忘记使用警报,而使用
socket_select()
的超时功能。这就是tv_sec
参数的用途 - 它给出以秒为单位的超时,之后即使没有套接字准备就绪,socket_select()
也会返回。然后您就可以进行常规处理。pcntl_alarm
andsocket_select
can coexist, but you need to be aware of how to do it right.In particular, if the alarm goes off while
socket_select()
is waiting, then after the alarm is handled thesocket_select()
will return immediately with an error indication. The error is "Interrupted System Call", which is what you're seeing in the output. You need to specifically check for that error, and retry thesocket_select()
.Alternatively, you can just forget about using the alarm and instead use the timeout facility of
socket_select()
. This is what thetv_sec
parameter is for - it gives a timeout in seconds, after which thesocket_select()
will return even if no sockets are ready. You can then do your regular processing.