php websocket 写一个简易聊天室

1.client.html

<!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:;" rel="external nofollow" rel="external nofollow" 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() {
            var Arr1 = ["聪明的", "狡猾的", "可爱的", "美丽的", "狡猾的", "善良的", "帅气的", "逗比的"];
            var Arr2 = ["大灰狼", "小白兔", "母老虎", "外星人", "皮卡丘", "HelloKitty", "吴亦凡", "薛之谦"];
            var ran1 = Math.floor(Math.random() * Arr1.length + 1) - 1;
            var ran2 = Math.floor(Math.random() * Arr2.length + 1) - 1;
            var n = Arr1[ran1] + Arr2[ran2];
            //以上五行是用来随机生成用户昵称的方法,参考一下 ,如果想自定义用户名可以将以上五行注释,然后以下两行取消注释
            //n=prompt('请给自己取一个霸气的名字:');
            //n=n.substr(0,16);
            //console.log(n);
            if (!n) {
                return;
            }
            so = new WebSocket(url);
            so.onopen = function () {
                if (so.readyState == 1) {
                    so.send('type=add&ming=' + n);
                }
            }

            so.onclose = function () {
                so = false;
                lct.appendChild(A.$$('<p class="c2">退出聊天室</p>'));
            }

            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 + '.jpg">';
                        }).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:;" rel="external nofollow" rel="external nofollow" >' + (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 + '.jpg">');
                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>

2.websocket.php

<?php
error_reporting(E_ALL ^ E_NOTICE);
ob_implicit_flush();

$sk = new Sock('127.0.0.1', 8000);
$sk->run();

class Sock
{
    public $sockets;
    public $users;
    public $master;

    private $sda = array();//已接收的数据
    private $slen = array();//数据总长度
    private $sjen = array();//接收数据的长度
    private $ar = array();//加密key
    private $n = array();

    public function __construct($address, $port)
    {
        $this->master = $this->WebSocket($address, $port);
        $this->sockets = array($this->master);
    }


    function run()
    {
        while (true) {
            $changes = $this->sockets;
            $write = NULL;
            $except = NULL;
            socket_select($changes, $write, $except, NULL);
            foreach ($changes as $sock) {
                if ($sock == $this->master) {
                    $client = socket_accept($this->master);
                    $key = uniqid();
                    $this->sockets[] = $client;
                    $this->users[$key] = array(
                        'socket' => $client,
                        'shou' => false
                    );
                } else {
                    $len = 0;
                    $buffer = '';
                    do {
                        $l = socket_recv($sock, $buf, 1000, 0);
                        $len += $l;
                        $buffer .= $buf;
                    } while ($l == 1000);
                    $k = $this->search($sock);
                    if ($len < 7) {
                        $this->send2($k);
                        continue;
                    }
                    if (!$this->users[$k]['shou']) {
                        $this->woshou($k, $buffer);
                    } else {
                        $buffer = $this->uncode($buffer, $k);
                        if ($buffer == false) {
                            continue;
                        }
                        $this->send($k, $buffer);
                    }
                }
            }

        }

    }

    function close($k)
    {
        socket_close($this->users[$k]['socket']);
        unset($this->users[$k]);
        $this->sockets = array($this->master);
        foreach ($this->users as $v) {
            $this->sockets[] = $v['socket'];
        }
        $this->e("key:$k close");
    }

    function search($sock)
    {
        foreach ($this->users as $k => $v) {
            if ($sock == $v['socket'])
                return $k;
        }
        return false;
    }

    function WebSocket($address, $port)
    {
        $server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        socket_set_option($server, SOL_SOCKET, SO_REUSEADDR, 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;
    }


    function woshou($k, $buffer)
    {
        $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));
        $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;
        }

    }


    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;
    }

    //用户加入
    function send($k, $msg)
    {
        parse_str($msg, $g);
        $ar = array();
        if ($g['type'] == 'add') {
            $this->users[$k]['name'] = $g['ming'];
            $ar['type'] = 'add';
            $ar['name'] = $g['ming'];
            $key = 'all';
        } else {
            $ar['nrong'] = $g['nr'];
            $key = $g['key'];
        }
        $this->send1($k, $ar, $key);
    }

    function getusers()
    {
        $ar = array();
        foreach ($this->users as $k => $v) {
            $ar[] = array('code' => $k, 'name' => $v['name']);
        }
        return $ar;
    }

    //$k 发信息人的code $key接受人的 code
    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;
            if ($ar['type'] == 'add') {
                $ar['type'] = 'madd';
                $ar['users'] = $this->getusers();
                $str1 = $this->code(json_encode($ar));
                socket_write($users[$k]['socket'], $str1, strlen($str1));
                unset($users[$k]);
            }
            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));
        }
    }

    //用户退出
    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);
    }
}

?>

3.Windows+R输入cmd进入命令行,找到以上代码保存的文件目录,输入php websocket.php进行启动
在这里插入图片描述
4.打开Apache,输入http://127.0.0.1/workerman-chat/chat/client.html,路径地址根据自己的存放的地址进行操作。
在这里插入图片描述
5.聊天室引用了一个sk文件夹的表情包,这个sk文件夹可以自己建,放在和上面代码同一个文件夹下即可,放入19张图片,名字依次为1-19,可以发送表情。
6.在进行命令行操作时有可能会报错,需要配置php.ini,搜索sockets,如下图908行所示,把前面的注释去掉。
在这里插入图片描述
源代码在这里下载原文,我仅仅是代码的搬运工…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值