>  기사  >  백엔드 개발  >  PHP에서 채팅 기능을 구현하는 방법

PHP에서 채팅 기능을 구현하는 방법

Guanhui
Guanhui원래의
2020-05-11 11:40:204164검색

PHP에서 채팅 기능을 구현하는 방법

php에서 채팅 기능을 구현하는 방법

1. 프런트 엔드에서 Ajax를 사용하여 정기적으로 PHP 스크립트를 폴링하면 PHP가 채팅 데이터를 쿼리하여 프런트 엔드로 반환하고 마지막으로 프런트 엔드에 반환합니다.

코드 예:

<html>
<head>
<title>聊天室</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="../jquery.min.js"></script>
<script>
$(function(){
    $("#post").click(function(){
        
    var content=$("#content").val();
    if(!$.trim(content)){
       alert(&#39;请填写内容&#39;);
       return false;
    }
    $("#content").val("");

    $.post("ajax.php", {content:content});});
})
    
    function getData(msg){
        if(msg == undefined)
        {
            msg = &#39;&#39;;    
        }
        $.post("get.php",{"msg":msg},function(data){
            //var myjson = eval("("+data+")");
            if(data){
                var chatcontent = &#39;&#39;;
                var obj = eval(&#39;(&#39;+data+&#39;)&#39;);
                $.each(obj,function(key,val){
                    chatcontent += "<div class=&#39;username&#39;>"+val[&#39;username&#39;]+" 说:</div>";
                    chatcontent += "<div class=&#39;content&#39;>"+val[&#39;content&#39;]+"</div>";
                })
                $("#chatshow").html(chatcontent);
            }
            
            getData();    
        })    
    }
    
    // function getUser(msg){
    //     if(msg == undefined){
    //         msg = &#39;&#39;;    
    //     }
    //     $.post("getuser.php",{"msg":msg},function(data){
    //         //var myjson = eval("("+data+")");
    //         if(data){
    //             var chatcontent = &#39;&#39;;
    //             var obj = eval(&#39;(&#39;+data+&#39;)&#39;);
    //             $.each(obj,function(key,val){
    //                 chatcontent += "<div class=&#39;username&#39;>"+val[&#39;username&#39;]+" 说:</div>";
    //                 chatcontent += "<div class=&#39;content&#39;>"+val[&#39;content&#39;]+"</div>";
    //             })
    //             $("#chatshow").html(chatcontent);
    //         }
    //         getData();    
    //     })    
    // }
    
    getData("one");
    
    

    // $(function(){
    //     $("#userlist p").click(function(){
    //         $("#content").val("@"+$(this).text()+" ");    
    //     })    
    // })
    
    // $(function(){
    //     $(document).keypress(function(e){
    //         if(e.ctrlKey && e.which == 13 || e.which == 10) { 
                   
    //             var content=$("#content").val();
    //             if(!$.trim(content)){
    //                alert(&#39;请填写内容&#39;);
    //                return false;
    //             }
    //             $("#content").val("");    
    //             $.post("ajax.php", {content:content});
    //         }      
    //     })
    //     //alert(event.clientX+document.body.clientWidth);
    // })
    
    /*window.onbeforeunload=function (){ 
        //return("===onbeforeunload==="); 
        if(event.clientX>document.body.clientWidth && event.clientY < 0 || event.altKey){ 
            return("你关闭了浏览器"); 
        }else{ 
            return(document.body.clientWidth); 
        } 
    } */
    
</script>
<style>
#chat{margin:0 auto;}
#chatshow{width:500px;height:400px;overflow:auto;border:1px solid #ccc;float:left;}
#userlist{width:100px;height:400px;overflow:auto;border:1px solid #ccc;float:left;margin-right:2px;}
#userlist p{color:#0F0; cursor:pointer;}
.clearboth{clear:both;}
.username{font-weight:bold;color:#00F;font-size:12px;margin-bottom:3px;margin-top:8px;}
</style>
</head>
<body>
<div id="chat">
<div id="userlist">
<div style="font-size:12px;font-weight:bold;">在线用户列表</div>
<div class="userlist">
<?php 
    // $dsn = "mysql:host=localhost;dbname=test;charset=utf8&#39;";
 //    $db = new PDO($dsn, &#39;root&#39;, &#39;root&#39;);
    $db = new PDO(&#39;mysql:dbname=test;host=127.0.0.1;charset=utf8&#39;, &#39;root&#39;, &#39;adminroot&#39;);
    $rs = $db->prepare("select * from chat where is_login = &#39;1&#39;");
    $rs->execute();
    while($row = $rs->fetch()){
        echo &#39;<p>&#39;.$row[&#39;username&#39;].&#39;</p>&#39;;    
    }
?>
</div>
</div>
<div id="chatshow"></div>
</div>
<div class="clearboth"></div>
<div>
<textarea name="content" id="content" style="width:600px;height:100px"></textarea>
<input type=&#39;button&#39; name=&#39;tj&#39; id="post" value=&#39;发布&#39; >
</div>
</body>
</html>
<?php
/*session_start();
$mem = new Memcache;
$mem->connect("localhost",11211);
if(isset($_POST[&#39;content&#39;])){
    $con[&#39;username&#39;] = $_SESSION["username"];
    $con[&#39;content&#39;] = $_POST["content"];
    
    $data = $mem->get(&#39;chat&#39;);
    $data[] = $con;
    $mem->set("chat",$data,0,0);    
}
$mem->close();
*/
    if(isset($_POST[&#39;content&#39;])){
        session_start();
        $filename = date("Ymd",time()).".txt";
        if(file_exists($filename)){
            $content = file_get_contents($filename);
            $data = json_decode($content,true);        
            $con[&#39;username&#39;] = $_SESSION["username"];
            $con[&#39;content&#39;] = $_POST["content"];
            $data[] = $con;
            $file = fopen($filename,"w");
            fwrite($file,json_encode($data));
            fclose($file);    
        }else{
            $file = fopen($filename,"w");
            $con[&#39;username&#39;] = $_SESSION["username"];
            $con[&#39;content&#39;] = $_POST["content"];
            $data[] = $con;
            fwrite($file,json_encode($data));
            fclose($file);    
        }
            
    }



?>
<?php 
/*require &#39;conn.inc&#39;;
function getData(){
    $sql="SELECT count(*) FROM  `talkroom`";
    $res=mysql_query($sql);
    $count = mysql_fetch_row($res);
    
    return $count;    
}
$old = getData();

while(true){
    $new = getData();
    if($new[0]>$old[0]){
        //echo json_encode($new[&#39;data&#39;]);
        //print_r($new);
        echo &#39;2222222222&#39;.&#39;<br/>&#39;;
    }
    //echo &#39;<br />11111111111111111111111111111111111111111111111111111111111111111111&#39;;
    usleep(1000);
}
*/
/*set_time_limit(0);
$mem = new Memcache;
$mem->connect("localhost",11211);

$count = count($mem->get("chat"));
$com = true;
//$mem->delete(&#39;chat&#39;);
if($_POST[&#39;msg&#39;] == "one"){
    exit(json_encode($mem->get("chat")));    
}
if($_POST[&#39;msg&#39;] == "break"){
    $com = false;    
}
$time1 = time();
while(true){
    if($com){
        $data = $mem->get("chat");
        if(count($data)>$count){
            echo json_encode($data);
            break;
        }
        usleep(300);
    }else{
        break;    
    }
}
$mem->close();*/


set_time_limit(0);
$filename = date("Ymd",time()).".txt";
if(file_exists($filename)){
    $content = file_get_contents($filename);
    $data = json_decode($content,true);        
    $count = count($data);
    // echo $count;die;
    if($_POST[&#39;msg&#39;] == "one"){
        exit(json_encode($data));    
    }
    
    while(true){
        
        $contents = file_get_contents($filename);
        $datas = json_decode($contents,true);
        $counts = count($datas);    
        if($counts>$count){
            echo json_encode($datas);
            break;
        }
        usleep(300);
    }
}else{
    $file = fopen($filename,"w");
    $con[&#39;username&#39;] = "系统消息";
    $con[&#39;content&#39;] = "欢迎来到EPGO聊天室";
    $data[] = $con;
    fwrite($file,json_encode($data));
    fclose($file);
    
    exit(json_encode($data));    
    
}

?>

2. WebSocket을 사용하여 서버와 긴 연결을 만들어 채팅 기능을 구현합니다.

클라이언트 코드:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>websocket聊天室</title>
    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,600" rel="stylesheet">
    <link rel="stylesheet" href="css/reset.min.css">
    <link rel="stylesheet" href="css/style.css">
    <style>
        .message img {
            float: left;
            width: 40px;
            height: 40px;
            margin-right: 12px;
            border-radius: 50%;
        }

        .you {
            margin-left: 60px;
            margin-top: -39px;
        }

        .me-header {
            float: right !important;
            margin-right: 0 !important;
        }

        .me {
            margin-right: 60px;
            margin-top: -39px;
        }

        .active-chat::-webkit-scrollbar, .left::-webkit-scrollbar {
            width: 2px;
        }
    </style>
</head>
<body>

<div>
    <div>
        <div>
            <div style="padding: 20px 29px;height: auto;">
                <div class="" style="font: 400 13.3333px Arial;font-weight: 400;">在线人数:<span id="numbers">0</span> 人
                </div>
            </div>
            <ul>
            </ul>
        </div>
        <div>
            <div><span>Tips: <span>PHP之websocket聊天室</span></span></div>
            <div class="chat active-chat" data-chat="person1"
                 style="height: auto;border-width: 0px;padding: 10px;height: 483px; padding: 10px;overflow-y: auto;scrollTop: 100px">
            </div>
            <div>
                <a href="javascript:;" class="write-link attach"></a>
                <input type="text" id="input-value" onkeydown="confirm(event)"/>
                <a href="javascript:;" class="write-link smiley"></a>
                <a href="javascript:;" class="write-link send" onclick="send()"></a>
            </div>
        </div>
    </div>
</div>

<script src="js/index.js"></script>
<script>
    var uname = prompt(&#39;请输入用户名&#39;, &#39;user&#39; + uuid(8, 16));
    var avatar = [&#39;bo-jackson.jpg&#39;, &#39;dog.png&#39;, &#39;drake.jpg&#39;, &#39;louis-ck.jpeg&#39;, &#39;michael-jordan.jpg&#39;, &#39;thomas.jpg&#39;];
    if (avatar[Math.round(Math.random() * 10)]) {
        var headerimg = "img/" + avatar[Math.round(Math.random() * 10)];
    } else {
        var headerimg = "img/" + avatar[0];
    }

    // 创建一个 websocket 连接
    var ws = new WebSocket("ws://127.0.0.1:1238");

    // websocket 创建成功事件
    ws.onopen = function () {
        var data = "系统消息:建立连接成功";
        console.log(data);

    };

    // websocket 接收到消息事件
    ws.onmessage = function (e) {
        var data = JSON.parse(e.data);
        switch (data.type) {
            case &#39;handShake&#39;:
                console.log(data.msg);
                //发送用户登录
                var user_info = {&#39;type&#39;: &#39;login&#39;, &#39;msg&#39;: uname, &#39;headerimg&#39;: headerimg};
                sendMsg(user_info);
                break;
            case &#39;login&#39;:
                userList(data.user_list);
                systemMessage(&#39;系统消息: &#39; + data.msg + &#39; 已上线&#39;);
                break;
            case &#39;logout&#39;:
                userList(data.user_list);
                if (data.msg.length > 0) {
                    systemMessage(&#39;系统消息: &#39; + data.msg + &#39; 已下线&#39;);
                }
                break;
            case &#39;user&#39;:
                messageList(data);
                break;
            case &#39;system&#39;:
                systemMessage();
                break;
        }
    }

    // websocket 错误事件
    ws.onerror = function () {
        var data = "系统消息 : 出错了,请退出重试.";
        alert(data);
    };

    function confirm(event) {
        var key_num = event.keyCode;
        if (13 == key_num) {
            send();
        } else {
            return false;
        }
    }

    function systemMessage(msg) {
        var html = `<div>
            <span>` + msg + `</span>
        </div>`;
        var active_chat = document.querySelector(&#39;div.active-chat&#39;);
        var oldHtml = active_chat.innerHTML;
        active_chat.innerHTML = oldHtml + html;
        active_chat.scrollTop = active_chat.scrollHeight;
    }

    function send() {
        var msg = document.querySelector("input#input-value").value;
        var reg = new RegExp("\r\n", "g");
        msg = msg.replace(reg, "");
        sendMsg({type: "user", msg: msg});
        document.querySelector("input#input-value").value = "";
    }

    function sendMsg(msg) {
        var data = JSON.stringify(msg);
        ws.send(data);
    }

    function userList(user) {
        var html = &#39;&#39;;
        for (var i = 0; i < user.length; i++) {
            html += `<li data-chat="person1">
                    <img src="` + user[i].headerimg + `" alt=""/>
                    <span>` + user[i].username + `</span>
                    <span>` + user[i].login_time + `</span>
                    <span style="color: green;font-size: 7px;">在线</span>
                </li>`;
        }
        document.querySelector(&#39;ul.people&#39;).innerHTML = html;
        document.querySelector(&#39;span#numbers&#39;).innerHTML = user.length;
    }

    function messageList(data) {
        if (data.from == uname) {
            var html = `<div>
                    <img src="` + data.headerimg + `" alt=""/>
                    <div class="bubble me">` + data.msg + `</div>
                </div>`;
        } else {
            var html = `<div>
                    <img src="` + data.headerimg + `" alt=""/>
                    <div class="bubble you">` + data.msg + `</div>
                </div>`;
        }
        var active_chat = document.querySelector(&#39;div.active-chat&#39;);
        var oldHtml = active_chat.innerHTML;
        active_chat.innerHTML = oldHtml + html;
        active_chat.scrollTop = active_chat.scrollHeight;
    }

    /**
     * 生产一个全局唯一ID作为用户名的默认值;
     *
     * @param len
     * @param radix
     * @returns {string}
     */
    function uuid(len, radix) {
        var chars = &#39;0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz&#39;.split(&#39;&#39;);
        var uuid = [], i;
        radix = radix || chars.length;

        if (len) {
            for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix];
        } else {
            var r;

            uuid[8] = uuid[13] = uuid[18] = uuid[23] = &#39;-&#39;;
            uuid[14] = &#39;4&#39;;

            for (i = 0; i < 36; i++) {
                if (!uuid[i]) {
                    r = 0 | Math.random() * 16;
                    uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
                }
            }
        }

        return uuid.join(&#39;&#39;);
    }
</script>
</body>
</html>

서버 코드:

<?php
/**
 * Created by PhpStorm.
 * User: 25754
 * Date: 2019/4/23
 * Time: 14:13
 */

class socketServer
{

    const LISTEN_SOCKET_NUM = 9;
    const LOG_PATH = "./log/";
    private $_ip = "127.0.0.1";
    private $_port = 1238;
    private $_socketPool = array();
    private $_master = null;

    public function __construct()
    {
        $this->initSocket();
    }

    private function initSocket()
    {
        try {
            //创建socket套接字
            $this->_master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
            // 设置IP和端口重用,在重启服务器后能重新使用此端口;
            socket_set_option($this->_master, SOL_SOCKET, SO_REUSEADDR, 1);
            //绑定地址与端口
            socket_bind($this->_master, $this->_ip, $this->_port);
            //listen函数使用主动连接套接口变为被连接套接口,使得一个进程可以接受其它进程的请求,从而成为一个服务器进程。在TCP服务器编程中listen函数把进程变为一个服务器,并指定相应的套接字变为被动连接,其中的能存储的请求不明的socket数目。
            socket_listen($this->_master, self::LISTEN_SOCKET_NUM);
        } catch (Exception $e) {
            $this->debug(array("code: " . $e->getCode() . ", message: " . $e->getMessage()));
        }
        //将socket保存到socket池中
        $this->_socketPool[0] = array(&#39;resource&#39; => $this->_master);
        $pid = getmypid();
        $this->debug(array("server: {$this->_master} started,pid: {$pid}"));
        while (true) {
            try {
                $this->run();
            } catch (Exception $e) {
                $this->debug(array("code: " . $e->getCode() . ", message: " . $e->getMessage()));
            }
        }
    }

    private function run()
    {
        $write = $except = NULL;
        $sockets = array_column($this->_socketPool, &#39;resource&#39;);
        $read_num = socket_select($sockets, $write, $except, NULL);
        if (false === $read_num) {
            $this->debug(array(&#39;socket_select_error&#39;, $err_code = socket_last_error(), socket_strerror($err_code)));
            return;
        }
        foreach ($sockets as $socket) {
            if ($socket == $this->_master) {
                $client = socket_accept($this->_master);
                if ($client === false) {
                    $this->debug([&#39;socket_accept_error&#39;, $err_code = socket_last_error(), socket_strerror($err_code)]);
                    continue;
                }
                //连接
                $this->connection($client);
            } else {
                //接受数据
                $bytes = @socket_recv($socket, $buffer, 2048, 0);
                if ($bytes == 0) {
                    $recv_msg = $this->disconnection($socket);
                } else {
                    if ($this->_socketPool[(int)$socket][&#39;handShake&#39;] == false) {
                        $this->handShake($socket, $buffer);
                        continue;
                    } else {
                        $recv_msg = $this->parse($buffer);
                    }
                }
                $msg = $this->doEvents($socket, $recv_msg);
                echo($msg);
                socket_getpeername ( $socket  , $address ,$port );
                $this->debug(array(
                    &#39;send_success&#39;,
                    json_encode($recv_msg),
                    $address,
                    $port
                ));
                $this->broadcast($msg);
            }
        }
    }

    /**
     * 数据广播
     * @param $data
     */
    private function broadcast($data)
    {
        foreach ($this->_socketPool as $socket) {
            if ($socket[&#39;resource&#39;] == $this->_master) {
                continue;
            }
            socket_write($socket[&#39;resource&#39;], $data, strlen($data));
        }
    }

    /**
     * 业务处理
     * @param $socket
     * @param $recv_msg
     * @return string
     */
    private function doEvents($socket, $recv_msg)
    {
        //联系我
    }

    /**
     * socket握手
     * @param $socket
     * @param $buffer
     * @return bool
     */
    public function handShake($socket, $buffer)
    {
        $acceptKey = $this->encry($buffer);
        $upgrade = "HTTP/1.1 101 Switching Protocols\r\n" .
            "Upgrade: websocket\r\n" .
            "Connection: Upgrade\r\n" .
            "Sec-WebSocket-Accept: " . $acceptKey . "\r\n\r\n";

        // 写入socket
        socket_write($socket, $upgrade, strlen($upgrade));
        // 标记握手已经成功,下次接受数据采用数据帧格式
        $this->_socketPool[(int)$socket][&#39;handShake&#39;] = true;
        socket_getpeername ( $socket  , $address ,$port );
        $this->debug(array(
            &#39;hand_shake_success&#39;,
            $socket,
            $address,
            $port
        ));
        //发送消息通知客户端握手成功
        $msg = array(&#39;type&#39; => &#39;handShake&#39;, &#39;msg&#39; => &#39;握手成功&#39;);
        $msg = $this->frame(json_encode($msg));
        socket_write($socket, $msg, strlen($msg));
        return true;
    }

    /**
     * 帧数据封装
     * @param $msg
     * @return string
     */
    private function frame($msg)
    {
        $frame = [];
        $frame[0] = &#39;81&#39;;
        $len = strlen($msg);
        if ($len < 126) {
            $frame[1] = $len < 16 ? &#39;0&#39; . dechex($len) : dechex($len);
        } else if ($len < 65025) {
            $s = dechex($len);
            $frame[1] = &#39;7e&#39; . str_repeat(&#39;0&#39;, 4 - strlen($s)) . $s;
        } else {
            $s = dechex($len);
            $frame[1] = &#39;7f&#39; . str_repeat(&#39;0&#39;, 16 - strlen($s)) . $s;
        }
        $data = &#39;&#39;;
        $l = strlen($msg);
        for ($i = 0; $i < $l; $i++) {
            $data .= dechex(ord($msg{$i}));
        }
        $frame[2] = $data;
        $data = implode(&#39;&#39;, $frame);
        return pack("H*", $data);
    }

    /**
     * 接受数据解析
     * @param $buffer
     * @return mixed
     */
    private function parse($buffer)
    {
        $decoded = &#39;&#39;;
        $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 json_decode($decoded, true);
    }

    // 提取 Sec-WebSocket-Key 信息
    private function getKey($req)
    {
        $key = null;
        if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $req, $match)) {
            $key = $match[1];
        }
        return $key;
    }

    //加密 Sec-WebSocket-Key
    private function encry($req)
    {
        $key = $this->getKey($req);
        return base64_encode(sha1($key . &#39;258EAFA5-E914-47DA-95CA-C5AB0DC85B11&#39;, true));
    }

    /**
     * 连接socket
     * @param $client
     */
    public function connection($client)
    {
        socket_getpeername ( $client  , $address ,$port );
        $info = array(
            &#39;resource&#39; => $client,
            &#39;userInfo&#39; => &#39;&#39;,
            &#39;handShake&#39; => false,
            &#39;ip&#39; => $address,
            &#39;port&#39; => $port,
        );
        $this->_socketPool[(int)$client] = $info;
        $this->debug(array_merge([&#39;socket_connect&#39;], $info));
    }

    /**
     * 断开连接
     * @param $socket
     * @return array
     */
    public function disconnection($socket)
    {
        $recv_msg = array(
            &#39;type&#39; => &#39;logout&#39;,
            &#39;msg&#39; => @$this->_socketPool[(int)$socket][&#39;username&#39;],
        );
        unset($this->_socketPool[(int)$socket]);
        return $recv_msg;
    }

    /**
     * 日志
     * @param array $info
     */
    private function debug(array $info)
    {
        $time = date(&#39;Y-m-d H:i:s&#39;);
        array_unshift($info, $time);
        $info = array_map(&#39;json_encode&#39;, $info);
        file_put_contents(self::LOG_PATH . &#39;websocket_debug.log&#39;, implode(&#39; | &#39;, $info) . "\r\n", FILE_APPEND);
    }
}

new socketServer();

추천 튜토리얼: "PHP Tutorial"

위 내용은 PHP에서 채팅 기능을 구현하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.