浅谈php中使用websocket
在PHP中,開發者需要考慮的東西比較多,從socket的連接、建立、綁定、監聽等都需要開發者自己去操作完成,對于初學者來說,難度方面也挺大的,所以本文的思路如下:
1、socket協議的簡介
2、介紹client與server之間的連接原理
3、PHP中建立socket的過程講解
4、用一個聊天室作為實例詳細講解在PHP中如何使用socket
一、socket協議的簡介
WebSocket是什么,有什么優點
WebSocket是一個持久化的協議,這是相對于http非持久化來說的。
舉個簡單的例子,http1.0的生命周期是以request作為界定的,也就是一個request,一個response,對于http來說,本次client與server的會話到此結束;而在http1.1中,稍微有所改進,即添加了keep-alive,也就是在一個http連接中可以進行多個request請求和多個response接受操作。然而在實時通信中,并沒有多大的作用,http只能由client發起請求,server才能返回信息,即server不能主動向client推送信息,無法滿足實時通信的要求。而WebSocket可以進行持久化連接,即client只需進行一次握手,成功后即可持續進行數據通信,值得關注的是WebSocket實現client與server之間全雙工通信,即server端有數據更新時可以主動推送給client端。
二、介紹client與server之間的socket連接原理
1、下面是一個演示client和server之間建立WebSocket連接時握手部分
?
2、client與server建立socket時握手的會話內容,即request與response
a、client建立WebSocket時向服務器端請求的信息
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket //告訴服務器現在發送的是WebSocket協議
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== //是一個Base64 encode的值,這個是瀏覽器隨機生成的,用于驗證服務器端返回數據是否是WebSocket助理
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
b、服務器獲取到client請求的信息后,根據WebSocket協議對數據進行處理并返回,其中要對Sec-WebSocket-Key進行加密等操作
HTTP/1.1 101 Switching Protocols
Upgrade: websocket //依然是固定的,告訴客戶端即將升級的是Websocket協議,而不是mozillasocket,lurnarsocket或者shitsocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= //這個則是經過服務器確認,并且加密過后的 Sec-WebSocket-Key,也就是client要求建立WebSocket驗證的憑證
Sec-WebSocket-Protocol: chat
?
3、socket建立連接原理圖:
?
?
三、PHP中建立socket的過程講解
1、在PHP中,client與server之間建立socket通信,首先在PHP中創建socket并監聽端口信息,代碼如下:
| 1 2 3 4 5 6 7 8 | //傳相應的IP與端口進行創建socket操作 function?WebSocket($address,$port){ ????$server?= socket_create(AF_INET, SOCK_STREAM, SOL_TCP); ????socket_set_option($server, SOL_SOCKET, SO_REUSEADDR, 1);//1表示接受所有的數據包 ????socket_bind($server,?$address,?$port); ????socket_listen($server); ????return?$server; } |
2、設計一個循環掛起WebSocket通道,進行數據的接收、處理和發送
| 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 | //對創建的socket循環進行監聽,處理數據 function?run(){ ????//死循環,直到socket斷開 ????while(true){ ????????$changes=$this->sockets; ????????$write=NULL; ????????$except=NULL; ????????? ????????/* ????????//這個函數是同時接受多個連接的關鍵,我的理解它是為了阻塞程序繼續往下執行。 ????????socket_select ($sockets, $write = NULL, $except = NULL, NULL); ? ????????$sockets可以理解為一個數組,這個數組中存放的是文件描述符。當它有變化(就是有新消息到或者有客戶端連接/斷開)時,socket_select函數才會返回,繼續往下執行。 ????????$write是監聽是否有客戶端寫數據,傳入NULL是不關心是否有寫變化。 ????????$except是$sockets里面要被排除的元素,傳入NULL是”監聽”全部。 ????????最后一個參數是超時時間 ????????如果為0:則立即結束 ????????如果為n>1: 則最多在n秒后結束,如遇某一個連接有新動態,則提前返回 ????????如果為null:如遇某一個連接有新動態,則返回 ????????*/ ????????socket_select($changes,$write,$except,NULL); ????????foreach($changes?as?$sock){ ????????????? ????????????//如果有新的client連接進來,則 ????????????if($sock==$this->master){ ? ????????????????//接受一個socket連接 ????????????????$client=socket_accept($this->master); ? ????????????????//給新連接進來的socket一個唯一的ID ????????????????$key=uniqid(); ????????????????$this->sockets[]=$client;??//將新連接進來的socket存進連接池 ????????????????$this->users[$key]=array( ????????????????????'socket'=>$client,??//記錄新連接進來client的socket信息 ????????????????????'shou'=>false???????//標志該socket資源沒有完成握手 ????????????????); ????????????//否則1.為client斷開socket連接,2.client發送信息 ????????????}else{ ????????????????$len=0; ????????????????$buffer=''; ????????????????//讀取該socket的信息,注意:第二個參數是引用傳參即接收數據,第三個參數是接收數據的長度 ????????????????do{ ????????????????????$l=socket_recv($sock,$buf,1000,0); ????????????????????$len+=$l; ????????????????????$buffer.=$buf; ????????????????}while($l==1000); ? ????????????????//根據socket在user池里面查找相應的$k,即健ID ????????????????$k=$this->search($sock); ? ????????????????//如果接收的信息長度小于7,則該client的socket為斷開連接 ????????????????if($len<7){ ????????????????????//給該client的socket進行斷開操作,并在$this->sockets和$this->users里面進行刪除 ????????????????????$this->send2($k); ????????????????????continue; ????????????????} ????????????????//判斷該socket是否已經握手 ????????????????if(!$this->users[$k]['shou']){ ????????????????????//如果沒有握手,則進行握手處理 ????????????????????$this->woshou($k,$buffer); ????????????????}else{ ????????????????????//走到這里就是該client發送信息了,對接受到的信息進行uncode處理 ????????????????????$buffer?=?$this->uncode($buffer,$k); ????????????????????if($buffer==false){ ????????????????????????continue; ????????????????????} ????????????????????//如果不為空,則進行消息推送操作 ????????????????????$this->send($k,$buffer); ????????????????} ????????????} ????????} ????????? ????} ????? } |
3、以上服務器端完成的WebSocket的前期工作后,就等著client連接進行,client創建WebSocket很簡單,代碼如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | var?ws =?new?WebSocket("ws://IP:端口"); //握手監聽函數 ws.onopen=function(){ ?????//狀態為1證明握手成功,然后把client自定義的名字發送過去 ????if(so.readyState==1){ ?????????//握手成功后對服務器發送信息 ?????so.send('type=add&ming='+n); ????} } //錯誤返回信息函數 ws.onerror =?function(){ ????console.log("error"); }; //監聽服務器端推送的消息 ws.onmessage =?function?(msg){ ????console.log(msg); } ? //斷開WebSocket連接 ws.onclose =?function(){ ????ws = false; } |
四、聊天室實例代碼
1、PHP部分
| 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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 | <?php error_reporting(E_ALL ^ E_NOTICE); ob_implicit_flush(); ? //地址與接口,即創建socket時需要服務器的IP和端口 $sk=new?Sock('127.0.0.1',8000); ? //對創建的socket循環進行監聽,處理數據 $sk->run(); ? //下面是sock類 class?Sock{ ????public?$sockets;?//socket的連接池,即client連接進來的socket標志 ????public?$users;???//所有client連接進來的信息,包括socket、client名字等 ????public?$master;??//socket的resource,即前期初始化socket時返回的socket資源 ????? ????private?$sda=array();???//已接收的數據 ????private?$slen=array();??//數據總長度 ????private?$sjen=array();??//接收數據的長度 ????private?$ar=array();????//加密key ????private?$n=array(); ????? ????public?function?__construct($address,?$port){ ? ????????//創建socket并把保存socket資源在$this->master ????????$this->master=$this->WebSocket($address,?$port); ? ????????//創建socket連接池 ????????$this->sockets=array($this->master); ????} ????? ????//對創建的socket循環進行監聽,處理數據 ????function?run(){ ????????//死循環,直到socket斷開 ????????while(true){ ????????????$changes=$this->sockets; ????????????$write=NULL; ????????????$except=NULL; ????????????? ????????????/* ????????????//這個函數是同時接受多個連接的關鍵,我的理解它是為了阻塞程序繼續往下執行。 ????????????socket_select ($sockets, $write = NULL, $except = NULL, NULL); ? ????????????$sockets可以理解為一個數組,這個數組中存放的是文件描述符。當它有變化(就是有新消息到或者有客戶端連接/斷開)時,socket_select函數才會返回,繼續往下執行。 ????????????$write是監聽是否有客戶端寫數據,傳入NULL是不關心是否有寫變化。 ????????????$except是$sockets里面要被排除的元素,傳入NULL是”監聽”全部。 ????????????最后一個參數是超時時間 ????????????如果為0:則立即結束 ????????????如果為n>1: 則最多在n秒后結束,如遇某一個連接有新動態,則提前返回 ????????????如果為null:如遇某一個連接有新動態,則返回 ????????????*/ ????????????socket_select($changes,$write,$except,NULL); ????????????foreach($changes?as?$sock){ ????????????????? ????????????????//如果有新的client連接進來,則 ????????????????if($sock==$this->master){ ? ????????????????????//接受一個socket連接 ????????????????????$client=socket_accept($this->master); ? ????????????????????//給新連接進來的socket一個唯一的ID ????????????????????$key=uniqid(); ????????????????????$this->sockets[]=$client;??//將新連接進來的socket存進連接池 ????????????????????$this->users[$key]=array( ????????????????????????'socket'=>$client,??//記錄新連接進來client的socket信息 ????????????????????????'shou'=>false???????//標志該socket資源沒有完成握手 ????????????????????); ????????????????//否則1.為client斷開socket連接,2.client發送信息 ????????????????}else{ ????????????????????$len=0; ????????????????????$buffer=''; ????????????????????//讀取該socket的信息,注意:第二個參數是引用傳參即接收數據,第三個參數是接收數據的長度 ????????????????????do{ ????????????????????????$l=socket_recv($sock,$buf,1000,0); ????????????????????????$len+=$l; ????????????????????????$buffer.=$buf; ????????????????????}while($l==1000); ? ????????????????????//根據socket在user池里面查找相應的$k,即健ID ????????????????????$k=$this->search($sock); ? ????????????????????//如果接收的信息長度小于7,則該client的socket為斷開連接 ????????????????????if($len<7){ ????????????????????????//給該client的socket進行斷開操作,并在$this->sockets和$this->users里面進行刪除 ????????????????????????$this->send2($k); ????????????????????????continue; ????????????????????} ????????????????????//判斷該socket是否已經握手 ????????????????????if(!$this->users[$k]['shou']){ ????????????????????????//如果沒有握手,則進行握手處理 ????????????????????????$this->woshou($k,$buffer); ????????????????????}else{ ????????????????????????//走到這里就是該client發送信息了,對接受到的信息進行uncode處理 ????????????????????????$buffer?=?$this->uncode($buffer,$k); ????????????????????????if($buffer==false){ ????????????????????????????continue; ????????????????????????} ????????????????????????//如果不為空,則進行消息推送操作 ????????????????????????$this->send($k,$buffer); ????????????????????} ????????????????} ????????????} ????????????? ????????} ????????? ????} ????? ????//指定關閉$k對應的socket ????function?close($k){ ????????//斷開相應socket ????????socket_close($this->users[$k]['socket']); ????????//刪除相應的user信息 ????????unset($this->users[$k]); ????????//重新定義sockets連接池 ????????$this->sockets=array($this->master); ????????foreach($this->users?as?$v){ ????????????$this->sockets[]=$v['socket']; ????????} ????????//輸出日志 ????????$this->e("key:$k close"); ????} ????? ????//根據sock在users里面查找相應的$k ????function?search($sock){ ????????foreach?($this->users?as?$k=>$v){ ????????????if($sock==$v['socket']) ????????????return?$k; ????????} ????????return?false; ????} ????? ????//傳相應的IP與端口進行創建socket操作 ????function?WebSocket($address,$port){ ????????$server?= socket_create(AF_INET, SOCK_STREAM, SOL_TCP); ????????socket_set_option($server, SOL_SOCKET, SO_REUSEADDR, 1);//1表示接受所有的數據包 ????????socket_bind($server,?$address,?$port); ????????socket_listen($server); ????????$this->e('Server Started : '.date('Y-m-d H:i:s')); ????????$this->e('Listening on?? : '.$address.' port '.$port); ????????return?$server; ????} ????? ????? ????/* ????* 函數說明:對client的請求進行回應,即握手操作 ????* @$k clien的socket對應的健,即每個用戶有唯一$k并對應socket ????* @$buffer 接收client請求的所有信息 ????*/ ????function?woshou($k,$buffer){ ? ????????//截取Sec-WebSocket-Key的值并加密,其中$key后面的一部分258EAFA5-E914-47DA-95CA-C5AB0DC85B11字符串應該是固定的 ????????$buf??=?substr($buffer,strpos($buffer,'Sec-WebSocket-Key:')+18); ????????$key??= trim(substr($buf,0,strpos($buf,"\r\n"))); ????????$new_key?=?base64_encode(sha1($key."258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true)); ????????? ????????//按照協議組合信息進行返回 ????????$new_message?=?"HTTP/1.1 101 Switching Protocols\r\n"; ????????$new_message?.=?"Upgrade: websocket\r\n"; ????????$new_message?.=?"Sec-WebSocket-Version: 13\r\n"; ????????$new_message?.=?"Connection: Upgrade\r\n"; ????????$new_message?.=?"Sec-WebSocket-Accept: "?.?$new_key?.?"\r\n\r\n"; ????????socket_write($this->users[$k]['socket'],$new_message,strlen($new_message)); ? ????????//對已經握手的client做標志 ????????$this->users[$k]['shou']=true; ????????return?true; ????????? ????} ????? ????//解碼函數 ????function?uncode($str,$key){ ????????$mask?=?array();? ????????$data?=?'';? ????????$msg?= unpack('H*',$str); ????????$head?=?substr($msg[1],0,2);? ????????if?($head?==?'81'?&& !isset($this->slen[$key])) {? ????????????$len=substr($msg[1],2,2); ????????????$len=hexdec($len);//把十六進制的轉換為十進制 ????????????if(substr($msg[1],2,2)=='fe'){ ????????????????$len=substr($msg[1],4,4); ????????????????$len=hexdec($len); ????????????????$msg[1]=substr($msg[1],4); ????????????}else?if(substr($msg[1],2,2)=='ff'){ ????????????????$len=substr($msg[1],4,16); ????????????????$len=hexdec($len); ????????????????$msg[1]=substr($msg[1],16); ????????????} ????????????$mask[] = hexdec(substr($msg[1],4,2));? ????????????$mask[] = hexdec(substr($msg[1],6,2));? ????????????$mask[] = hexdec(substr($msg[1],8,2));? ????????????$mask[] = hexdec(substr($msg[1],10,2)); ????????????$s?= 12; ????????????$n=0; ????????}else?if($this->slen[$key] > 0){ ????????????$len=$this->slen[$key]; ????????????$mask=$this->ar[$key]; ????????????$n=$this->n[$key]; ????????????$s?= 0; ????????} ????????? ????????$e?=?strlen($msg[1])-2; ????????for?($i=$s;?$i<=?$e;?$i+= 2) {? ????????????$data?.=?chr($mask[$n%4]^hexdec(substr($msg[1],$i,2)));? ????????????$n++;? ????????}? ????????$dlen=strlen($data); ????????? ????????if($len?> 255 &&?$len?>?$dlen+intval($this->sjen[$key])){ ????????????$this->ar[$key]=$mask; ????????????$this->slen[$key]=$len; ????????????$this->sjen[$key]=$dlen+intval($this->sjen[$key]); ????????????$this->sda[$key]=$this->sda[$key].$data; ????????????$this->n[$key]=$n; ????????????return?false; ????????}else{ ????????????unset($this->ar[$key],$this->slen[$key],$this->sjen[$key],$this->n[$key]); ????????????$data=$this->sda[$key].$data; ????????????unset($this->sda[$key]); ????????????return?$data; ????????} ????????? ????} ????? ????//與uncode相對 ????function?code($msg){ ????????$frame?=?array();? ????????$frame[0] =?'81';? ????????$len?=?strlen($msg); ????????if($len?< 126){ ????????????$frame[1] =?$len<16?'0'.dechex($len):dechex($len); ????????}else?if($len?< 65025){ ????????????$s=dechex($len); ????????????$frame[1]='7e'.str_repeat('0',4-strlen($s)).$s; ????????}else{ ????????????$s=dechex($len); ????????????$frame[1]='7f'.str_repeat('0',16-strlen($s)).$s; ????????} ????????$frame[2] =?$this->ord_hex($msg); ????????$data?= implode('',$frame);? ????????return?pack("H*",?$data);? ????} ????? ????function?ord_hex($data)? {? ????????$msg?=?'';? ????????$l?=?strlen($data);? ????????for?($i= 0;?$i<$l;?$i++) {? ????????????$msg?.=?dechex(ord($data{$i}));? ????????}? ????????return?$msg;? ????} ????? ????//用戶加入或client發送信息 ????function?send($k,$msg){ ????????//將查詢字符串解析到第二個參數變量中,以數組的形式保存如:parse_str("name=Bill&age=60",$arr) ????????parse_str($msg,$g); ????????$ar=array(); ? ????????if($g['type']=='add'){ ????????????//第一次進入添加聊天名字,把姓名保存在相應的users里面 ????????????$this->users[$k]['name']=$g['ming']; ????????????$ar['type']='add'; ????????????$ar['name']=$g['ming']; ????????????$key='all'; ????????}else{ ????????????//發送信息行為,其中$g['key']表示面對大家還是個人,是前段傳過來的信息 ????????????$ar['nrong']=$g['nr']; ????????????$key=$g['key']; ????????} ????????//推送信息 ????????$this->send1($k,$ar,$key); ????} ????? ????//對新加入的client推送已經在線的client ????function?getusers(){ ????????$ar=array(); ????????foreach($this->users?as?$k=>$v){ ????????????$ar[]=array('code'=>$k,'name'=>$v['name']); ????????} ????????return?$ar; ????} ????? ????//$k 發信息人的socketID $key接受人的 socketID ,根據這個socketID可以查找相應的client進行消息推送,即指定client進行發送 ????function?send1($k,$ar,$key='all'){ ????????$ar['code1']=$key; ????????$ar['code']=$k; ????????$ar['time']=date('m-d H:i:s'); ????????//對發送信息進行編碼處理 ????????$str?=?$this->code(json_encode($ar)); ????????//面對大家即所有在線者發送信息 ????????if($key=='all'){ ????????????$users=$this->users; ????????????//如果是add表示新加的client ????????????if($ar['type']=='add'){ ????????????????$ar['type']='madd'; ????????????????$ar['users']=$this->getusers();????????//取出所有在線者,用于顯示在在線用戶列表中 ????????????????$str1?=?$this->code(json_encode($ar));?//單獨對新client進行編碼處理,數據不一樣 ????????????????//對新client自己單獨發送,因為有些數據是不一樣的 ????????????????socket_write($users[$k]['socket'],$str1,strlen($str1)); ????????????????//上面已經對client自己單獨發送的,后面就無需再次發送,故unset ????????????????unset($users[$k]); ????????????} ????????????//除了新client外,對其他client進行發送信息。數據量大時,就要考慮延時等問題了 ????????????foreach($users?as?$v){ ????????????????socket_write($v['socket'],$str,strlen($str)); ????????????} ????????}else{ ????????????//單獨對個人發送信息,即雙方聊天 ????????????socket_write($this->users[$k]['socket'],$str,strlen($str)); ????????????socket_write($this->users[$key]['socket'],$str,strlen($str)); ????????} ????} ????? ????//用戶退出向所用client推送信息 ????function?send2($k){ ????????$this->close($k); ????????$ar['type']='rmove'; ????????$ar['nrong']=$k; ????????$this->send1(false,$ar,'all'); ????} ????? ????//記錄日志 ????function?e($str){ ????????//$path=dirname(__FILE__).'/log.txt'; ????????$str=$str."\n"; ????????//error_log($str,3,$path); ????????//編碼處理 ????????echo?iconv('utf-8','gbk//IGNORE',$str); ????} } ?> |
2、client部分
| 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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 | <!doctype html> <html> <head> <meta charset="utf-8"> <meta name="viewport"?content="width=device-width, initial-scale=1, user-scalable=no"/> <title>HTML5 websocket 網頁聊天室 javascript php</title> <style type="text/css"> body,p{margin:0px; padding:0px; font-size:14px; color:#333; font-family:Arial, Helvetica, sans-serif;} #ltian,.rin{width:98%; margin:5px auto;} #ltian{border:1px #ccc solid;overflow-y:auto; overflow-x:hidden; position:relative;} #ct{margin-right:111px; height:100%;overflow-y:auto;overflow-x: hidden;} #us{width:110px; overflow-y:auto; overflow-x:hidden; float:right; border-left:1px #ccc solid; height:100%; background-color:#F1F1F1;} #us p{padding:3px 5px; color:#08C; line-height:20px; height:20px; cursor:pointer; overflow:hidden; white-space:nowrap; text-overflow:ellipsis;} #us p:hover,#us p:active,#us p.ck{background-color:#069; color:#FFF;} #us p.my:hover,#us p.my:active,#us p.my{color:#333;background-color:transparent;} button{float:right; width:80px; height:35px; font-size:18px;} input{width:100%; height:30px; padding:2px; line-height:20px; outline:none; border:solid 1px #CCC;} .rin p{margin-right:160px;} .rin span{float:right; padding:6px 5px 0px 5px; position:relative;} .rin span img{margin:0px 3px; cursor:pointer;} .rin span form{position:absolute; width:25px; height:25px; overflow:hidden; opacity:0; top:5px; right:5px;} .rin span input{width:180px; height:25px; margin-left:-160px; cursor:pointer} ? #ct p{padding:5px; line-height:20px;} #ct a{color:#069; cursor:pointer;} #ct span{color:#999; margin-right:10px;} .c2{color:#999;} .c3{background-color:#DBE9EC; padding:5px;} .qp{position:absolute; font-size:12px; color:#666; top:5px; right:130px; text-decoration:none; color:#069;} #ems{position:absolute; z-index:5; display:none; top:0px; left:0px; max-width:230px; background-color:#F1F1F1; border:solid 1px #CCC; padding:5px;} #ems img{width:44px; height:44px; border:solid 1px #FFF; cursor:pointer;} #ems img:hover,#ems img:active{border-color:#A4B7E3;} #ems a{color:#069; border-radius:2px; display:inline-block; margin:2px 5px; padding:1px 8px; text-decoration:none; background-color:#D5DFFD;} #ems a:hover,#ems a:active,#ems a.ck{color:#FFF; background-color:#069;} .tc{text-align:center; margin-top:5px;} </style> </head> ? <body> <div id="ltian"> ????<div id="us"?class="jb"></div> ????<div id="ct"></div> ????<a href="javascript:;"?class="qp"?onClick="this.parentNode.children[1].innerHTML=''">清屏</a> </div> <div?class="rin"> ????<button id="sd">發送</button> ????<span><img src="http://www.yxsss.com/ui/sk/t.png"?title="表情"?id="imgbq"><img src="http://www.yxsss.com/ui/sk/e.png"?title="上傳圖片"><form><input type="file"?title="上傳圖片"?id="upimg"></form></span> ????<p><input id="nrong"></p> </div> <div id="ems"><p></p><p?class="tc"></p></div> <script> if(typeof(WebSocket)=='undefined'){ ????alert('你的瀏覽器不支持 WebSocket ,推薦使用Google Chrome 或者 Mozilla Firefox');? } </script> <script src="http://www.yxsss.com/ui/p/a.js"?type="text/javascript"></script> <script> (function(){ ????var?key='all',mkey; ????var?users={}; ????var?url='ws://127.0.0.1:8000'; ????var?so=false,n=false; ????var?lus=A.$('us'),lct=A.$('ct'); ????function?st(){ ????????n=prompt('請給自己取一個響亮的名字:'); ????????n=n.substr(0,16); ????????if(!n){ ????????????return?;??? ????????} ????????//創建socket,注意URL的格式:ws://ip:端口 ????????so=new?WebSocket(url); ????????//握手監聽函數 ????????so.onopen=function(){ ????????????//狀態為1證明握手成功,然后把client自定義的名字發送過去 ????????????if(so.readyState==1){ ????????????????so.send('type=add&ming='+n); ????????????} ????????} ????????? ????????//握手失敗或者其他原因連接socket失敗,則清除so對象并做相應提示操作 ????????so.onclose=function(){ ????????????so=false; ????????????lct.appendChild(A.$$('<p class="c2">退出聊天室</p>')); ????????} ????????? ????????//數據接收監聽,接收服務器推送過來的信息,返回的數據給msg,然后進行顯示 ????????so.onmessage=function(msg){ ????????????eval('var da='+msg.data); ????????????var?obj=false,c=false; ????????????if(da.type=='add'){ ????????????????var?obj=A.$$('<p>'+da.name+'</p>'); ????????????????lus.appendChild(obj); ????????????????cuser(obj,da.code); ????????????????obj=A.$$('<p><span>['+da.time+']</span>歡迎<a>'+da.name+'</a>加入</p>'); ????????????????c=da.code; ????????????}else?if(da.type=='madd'){ ????????????????mkey=da.code; ????????????????da.users.unshift({'code':'all','name':'大家'}); ????????????????for(var?i=0;i<da.users.length;i++){ ????????????????????var?obj=A.$$('<p>'+da.users[i].name+'</p>'); ????????????????????lus.appendChild(obj); ????????????????????if(mkey!=da.users[i].code){ ????????????????????????cuser(obj,da.users[i].code); ????????????????????}else{ ????????????????????????obj.className='my'; ????????????????????????document.title=da.users[i].name; ????????????????????} ????????????????} ????????????????obj=A.$$('<p><span>['+da.time+']</span>歡迎'+da.name+'加入</p>'); ????????????????users.all.className='ck'; ????????????} ????????????? ????????????if(obj==false){ ????????????????if(da.type=='rmove'){ ????????????????????var?obj=A.$$('<p class="c2"><span>['+da.time+']</span>'+users[da.nrong].innerHTML+'退出聊天室</p>'); ????????????????????lct.appendChild(obj); ????????????????????users[da.nrong].del(); ????????????????????delete?users[da.nrong]; ????????????????}else{ ????????????????????da.nrong=da.nrong.replace(/{\\(\d+)}/g,function(a,b){ ????????????????????????return?'<img src="sk/'+b+'.gif">'; ????????????????????}).replace(/^data\:image\/png;base64\,.{50,}$/i,function(a){ ????????????????????????return?'<img src="'+a+'">'; ????????????????????}); ????????????????????//da.code 發信息人的code ????????????????????if(da.code1==mkey){ ????????????????????????obj=A.$$('<p class="c3"><span>['+da.time+']</span><a>'+users[da.code].innerHTML+'</a>對我說:'+da.nrong+'</p>'); ????????????????????????c=da.code; ????????????????????}else?if(da.code==mkey){ ????????????????????????if(da.code1!='all') ????????????????????????obj=A.$$('<p class="c3"><span>['+da.time+']</span>我對<a>'+users[da.code1].innerHTML+'</a>說:'+da.nrong+'</p>'); ????????????????????????else ????????????????????????obj=A.$$('<p><span>['+da.time+']</span>我對<a>'+users[da.code1].innerHTML+'</a>說:'+da.nrong+'</p>'); ????????????????????????c=da.code1; ????????????????????}else?if(da.code==false){ ????????????????????????obj=A.$$('<p><span>['+da.time+']</span>'+da.nrong+'</p>'); ????????????????????}else?if(da.code1){ ????????????????????????obj=A.$$('<p><span>['+da.time+']</span><a>'+users[da.code].innerHTML+'</a>對'+users[da.code1].innerHTML+'說:'+da.nrong+'</p>'); ????????????????????????c=da.code; ????????????????????} ????????????????} ????????????} ????????????if(c){ ????????????????????obj.children[1].onclick=function(){ ????????????????????????users[c].onclick(); ????????????????????} ????????????????} ????????????lct.appendChild(obj); ????????????lct.scrollTop=Math.max(0,lct.scrollHeight-lct.offsetHeight); ????????????? ????????} ????} ????A.$('sd').onclick=function(){ ????????if(!so){ ?????????????return?st(); ????????} ????????var?da=A.$('nrong').value.trim(); ????????if(da==''){ ????????????alert('內容不能為空'); ????????????return?false;?? ????????} ????????A.$('nrong').value=''; ????????so.send('nr='+esc(da)+'&key='+key); ????} ????A.$('nrong').onkeydown=function(e){ ????????var?e=e||event; ????????if(e.keyCode==13){ ????????????A.$('sd').onclick(); ????????} ????} ????function?esc(da){ ????????da=da.replace(/</g,'<').replace(/>/g,'>').replace(/\"/g,'"'); ????????return?encodeURIComponent(da); ????} ????function?cuser(t,code){ ????????users[code]=t; ????????t.onclick=function(){ ????????????t.parentNode.children.rcss('ck',''); ????????????t.rcss('','ck'); ????????????key=code; ????????} ????} ????A.$('ltian').style.height=(document.documentElement.clientHeight - 70)+'px'; ????st(); ????? ? ????var?bq=A.$('imgbq'),ems=A.$('ems'); ????var?l=80,r=4,c=5,s=0,p=Math.ceil(l/(r*c)); ????var?pt='sk/'; ????bq.onclick=function(e){ ????????var?e=e||event; ????????if(!so){ ?????????????return?st(); ????????} ????????ems.style.display='block'; ????????document.onclick=function(){ ????????????gb();?? ????????} ????????ct(); ????????try{e.stopPropagation();}catch(o){} ????} ????? ????for(var?i=0;i<p;i++){ ????????var?a=A.$$('<a href="javascript:;">'+(i+1)+'</a>'); ????????ems.children[1].appendChild(a); ????????ef(a,i); ????} ????ems.children[1].children[0].className='ck'; ????? ????function?ct(){ ????????var?wz=bq.weiz(); ????????with(ems.style){ ????????????top=wz.y-242+'px'; ????????????left=wz.x+bq.offsetWidth-235+'px'; ????????} ????} ????????? ????function?ef(t,i){ ????????t.onclick=function(e){ ????????????var?e=e||event; ????????????s=i*r*c; ????????????ems.children[0].innerHTML=''; ????????????hh(); ????????????this.parentNode.children.rcss('ck',''); ????????????this.rcss('','ck'); ????????????try{e.stopPropagation();}catch(o){} ????????} ????} ????? ????function?hh(){ ????????var?z=Math.min(l,s+r*c); ????????for(var?i=s;i<z;i++){ ????????????var?a=A.$$('<img src="'+pt+i+'.gif">'); ????????????hh1(a,i); ????????????ems.children[0].appendChild(a); ????????} ????????ct(); ????} ????? ????function?hh1(t,i){ ????????t.onclick=function(e){ ????????????var?e=e||event; ????????????A.$('nrong').value+='{\\'+i+'}'; ????????????if(!e.ctrlKey){ ????????????????gb(); ????????????} ????????????try{e.stopPropagation();}catch(o){} ????????} ????} ????? ????function?gb(){ ????????ems.style.display=''; ????????A.$('nrong').focus(); ????????document.onclick=''; ????} ????hh(); ????A.on(window,'resize',function(){ ????????A.$('ltian').style.height=(document.documentElement.clientHeight - 70)+'px'; ????????ct(); ????})? ? ????var?fimg=A.$('upimg'); ????var?img=new?Image(); ????var?dw=400,dh=300; ????A.on(fimg,'change',function(ev){ ????????if(!so){ ????????????st(); ????????????return?false; ????????} ????????if(key=='all'){ ????????????alert('由于資源限制 發圖只能私聊'); ????????????return?false;?? ????????} ????????var?f=ev.target.files[0]; ????????if(f.type.match('image.*')){ ????????????var?r =?new?FileReader(); ????????????r.onload =?function(e){ ????????????????img.setAttribute('src',e.target.result); ????????????}; ????????????r.readAsDataURL(f); ????????} ????}); ????img.onload=function(){ ????????ih=img.height,iw=img.width; ????????if(iw/ih > dw/dh && iw > dw){ ????????????ih=ih/iw*dw; ????????????iw=dw; ????????}else?if(ih > dh){ ????????????iw=iw/ih*dh; ????????????ih=dh; ????????} ????????var?rc = A.$$('canvas'); ????????var?ct = rc.getContext('2d'); ????????rc.width=iw; ????????rc.height=ih; ????????ct.drawImage(img,0,0,iw,ih); ????????var?da=rc.toDataURL(); ????????so.send('nr='+esc(da)+'&key='+key); ????} ????? })(); </script> </body> </html> |
參考連接:http://www.jb51.net/article/48019.htm,http://blog.sina.com.cn/s/blog_bf397e780102w25k.html
總結
以上是生活随笔為你收集整理的浅谈php中使用websocket的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 千层糕家常做法?
- 下一篇: 市盈率是负值什么意思