php之socket笔记

By | 2014/11/15

随着开发深入,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);

发表评论

电子邮件地址不会被公开。

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据