老雷socket编程之websocket实现

我们主要实现私聊和群聊两个功能,要在web端实现想微信QQ那样的即时通讯的功能,我们需要了解一下websocket。
websocket是一种可以双向通讯的长连接协议,http是获取完数据就关闭,websocket则可以一直连接,就像铺了一条管道一样,水可以一直流着。

一、websocket前端

    var ws = new WebSocket("ws://127.0.0.1.com:8282");
ws.onopen=function(){
var msg = JSON.stringify({
type: "login",
content: "login"
});
ws.send(msg);
} ws.onmessage = function (e){
console.log(e);
//服务器发送的内容
var res = JSON.parse(e.data);
switch(res.type){
case "login": break;
case "pm": break;
case "groupPm": break; }
}
ws.onerror=function (e){
console.log(e);
}
ws.onclose=function (e){
console.log(e);
}

二、服务端


客户端发送http请求,带上Sec-WebSocket-Key,
服务端握手 加密key,发送给客户端。
双方能进行交流。

发送接收消息需要进行打包encode 解包decode。

<?php

class SocketService
{
public $host="tcp://0.0.0.0:8000";
private $address;
private $port;
private $_sockets;
public $clients;
public $maxid=1000;
public function __construct($address = '', $port='')
{
if(!empty($address)){
$this->address = $address;
}
if(!empty($port)) {
$this->port = $port;
}
} public function onConnect($client_id){
echo "Client client_id:{$client_id} \n"; } public function onMessage($client_id,$msg){
//发给所有的
foreach($this->clients as $kk=>$cc){
if($kk>0){
$this->send($cc, $msg);
}
}
} public function onClose($client_id){
echo "$client_id close \n";
} public function service(){
//获取tcp协议号码。
$tcp = getprotobyname("tcp");
$sock = stream_socket_server($this->host, $errno, $errstr);; if(!$sock)
{
throw new Exception("failed to create socket: ".socket_strerror($sock)."\n");
}
stream_set_blocking($sock,0);
$this->_sockets = $sock;
echo "listen on $this->address $this->host ... \n";
} public function run(){
$this->service();
$this->clients[] = $this->_sockets;
while (true){
$changes = $this->clients;
//$write = NULL;
//$except = NULL;
stream_select($changes, $write, $except, NULL);
foreach ($changes as $key => $_sock){
if($this->_sockets == $_sock){ //判断是不是新接入的socket
if(($newClient = stream_socket_accept($_sock)) === false){
unset($this->clients[$key]);
continue;
}
$line = trim(stream_socket_recvfrom($newClient, 1024));
//握手
$this->handshaking($newClient, $line);
$this->maxid++;
$this->clients[$this->maxid] = $newClient;
$this->onConnect($this->maxid);
} else {
$res=@stream_socket_recvfrom($_sock, 2048);
//客户端主动关闭
if(strlen($res) < 9) {
stream_socket_shutdown($this->clients[$key],STREAM_SHUT_RDWR);
unset($this->clients[$key]);
$this->onClose($key);
}else{
//解密
$msg = $this->decode($res);
$this->onMessage($key,$msg);
} }
}
}
} /**
* 握手处理
* @param $newClient socket
* @return int 接收到的信息
*/
public function handshaking($newClient, $line){ $headers = array();
$lines = preg_split("/\r\n/", $line);
foreach($lines as $line)
{
$line = chop($line);
if(preg_match('/\A(\S+): (.*)\z/', $line, $matches))
{
$headers[$matches[1]] = $matches[2];
}
}
$secKey = $headers['Sec-WebSocket-Key'];
$secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
$upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
"Upgrade: websocket\r\n" .
"Connection: Upgrade\r\n" .
"WebSocket-Origin: $this->address\r\n" .
"WebSocket-Location: ws://$this->address:$this->port/websocket/websocket\r\n".
"Sec-WebSocket-Accept:$secAccept\r\n\r\n";
return stream_socket_sendto($newClient, $upgrade);
} /**
* 发送数据
* @param $newClinet 新接入的socket
* @param $msg 要发送的数据
* @return int|string
*/
public function send($newClinet, $msg){
$msg = $this->encode($msg);
stream_socket_sendto($newClinet, $msg);
}
/**
* 解析接收数据
* @param $buffer
* @return null|string
*/
public function decode($buffer){
$len = $masks = $data = $decoded = null;
$len = ord($buffer[1]) & 127;
if ($len === 126) {
$masks = substr($buffer, 4, 4);
$data = substr($buffer, 8);
} else if ($len === 127) {
$masks = substr($buffer, 10, 4);
$data = substr($buffer, 14);
} else {
$masks = substr($buffer, 2, 4);
$data = substr($buffer, 6);
}
for ($index = 0; $index < strlen($data); $index++) {
$decoded .= $data[$index] ^ $masks[$index % 4];
}
return $decoded;
} /**
*打包消息
**/
public function encode($buffer) {
$first_byte="\x81";
$len=strlen($buffer);
if ($len <= 125) {
$encode_buffer = $first_byte . chr($len) . $buffer;
} else {
if ($len <= 65535) {
$encode_buffer = $first_byte . chr(126) . pack("n", $len) . $buffer;
} else {
$encode_buffer = $first_byte . chr(127) . pack("xxxxN", $len) . $buffer;
}
}
return $encode_buffer;
} /**
* 关闭socket
*/
public function close(){
return socket_close($this->_sockets);
}
} $sock = new SocketService('127.0.0.1','9000');
$sock->run();

三、常见应用

1.聊天室、群聊 实现类似QQ群的web版本

2.im私聊、客服 实现类似qq聊天,和即时客服交流

3.消息推送 建立即时的web消息推送

课后练习
实现聊天室 跟 个人聊天
前端格式

var msg = JSON.stringify({
type: "login",
content: "login"
});
var msg = JSON.stringify({
type: "group",
content: "login",
gid:123
}); var msg = JSON.stringify({
type: "pm",
content: "login",
uid:123
});

老雷socket编程之websocket实现的更多相关文章

  1. 老雷socket编程之PHP利用socket扩展实现聊天服务

    老雷socket编程之PHP利用socket扩展实现聊天服务 socket聊天服务原理 PHP有两个socket的扩展 sockets和streamssockets socket_create(AF_ ...

  2. 老雷socket编程之认识常用协议

    老雷socket编程之常见网络协议 1.ip IP协议是将多个包交换网络连接起来,它在源地址和目的地址之间传送一种称之为数据包的东西, 它还提供对数据大小的重新组装功能,以适应不同网络对包大小的要求. ...

  3. [深入浅出WP8.1(Runtime)]Socket编程之UDP协议

    13.3 Socket编程之UDP协议 UDP协议和TCP协议都是Socket编程的协议,但是与TCP协议不同,UDP协议并不提供超时重传,出错重传等功能,也就是说其是不可靠的协议.UDP适用于一次只 ...

  4. iPhone socket 编程之BSD Socket篇

    iPhone socket 编程之BSD Socket篇 收藏在进行iPhone网络通讯程序的开发中,不可避免的要利用Socket套接字.iPhone提供了Socket网络编程的接口CFSocket, ...

  5. PHP Socket 编程之9个主要函数的使用之测试案例

    php的socket编程算是比较难以理解的东西吧,不过,我们只要理解socket几个函数之间的关系,以及它们所扮演的角色,那么理解起来应该不是很难了,在笔者看来,socket编程,其实就是建立一个网络 ...

  6. Linux系统编程(33)—— socket编程之TCP程序的错误处理

    上一篇的例子不仅功能简单,而且简单到几乎没有什么错误处理,我们知道,系统调用不能保证每次都成功,必须进行出错处理,这样一方面可以保证程序逻辑正常,另一方面可以迅速得到故障信息. 为使错误处理的代码不影 ...

  7. Python socket编程之二:【struct.pack】&【struct.unpack】

    import struct """通过 socket 的 send 和 recv 只能传输 str 格式的数据""" "" ...

  8. Linux系统编程(37)—— socket编程之UDP服务器与客户端

    典型的UDP客户端/服务器通讯过程: 编写UDP Client程序的步骤 1.初始化sockaddr_in结构的变量,并赋值.这里使用"8888"作为连接的服务程序的端口,从命令行 ...

  9. Linux系统编程(35)—— socket编程之TCP服务器的并发处理

    我们知道,服务器通常是要同时服务多个客户端的,如果我们运行上一篇实现的server和client之后,再开一个终端运行client试试,新的client就不能能得到服务了.因为服务器之支持一个连接. ...

随机推荐

  1. Python 标准库 —— string

    1. maketrans()/translate() maketrans(frm, to) -> string, 建立从字符串 frm 到 to 的映射表(字符串的形式): translate( ...

  2. Python Numpy基础教程

    Python Numpy基础教程 本文是一个关于Python numpy的基础学习教程,其中,Python版本为Python 3.x 什么是Numpy Numpy = Numerical + Pyth ...

  3. 使用WinPcap获取网卡MAC地址

    关键字:WinPcap 网卡 MAC地址 作者:txw1958 在WpdPack_4_1_2\WpdPack\Examples-remote\PacketDriver\GetMacAddress\目录 ...

  4. Python科学计算(两)——时域波形和正弦信号的频谱

    Python科学计算(两)-- 时域和频域波形为正弦波形信号生成.计算和显示 # -*- coding: utf-8 -*- import numpy as np import matplotlib. ...

  5. WPF图片放大后模糊的解决方法

    原文:WPF图片放大后模糊的解决方法 WPF中显示图片的方式很多,可以用Image控件来显示图像,或者直接设置一个控件的Background.图片的放大也很简单,直接设置显示图片的控件的Width和H ...

  6. opencart框架分析与概况

    1. Opencart的系统架构 1.1. 架构分析 Opencart是有自己的开发架构的,其架构的核心都在system\engine 下,访问控制统一用 根目录下的 index.php 来协调. R ...

  7. WCF学习目录

    WCF 基本 WCF概念 WCF配置文件详解 多个不同类对象传输思路 WCF 大文件传输配置 Uri ? & = 毫秒数据字符串转换为DateTime POST请求——HttpWebReque ...

  8. TinyMCE插件CodeSample前后端应用

    后端使用插件CodeSample方法: <html> <textarea id="editor_id" name="content" plac ...

  9. mariadb 允许远程访问

    进入MariaDB服务器,将mysql.user的host字段的值改为%就表示在任何客户端机器上能以root用户登录到mysql服务器,建议在开发时设为%. 1 MariaDB [(none)]> ...

  10. G1 安装 Linux Debian system

    开发Android第五步,G1 安装 Linux Debian system 在 G1 上安装 Linux Debian system (Debian ARMEL) 要具备以下条件: (a) 最好是 ...