费话少说,用源代码说话
1)客户端实现
1 2 3 4Web sockets test 5 6 37 38 39 40 41 42 43 44 45
2)服务器端实现
1 class WS { 2 var $master; // 连接 server 的 client 3 var $sockets = array(); // 不同状态的 socket 管理 4 var $handshake = false; // 判断是否握手 5 6 function __construct($address, $port){ 7 // 建立一个 socket 套接字 8 $this->master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) 9 or die("socket_create() failed"); 10 socket_set_option($this->master, SOL_SOCKET, SO_REUSEADDR, 1) 11 or die("socket_option() failed"); 12 socket_bind($this->master, $address, $port) 13 or die("socket_bind() failed"); 14 socket_listen($this->master, 2) 15 or die("socket_listen() failed"); 16 17 $this->sockets[] = $this->master; 18 19 // debug 20 echo("Master socket : ".$this->master."\n"); 21 22 while(true) { 23 //自动选择来消息的 socket 如果是握手 自动选择主机 24 $write = NULL; 25 $except = NULL; 26 socket_select($this->sockets, $write, $except, NULL); 27 28 foreach ($this->sockets as $socket) { 29 //连接主机的 client 30 if ($socket == $this->master){ 31 $client = socket_accept($this->master); 32 if ($client < 0) { 33 // debug 34 echo "socket_accept() failed"; 35 continue; 36 } else { 37 //connect($client); 38 array_push($this->sockets, $client); 39 echo "connect client\n"; 40 } 41 } else { 42 $bytes = @socket_recv($socket,$buffer,2048,0); 43 print_r($buffer); 44 if($bytes == 0) return; 45 if (!$this->handshake) { 46 // 如果没有握手,先握手回应 47 $this->doHandShake($socket, $buffer); 48 echo "shakeHands\n"; 49 } else { 50 51 // 如果已经握手,直接接受数据,并处理 52 $buffer = $this->decode($buffer); 53 //process($socket, $buffer); 54 echo "send file\n"; 55 } 56 } 57 } 58 } 59 } 60 61 function dohandshake($socket, $req) 62 { 63 // 获取加密key 64 $acceptKey = $this->encry($req); 65 $upgrade = "HTTP/1.1 101 Switching Protocols\r\n" . 66 "Upgrade: websocket\r\n" . 67 "Connection: Upgrade\r\n" . 68 "Sec-WebSocket-Accept: " . $acceptKey . "\r\n" . 69 "\r\n"; 70 71 echo "dohandshake ".$upgrade.chr(0); 72 // 写入socket 73 socket_write($socket,$upgrade.chr(0), strlen($upgrade.chr(0))); 74 // 标记握手已经成功,下次接受数据采用数据帧格式 75 $this->handshake = true; 76 } 77 78 79 function encry($req) 80 { 81 $key = $this->getKey($req); 82 $mask = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 83 84 return base64_encode(sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true)); 85 } 86 87 function getKey($req) 88 { 89 $key = null; 90 if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $req, $match)) { 91 $key = $match[1]; 92 } 93 return $key; 94 } 95 96 // 解析数据帧 97 function decode($buffer) 98 { 99 $len = $masks = $data = $decoded = null;100 $len = ord($buffer[1]) & 127;101 102 if ($len === 126) {103 $masks = substr($buffer, 4, 4);104 $data = substr($buffer, 8);105 } else if ($len === 127) {106 $masks = substr($buffer, 10, 4);107 $data = substr($buffer, 14);108 } else {109 $masks = substr($buffer, 2, 4);110 $data = substr($buffer, 6);111 }112 for ($index = 0; $index < strlen($data); $index++) {113 $decoded .= $data[$index] ^ $masks[$index % 4];114 }115 return $decoded;116 }117 118 // 返回帧信息处理119 function frame($s) 120 {121 $a = str_split($s, 125);122 if (count($a) == 1) {123 return "\x81" . chr(strlen($a[0])) . $a[0];124 }125 $ns = "";126 foreach ($a as $o) {127 $ns .= "\x81" . chr(strlen($o)) . $o;128 }129 return $ns;130 }131 132 // 返回数据133 function send($client, $msg)134 {135 $msg = $this->frame($msg);136 socket_write($client, $msg, strlen($msg));137 }138 } 测试 $ws = new WS("127.0.0.1",2000);