随着开发深入,socket的应用还是比较常见的。毕竟自己是业余出身,水平到不了自己开发php扩展的程度。所以利用php本身的功能,实现socket的一些应用还算是比较靠谱的选择。
如果在linux平台,可以考虑Swoole扩展,功能还是很强大的。至于windows,扩展的编译实在不方便。还是进入我们的主角吧,php的socket编程。内容多是基于众多网上资料拼凑而成。
一.需要了解的基本概念
1.阻塞/非阻塞 阻塞IO是指调用结果返回之前,当前线程会被挂起;相反,非阻塞指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。
2.多路复用 对于 Socket 来说,应该说能同时处理多个连接的模型都应该被称为多路复用,目前比较常用的有 select/poll/epoll/kqueue。在这些多路复用的模式中,异步阻塞/非阻塞模式的扩展性和性能最好。
3.同步/异步 所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回;相反,当一个异步过程调用发出后,调用者不能立刻得到结果,实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。
使用 select/poll 的同步模型:属于同步非阻塞 IO 模型,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | /** * SelectSocketServer Class * By James.Huang <shagoo#gmail.com> **/ set_time_limit(0); class SelectSocketServer { private static $socket; private static $timeout = 60; private static $maxconns = 1024; private static $connections = array(); function SelectSocketServer($port) { global $errno, $errstr; if ($port < 1024) { die("Port must be a number which bigger than 1024/n"); } $socket = socket_create_listen($port); if (!$socket) die("Listen $port failed"); socket_set_nonblock($socket); // 非阻塞 while (true) { $readfds = array_merge(self::$connections, array($socket)); $writefds = array(); // 选择一个连接,获取读、写连接通道 if (socket_select($readfds, $writefds, $e = null, $t = self::$timeout)) { // 如果是当前服务端的监听连接 if (in_array($socket, $readfds)) { // 接受客户端连接 $newconn = socket_accept($socket); $i = (int) $newconn; $reject = ''; if (count(self::$connections) >= self::$maxconns) { $reject = "Server full, Try again later./n"; } // 将当前客户端连接放入 socket_select 选择 self::$connections[$i] = $newconn; // 输入的连接资源缓存容器 $writefds[$i] = $newconn; // 连接不正常 if ($reject) { socket_write($writefds[$i], $reject); unset($writefds[$i]); self::close($i); } else { echo "Client $i come./n"; } // remove the listening socket from the clients-with-data array $key = array_search($socket, $readfds); unset($readfds[$key]); } // 轮循读通道 foreach ($readfds as $rfd) { // 客户端连接 $i = (int) $rfd; // 从通道读取 $line = @socket_read($rfd, 2048, PHP_NORMAL_READ); if ($line === false) { // 读取不到内容,结束连接 echo "Connection closed on socket $i./n"; self::close($i); continue; } $tmp = substr($line, -1); if ($tmp != "/r" && $tmp != "/n") { // 等待更多数据 continue; } // 处理逻辑 $line = trim($line); if ($line == "quit") { echo "Client $i quit./n"; self::close($i); break; } if ($line) { echo "Client $i >>" . $line . "/n"; } } // 轮循写通道 foreach ($writefds as $wfd) { $i = (int) $wfd; $w = socket_write($wfd, "Welcome Client $i!/n"); } } } } function close ($i) { socket_shutdown(self::$connections[$i]); socket_close(self::$connections[$i]); unset(self::$connections[$i]); } } new SelectSocketServer(2000); |