1+ <?php
2+ class WS {
3+ var $ master ;
4+ var $ sockets = array ();
5+ var $ debug = false ;
6+ var $ handshake = false ;
7+
8+ function __construct ($ address , $ port ){
9+ $ this ->master =socket_create (AF_INET , SOCK_STREAM , SOL_TCP ) or die ("socket_create() failed " );
10+ socket_set_option ($ this ->master , SOL_SOCKET , SO_REUSEADDR , 1 ) or die ("socket_option() failed " );
11+ socket_bind ($ this ->master , $ address , $ port ) or die ("socket_bind() failed " );
12+ socket_listen ($ this ->master ,20 ) or die ("socket_listen() failed " );
13+
14+ $ this ->sockets [] = $ this ->master ;
15+ $ this ->say ("Server Started : " .date ('Y-m-d H:i:s ' ));
16+ $ this ->say ("Listening on : " .$ address ." port " .$ port );
17+ $ this ->say ("Master socket : " .$ this ->master ."\n" );
18+
19+ while (true ){
20+ $ socketArr = $ this ->sockets ;
21+ $ write = NULL ;
22+ $ except = NULL ;
23+ socket_select ($ socketArr , $ write , $ except , NULL ); //自动选择来消息的socket 如果是握手 自动选择主机
24+ foreach ($ socketArr as $ socket ){
25+ if ($ socket == $ this ->master ){ //主机
26+ $ client = socket_accept ($ this ->master );
27+ if ($ client < 0 ){
28+ $ this ->log ("socket_accept() failed " );
29+ continue ;
30+ } else {
31+ $ this ->connect ($ client );
32+ }
33+ } else {
34+ $ this ->log ("^^^^ " );
35+ $ bytes = @socket_recv ($ socket ,$ buffer ,2048 ,0 );
36+ $ this ->log ("^^^^ " );
37+ if ($ bytes == 0 ){
38+ $ this ->disConnect ($ socket );
39+ }
40+ else {
41+ if (!$ this ->handshake ){
42+ $ this ->doHandShake ($ socket , $ buffer );
43+ }
44+ else {
45+ $ buffer = $ this ->decode ($ buffer );
46+ $ this ->send ($ socket , $ buffer );
47+ }
48+ }
49+ }
50+ }
51+ }
52+ }
53+
54+ function send ($ client , $ msg ){
55+ $ this ->log ("> " . $ msg );
56+ $ msg = $ this ->frame ($ msg );
57+ socket_write ($ client , $ msg , strlen ($ msg ));
58+ $ this ->log ("! " . strlen ($ msg ));
59+ }
60+ function connect ($ socket ){
61+ array_push ($ this ->sockets , $ socket );
62+ $ this ->say ("\n" . $ socket . " CONNECTED! " );
63+ $ this ->say (date ("Y-n-d H:i:s " ));
64+ }
65+ function disConnect ($ socket ){
66+ $ index = array_search ($ socket , $ this ->sockets );
67+ socket_close ($ socket );
68+ $ this ->say ($ socket . " DISCONNECTED! " );
69+ if ($ index >= 0 ){
70+ array_splice ($ this ->sockets , $ index , 1 );
71+ }
72+ }
73+ function doHandShake ($ socket , $ buffer ){
74+ $ this ->log ("\nRequesting handshake... " );
75+ $ this ->log ($ buffer );
76+ list ($ resource , $ host , $ origin , $ key ) = $ this ->getHeaders ($ buffer );
77+ $ this ->log ("Handshaking... " );
78+ $ upgrade = "HTTP/1.1 101 Switching Protocol \r\n" .
79+ "Upgrade: websocket \r\n" .
80+ "Connection: Upgrade \r\n" .
81+ "Sec-WebSocket-Accept: " . $ this ->calcKey ($ key ) . "\r\n\r\n" ; //必须以两个回车结尾
82+ $ this ->log ($ upgrade );
83+ $ sent = socket_write ($ socket , $ upgrade , strlen ($ upgrade ));
84+ $ this ->handshake =true ;
85+ $ this ->log ("Done handshaking... " );
86+ return true ;
87+ }
88+
89+ function getHeaders ($ req ){
90+ $ r = $ h = $ o = $ key = null ;
91+ if (preg_match ("/GET (.*) HTTP/ " ,$ req ,$ match )) { $ r = $ match [1 ]; }
92+ if (preg_match ("/Host: (.*) \r\n/ " ,$ req ,$ match )) { $ h = $ match [1 ]; }
93+ if (preg_match ("/Origin: (.*) \r\n/ " ,$ req ,$ match )) { $ o = $ match [1 ]; }
94+ if (preg_match ("/Sec-WebSocket-Key: (.*) \r\n/ " ,$ req ,$ match )) { $ key = $ match [1 ]; }
95+ return array ($ r , $ h , $ o , $ key );
96+ }
97+
98+ function calcKey ($ key ){
99+ //基于websocket version 13
100+ $ accept = base64_encode (sha1 ($ key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11 ' , true ));
101+ return $ accept ;
102+ }
103+
104+ function decode ($ buffer ) {
105+ $ len = $ masks = $ data = $ decoded = null ;
106+ $ len = ord ($ buffer [1 ]) & 127 ;
107+
108+ if ($ len === 126 ) {
109+ $ masks = substr ($ buffer , 4 , 4 );
110+ $ data = substr ($ buffer , 8 );
111+ }
112+ else if ($ len === 127 ) {
113+ $ masks = substr ($ buffer , 10 , 4 );
114+ $ data = substr ($ buffer , 14 );
115+ }
116+ else {
117+ $ masks = substr ($ buffer , 2 , 4 );
118+ $ data = substr ($ buffer , 6 );
119+ }
120+ for ($ index = 0 ; $ index < strlen ($ data ); $ index ++) {
121+ $ decoded .= $ data [$ index ] ^ $ masks [$ index % 4 ];
122+ }
123+ return $ decoded ;
124+ }
125+
126+ function frame ($ s ){
127+ $ a = str_split ($ s , 125 );
128+ if (count ($ a ) == 1 ){
129+ return "\x81" . chr (strlen ($ a [0 ])) . $ a [0 ];
130+ }
131+ $ ns = "" ;
132+ foreach ($ a as $ o ){
133+ $ ns .= "\x81" . chr (strlen ($ o )) . $ o ;
134+ }
135+ return $ ns ;
136+ }
137+
138+
139+ function say ($ msg = "" ){
140+ echo $ msg . "\n" ;
141+ }
142+ function log ($ msg = "" ){
143+ if ($ this ->debug ){
144+ echo $ msg . "\n" ;
145+ }
146+ }
147+ }
148+
149+
150+ new WS ('localhost ' , 4000 );
0 commit comments