websocket 多聊天室功能


websocket 类也是在网上找到的。 修改后可以用来创建多房间聊天室。可以发送图片表情,图片,及文字。

分享的代码,已经测试。可正常运行

HTML 端代码

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"/>
    <title>直播课程title>
    <link rel="stylesheet" href="css/base.css">
    <link rel="stylesheet" href="css/zhibo.css">
    <script type="text/javascript" src="js/jquery-1.11.3.min.js">script>
head>
<body class="scope">
    <div style="width: 100%; height:250px;">
        <script type="text/javascript" src="player/sewise.player.min.js">script>
        <script type="text/javascript">
        var videourl='视屏播放地址不用写';
            SewisePlayer.setup({
                server: "vod",
                type: "m3u8",
                autostart: "true",
                videourl: videourl,
                    skin: "vodWhite",
                    title: "三年级预备课程一",
                    claritybutton: "disable",
                    lang: "zh_CN"
            });
        script>
    div>
    
    <div class="log  comment" id="log">
          <div class="luck">
                      <div class="user"><img src="img/user.png">div>
                      <div class="content1"><span class="luckyP">奕佚奕佚span><span class="yuanwang">我是用户1我是用是用户1我是用户1我是用户1我是用户1span>div>
          div>     
             <div class="luck">
                      <div class="user"><img src="img/user.png">div>
                      <div class="content1"><span class="luckyP">莫寒span><span class="yuanwang"><img class="chat_pic" src="img/pic.jpg" alt="">span>div>
             div>
             <div class="luck">
                      <div class="user"><img src="img/user.png">div>
                      <div class="content1"><span class="luckyP">奕佚奕佚span><span class="yuanwang">我是用户1我是用是用户1我是用户1我是用户1我是用户1span>div>
          div>
             <div class="luck">
                      <div class="user"><img src="img/user.png">div>
                      <div class="content1"><span class="luckyP">奕佚奕佚span><span class="yuanwang">我是用户1我是用是用户1我是用户1我是用户1我是用户1span>div>
          div>     
             <div class="luck">
                      <div class="user"><img src="img/user.png">div>
                      <div class="content1"><span class="luckyP">莫寒span><span class="yuanwang"><img class="chat_pic" src="img/150599173575244039.jpg" alt="">span>div>
             div>
              <div class="luck">
                      <div class="user"><img src="img/user.png">div>
                      <div class="content1"><span class="luckyP">奕佚奕佚span><span class="yuanwang">我是用户1我是用是用户1我是用户1我是用户1我是用户1span>div>
          div>     
    div>
    <div class="sendCon">
        <p class="info">
            <span class="emoji"> <input type="button" value=":)">span>
            <span class="placeholder"><img  src="img/pic.jpg" alt=""><input type="file" id="file_img" onchange="preImg(this.id,'yulan_img')">span>
            <span class="text"><input type="text" id="text" placeholder="">span>
            <span class="send"><input type="button" class="btn" value="发送" onclick="sendText()">span>
        p>
        <ul class="emojiCon">ul>
    div>

<script>
    var serverUrl="http://192.168.3.240";  //图片服务器的地址ip
    var url="ws://你自已访问的ip:端口(8000)";
    function link(){
        socket=new WebSocket(url);
        socket.onopen=function(){
            // alert('连接成功')
        };
        socket.onerror=function(errorEvent){
            // alert('error');
        }
        socket.onmessage=function(msg){
            setMsg(msg.data);
            console.log(msg);
        }
        // socket.onclose=function(){setMsg('断开连接')}
    }
    function closeScoket(){
        socket.close();
        socket=null;
    }
    //向页面中实时更新消息数据
    function setMsg(var1){
        var obj=JSON.parse(var1);
        if(obj.type==1){
            $('.log').append('
' +'
' +'
奕佚奕佚'+obj.msg+'
' ); add(); }else{ var img = new Image(); img.src=obj.msg; img.onload=function(){ var isPic=''; //width>30 表示不是表情包图片 if(img.width>30){ isPic=''+obj.msg+'" alt="">'; }else{ isPic=''+obj.msg+'" alt="">' } $('.log').append('
' +'
' +'
奕佚奕佚'+isPic+'
' ); add(); } } } var arr=[1,2,3,4]; //发送文字 function sendText(id){ var json; if($('#'+id).attr('src')==undefined){ json=JSON.stringify({'room':arr[1],'type':'1','msg':$('#text').val()}); if($('#text').val()==""){ $('body').append(pop("消息不能为空")); return; } }else{ json=JSON.stringify({'room':arr[1],'type':'2','msg':$('#'+id).attr('src')}); $('.emojiCon').css('display','none'); } socket.send(json); $('#text').val(''); } //发送图片 function sendPic(){ $.ajax({ url:''+serverUrl+'/test/Home/index/base64_decode', type:'post', data:{'msg':$('#yulan_img').attr('src')}, dataType:'html', success:function(data){ console.log(data); var json=JSON.stringify({'room':arr[3],'type':'2','msg':data}); socket.send(json); $('.mask').remove(); }, error:function(event){ $('body').append(pop('服务器开小差啦~')) } }) } //让讨论区的滚动条始终在底部 让用户立马看到自己的消息 function add(){ var div = document.getElementById('log'); div.scrollTop = div.scrollHeight; } //加载表情包 function afterLoad(){ var html=''; for(var i=0;i<91;i++){ html+='
  • '+(i+1)+'" onclick="sendText(id)" src="'+serverUrl+'/test/Public/img/qq/'+(i+1)+'.gif" alt="">
  • '; } $('.emojiCon').html(html); } //图片预览 function preImg(sourceId, targetId) { if (typeof FileReader === 'undefined') { alert('Your browser does not support FileReader...'); return; } var reader = new FileReader(); reader.onload = function(e) { var img = document.getElementById(targetId); img.src = this.result; var w=img.width; var h=img.height; var winW=$(window).width(); var winH=$(window).height(); var marginTop=parseInt((winH-40-h)/2); var marginLeft=parseInt((winW-w )/2); if(w/h>1.2){ $(img).css('width',winW).css('margin-top',marginTop); alert("marginTop:"+marginTop); }else if(h/w>2){//超长图的时候 滚动条显示 $('.over_pic').css('height',winH-40).css('overflow','scroll'); $(img).css('width',winW); }else{//竖图的时候 $(img).css('height',winH-40).css('margin-left',marginLeft); alert("marginLeft:"+marginLeft); } } reader.readAsDataURL(document.getElementById(sourceId).files[0]); $('body').append('
    ' +'

    '); } $(function(){ link(); afterLoad(); add(); $('.log').css('height',$(window).height()*0.65+'px'); $('.emoji').click(function(){ $('.emojiCon').show(); }) }) script> body> html>

    CSS 前端代码  bass

    @charset "utf-8";
    /* 禁用iPhone中Safari的字号自动调整 */
    html {  
        -webkit-text-size-adjust: 100%; 
        -ms-text-size-adjust: 100%;
        font-family:"微软雅黑",Arial,sans-serif;
    }
    /* 去除iPhone中默认的input样式 */
    input{/* -webkit-appearance:none; */ resize: none;}
    /* 取消链接高亮  */
    body,div,ul,li,ol,h1,h2,h3,h4,h5,h6,input,textarea,select,p,dl,dt,dd,a,img,button,form,table,th,tr,td,tbody,article,
    aside, details,figcaption,figure,footer,header,hgroup, menu,nav,section{
      -webkit-tap-highlight-color:rgba(0, 0, 0, 0);
    }
    /* 设置HTML5元素为块 */
    article, aside, details,figcaption,figure,footer,header,hgroup, menu,nav,section {
        display: block;
    }
    /* 图片自适应 */
    img {width: 100%;height: auto;width:auto\9; /* ie8 */
        -ms-interpolation-mode:bicubic;/*为了照顾ie图片缩放失真*/}
    *:focus {outline:none;}
    /* 清零 */
    body,div,ul,li,ol,h1,h2,h3,h4,h5,h6,input,textarea,select,p,dl,dt,dd,
    a,img,button,form,table,th,tr,td,tbody,article,
    aside, details,figcaption,figure,footer,header,hgroup, menu,nav,section
        {
            margin:0; 
            padding:0; 
            border:none;
        }
    em,i{font-style:normal;}
    strong{font-weight: normal;}
    .clearfix:after{content:""; display:block; visibility:hidden; height:0; clear:both;}
    .clearfix{zoom:1;}
    a{text-decoration:none; color:#969696;}
    a:hover{ text-decoration:none;}
    ul,ol,li{list-style:none;}
    fieldset,img{border: none;}
    q:before,q:after {content:"";}
    input:password {ime-mode:disabled;}
    *{box-sizing:border-box;}
    .scope{min-width:320px; max-width:750px; margin: 0 auto;}
    *{
        margin: 0;
        padding: 0;
    }
    @media screen  and (max-width: 750px){
        html{font-size:30px;}
    }
    @media screen and (min-width:640px) and (max-width:749px){
        html{font-size:25px; }
    }
    @media screen and (min-width:480px) and (max-width:639px){
        html{font-size:20px; }
    }
    @media screen and (min-width:320px) and (max-width:479px){
        html{font-size:15px; }
    }
    
    /*body{font-size:62.5%;}*/
    fieldset,img{border:0}
    a{text-decoration:none;color:#000;outline:none}
    li{list-style:none}
    h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal}
    input,button,textarea,select,optgroup,option{
        font-family:inherit;font-size:inherit;
        font-style:inherit;font-weight:inherit}
    input,button,textarea,select{*font-size:100%}
    .clear:after{
      display:block; 
      content:"clear"; 
      height:0; 
      clear:both;
      visibility:hidden;
    }
    .clear{zoom:1;}

    CSS 前端代码  zhibo

    .comment{
        overflow: scroll;
        border:1px solid red;
        padding:2%;
    }
    .luck{
        padding: 10px 0 0 0;
        overflow:hidden;
    }
    .luckyP{
        color:#44c6dd;
    }
    .chat_pic{
        width:40%;
        display:inline-block;
        vertical-align: top;
    }
    .chat_pic1{
        width:10%;
        vertical-align: middle;
    }
    .user img{
        width:40%;    
    }
    .user,.content1{
        float:left;
    }
    .user{
        width:15%;
        text-align: center;
        vertical-align: middle;
    }
    .content1{
        width:85%;
        line-height:25px;
    }
    .sendCon{
        position:fixed;
        bottom:0;
        left:0;
        right:0;
        padding:2%;
        background:#fff;
        border:1px solid #ddd;
        min-width:320px;
        max-width:750px;
        margin:0 auto;
    }
    .info span{
        display:inline-block;
    }
    .placeholder{
        width:15%;
        position:relative;
        text-align: center;
        vertical-align: middle;
    }
        input[type="file"]{
            width:80%;
            position:absolute;
            left:2%;
            top:2%;
            opacity:0;
        }
        .placeholder img{
            width:65%;
    
        }
    .emoji{
        width:10%;
        text-align: center;
        /*vertical-align: middle;*/
        /*border:1px solid red;*/
    }
        .emoji input{
            width:100%;
            padding:8% 2%;
            border:1px solid #ddd;
            background:#fff;
    
        }
        /*.emoji img{
            width:80%;
            border:1px solid blue;
            vertical-align: middle;
        }*/
    .text{
        width:55%;
    }
        #text{
            width:100%;
            border:1px solid #ddd;
            padding:2%;
        }
    .send{
        width:15%;
    }
        .btn{
            width:100%;
            background:#86e2fd;
            color:#fff;
            padding:9% 2%;
            vertical-align: middle;
        }
    
    .mask{
        background:rgba(0,0,0,0.9);
        width:100%;
        height:100%;
        position:fixed;
        left:0;
        top:0;
        /*display:none;*/
    }
    .sure{
        background:#19ad17;
        color:#fff;
        padding:10px;
    }
    .mask p{
        text-align: right; 
        position:absolute;
        bottom:0;
        left:0;
        right:0;
        height:40px;
        vertical-align: middle;
    }
    .over_pic img{
        display:block;
    }
    /*表情包div*/
    .emojiCon{
        position:absolute;
        bottom:0;
        left:0;
        display:none;
        background:#fff;
    }
    .emojiCon li{
        border:1px solid #ddd;
        float:left;
        width:8.33%;
    }

    php  服务端代码 server.php

    <?php
        
        include 'websocket.class.php';
        
        $config=array(
        
        'address'=>'你自已ip地址',  //注意事 项 linux下云服务器,用ifconfig 查看自已的内网ip  外网访问的ip 是用写在html那块的
        
        'port'=>'8000',
        
        'event'=>'WSevent',//回调函数的函数名
        
        'log'=>true,
        
        );
        
        $websocket = new websocket($config);
        
        $websocket->run();
    
    function WSevent($type,$event){
      global $websocket;
        if('in'==$type){
          $websocket->log('客户进入id:'.$event['k']);
        }elseif('out'==$type){
          $websocket->log('客户退出id:'.$event['k']);
        }elseif('msg'==$type){
          $websocket->log($event['k'].'消息:'.$event['msg']);
          roboot($event['sign'],$event['msg']);
        }
    }
    function roboot($sign,$t){
    
    global $websocket;
    
        $msg_arr = json_decode($t,true);
        foreach($websocket->users as $k =>&$v){
            if(empty($v['room'])){
                $v['room']=$msg_arr['room'];
            }
            if($v['room'] ==$msg_arr['room']){
                 $websocket->idwrite($k,$t);
            }
        }
    }
        

    php 服务羰代码  websocket.class.php

    <?php
    /*
    创建类websocket($config);
    $config结构:
    $config=array(
      'address'=>'192.168.0.200',//绑定地址
      'port'=>'8000',//绑定端口
      'event'=>'WSevent',//回调函数的函数名
      'log'=>true,//命令行显示记录
    );
    
    回调函数返回数据格式
    function WSevent($type,$event)
    
    $type字符串 事件类型有以下三种
    in  客户端进入
    out 客户端断开
    msg 客户端消息到达
    均为小写
    
    $event 数组
    $event['k']内置用户列表的userid;
    $event['sign']客户标示
    $event['msg']收到的消息 $type='msg'时才有该信息
    
    方法:
    run()运行
    search(标示)遍历取得该标示的id
    close(标示)断开连接
    write(标示,信息)推送信息
    idwrite(id,信息)推送信息
    
    属性:
    $users 客户列表
    结构:
    $users=array(
    [用户id]=>array('socket'=>[标示],'hand'=[是否握手-布尔值]),
    [用户id]=>arr.....
    )
    */
    
    class websocket{
        public $log;
        public $event;
        public $signets;
        public $users;  
        public $master; 
        public function __construct($config){
            if (substr(php_sapi_name(), 0, 3) !== 'cli') {
                die("请通过命令行模式运行!");
            }
            error_reporting(E_ALL);
            set_time_limit(0);
            ob_implicit_flush();
            $this->event = $config['event'];
            $this->log = $config['log'];
            $this->master=$this->WebSocket($config['address'], $config['port']);
            $this->sockets=array('s'=>$this->master);
        }
        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->log('开始监听: '.$address.' : '.$port);
            return $server;
        }
      function run(){
        while(true){
          $changes=$this->sockets;
          @socket_select($changes,$write=NULL,$except=NULL,NULL);
          foreach($changes as $sign){
            if($sign==$this->master){
              $client=socket_accept($this->master);
              $this->sockets[]=$client;
              $user = array(
                'socket'=>$client,
                'hand'=>false,
              );
              $this->users[] = $user;
              $k=$this->search($client);
              $eventreturn = array('k'=>$k,'sign'=>$sign);
              $this->eventoutput('in',$eventreturn);
            }else{
              $len=socket_recv($sign,$buffer,2048,0);
              $k=$this->search($sign);
              $user=$this->users[$k];
              if($len<7){
                $this->close($sign);
                $eventreturn = array('k'=>$k,'sign'=>$sign);
                $this->eventoutput('out',$eventreturn);
                continue;
              }
              if(!$this->users[$k]['hand']){//没有握手进行握手
                $this->handshake($k,$buffer);
              }else{
                $buffer = $this->uncode($buffer);
                $eventreturn = array('k'=>$k,'sign'=>$sign,'msg'=>$buffer);
                $this->eventoutput('msg',$eventreturn);
              }
            }
          }
        }
      }
      function search($sign){//通过标示遍历获取id
        foreach ($this->users as $k=>$v){
          if($sign==$v['socket'])
          return $k;
        }
        return false;
      }
      function close($sign){//通过标示断开连接
        $k=array_search($sign, $this->sockets);
        socket_close($sign);
        unset($this->sockets[$k]);
        unset($this->users[$k]);
      }
      function handshake($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]['hand']=true;
        return true;
      }
      function uncode($str){
        $mask = array();  
        $data = '';  
        $msg = unpack('H*',$str);  
        $head = substr($msg[1],0,2);  
        if (hexdec($head{1}) === 8) {  
          $data = false;  
        }else if (hexdec($head{1}) === 1){  
          $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;  
          $e = strlen($msg[1])-2;  
          $n = 0;  
          for ($i=$s; $i<= $e; $i+= 2) {  
            $data .= chr($mask[$n%4]^hexdec(substr($msg[1],$i,2)));  
            $n++;  
          }  
        }  
        return $data;
      }
        function code($msg){
          $msg = preg_replace(array('/\r$/','/\n$/','/\r\n$/',), '', $msg);
          $frame = array();  
          $frame[0] = '81';  
          $len = strlen($msg);  
          $frame[1] = $len<16?'0'.dechex($len):dechex($len);
          $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 idwrite($id,$t){//通过id推送信息
          if(!$this->users[$id]['socket']){return false;}//没有这个标示
          $t=$this->code($t);
          return socket_write($this->users[$id]['socket'],$t,strlen($t));
        }
        function write($k,$t){//通过标示推送信息
          $t=$this->code($t);
          return socket_write($k,$t,strlen($t));
        }
        function eventoutput($type,$event){//事件回调
          call_user_func($this->event,$type,$event);
        }
        function log($t){//控制台输出
          if($this->log){
            $t=$t."\r\n";
            fwrite(STDOUT, iconv('utf-8','gbk//IGNORE',$t));
          }
        }
    }

    php 上传图片异步处理代码 

    $update_path = "Upload/";  
            $msg = $_POST['msg'];
            $exe = str_replace('/', '.', strstr(strstr($msg, ';', TRUE), '/'));
            $exe = $exe == '.jpeg' ? '.jpg' : $exe;
            $tmp = base64_decode(substr(strstr($msg, ','), 1));
            $path = $update_path . md5(time()) . $exe;
            $res  =file_put_contents($path, $tmp);
            echo "http://".$_SERVER['SERVER_NAME']."/test/".$path;

    window 下  打开dos 命令  找到php.exe 文件位置。用php.exe 执行 server.php 文件开启服务器监听。   php.exe   server.php

    打开html页面,点击链接,就可以链接websocket了。提示成功,便可以发送信息了。

    注意事项目:linux 下 滥听端口时,注意服务器防火墙是否开启端口过滤。 Linux  有iptables firewall 两个防火墙软件件