老雷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[email protected]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
});

原文地址:https://www.cnblogs.com/lrjxgl/p/11125490.html

时间: 2024-11-08 18:27:05

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

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

老雷socket编程之常见网络协议 1.ip IP协议是将多个包交换网络连接起来,它在源地址和目的地址之间传送一种称之为数据包的东西, 它还提供对数据大小的重新组装功能,以适应不同网络对包大小的要求. 2.TCP 传输控制协议 TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的.可靠的.基于字节流的传输层通信协议. TCP建立一个连接需要三次握手,而终止一个连接要经过四次握手 当主动方发出SYN连接请求后,等待对方回答SYN+ACK [1] ,并最

socket编程之二:两种链接类型upd和upd

前面一篇文章说到了一些计算机网络的基础知识,引入了socket,从这节开始,就进入正题了. 一 概率 TCP:Transimission Control Protocol传输控制协议. UPD:User Datagram Protocol用户数据包协议. 两者都属于上一篇文章说的OSI模型中的第四层--传输层的协议. 两者相比: TCP协议面向连接,UDP协议面向非连接:(链接) TCP协议传输速度慢,UDP协议传输速度快:(速度) TCP有丢包重传机制,UDP没有:(重传) TCP协议保证数据

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

13.3 Socket编程之UDP协议 UDP协议和TCP协议都是Socket编程的协议,但是与TCP协议不同,UDP协议并不提供超时重传,出错重传等功能,也就是说其是不可靠的协议.UDP适用于一次只传送少量数据.对可靠性要求不高的应用环境.既然 UDP 是一种不可靠的网络协议,那么还有什么使用价值或必要呢?其实不然,在有些情况下UDP协议可能会变得非常有用.因为UDP具有TCP所望尘莫及的速度优势.虽然TCP协议中植入了各种安全保障功能,但是在实际执行的过程中会占用大量的系统开销,无疑使速度受

Socket编程之Select模型

echoserver_select.c 1 #include <apue.h> 2 3 #define BACKLOG 10 4 #define PORT 8080 5 #define MAXCLIENT 20 6 #define LEN_BUF 255 7 8 fd_set grset; 9 int maxfd; 10 struct client 11 { 12 char ip[16]; 13 unsigned short port; 14 int connfd; 15 }; 16 17 s

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

php的socket编程算是比较难以理解的东西吧,不过,我们只要理解socket几个函数之间的关系,以及它们所扮演的角色,那么理解起来应该不是很难了,在笔者看来,socket编程,其实就是建立一个网络服务的客户端和服务端,这和mysql的客户端和服务端是一样的,你只要理解mysql的客户端和服务端是怎么一回事,你就应该能够理解下面我要讲的东西吧. 关于socket编程所涉及到的网络协议,什么TCP啊,UDP啊,什么socket三次握手等等,这些网络协议网上有很详细的解释,这里不讲,只截个sock

socket编程之select(),poll(),epoll()

socket编程,通信 client端  socket() ----->connect() ------->recv() -----> close(); server端 socket() ----->bind()  ------> listen() ---->accept() ------>send() ------->close(); 1> socket(int family,int type,int protocol); family ->协

MAC OS/IOS Socket编程之AsyncSocket

最近在做越狱状态下的IOS后台监控软件,后台守护进程将取得的数据通过TCP发送到服务器,通过查阅资料了解到IOSsocket编程推荐使用公开的类库AsyncSocket,使用该类库极大的方便了socket的操作,下面开始介绍. 1.Socket简介 socket是应用层与TCP/IP协议簇通信的中间软件抽象层,它是一组接口.Socket描述了一个IP.端口对,它简化了程序员的操作,知道对方的IP以及PORT就可以给对方发送消息,再由服务器端来处理发送的这些消息.所以,Socket一定包含了通信的

socket编程之TCP/UDP

目标: 1.编写TCP服务端客户端,实现客户端发送数据,服务端接收打印 2.采用OOP方式编写TCP服务端客户端,实现客户端发送数据,服务端添加时间戳,返回给客户端 3.采用OOP方式编写UDP服务端客户端,实现客户端发送数据,服务端添加时间戳,返回给客户端. 1.编写TCP服务端客户端,实现客户端发送数据,服务端接收打印 服务端代码如下: [[email protected] python]# cat tcpserver.py #!/usr/bin/env python # -*- codin

计算机网络Socket编程之TCP协议

>TCP协议位于传输层,是一种面向连接的可靠的传输协议 >socket(套接字):是IP地址与端口号的统称 >套接字的基本结构 struct sockaddr   这个结构用来存储套接字地址 结构体的定义 struct sockaddr { unsigned short sa_family; /* address族, AF_xxx */ har sa_data[14]; /* 14 bytes的协议地址 */ }; sa_family    一般来说,都是"AFINET&quo