通过http长连接和http短连接及websocket实现在线即时通讯聊天

通过http长连接和http短连接及websocket实现在线即时通讯聊天

通过http长连接和http短连接及websocket实现在线即时通讯聊天

聊天室使我们日常生活中经常用到的,比如微信群聊,今天我带大家了解一下通过三种技术手段可以实现聊天室的功能,主要分为HTTP长连接、HTTP短连接、websocket。UDP通讯、TCP通讯,今天我们主要来讲怎么用http长短连接及websocket来实现即时通讯。

一、什么是HTTP长连接、HTTP短连接、websocket

在HTTP/1.0中默认使用http短连接。也就是说,客户端和服务器每进行一次HTTP操作,就建立一次连接,任务结束就中断连接。当客户端浏览器访问的某个HTML或其他类型的Web页中包含有其他的Web资源(如JavaScript文件、图像文件、CSS文件等),每遇到这样一个Web资源,浏览器就会重新建立一个HTTP会话。

而从HTTP/1.1起,默认使用长连接,用以保持连接特性,提高传输的效率和减低服务器的开销。使用长连接的HTTP协议,会在响应头加入这行代码:

Connection:keep-alive

在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,客户端再次访问这个服务器时,会继续使用这一条已经建立的连接。Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。实现长连接需要客户端和服务端都支持长连接。

HTTP协议的长连接和短连接,实质上是TCP协议的长连接和短连接。

websocket是html5的新特性,WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

二、http长连接实现即时聊天

主要通过ajax长连接后端php,后端php一直定时监控消息,有新消息就即时反馈给浏览器,重新开始新的长连接,及时性相对比较高。

<?php
function writefile($file_name, $text) {
    $handle = fopen($file_name, "a");
    if (flock($handle, LOCK_EX)) {
        fwrite($handle, $text.'\n');
        flock($handle, LOCK_UN);
    }
    fclose($handle);
}

$_talklistfile = "../Data/talklist.txt";
if (!file_exists($_talklistfile)) {
    file_put_contents($_talklistfile, "");
}

if (!empty($_POST['action'])) {
    switch ($_POST['action']) {
        case "getwords":
            $_mtime = $_POST['mtime'];
            $i = 0;
            while (true) {

                usleep(500000); //0.5秒
                $i++;
                if ($i >= 40) {
                    echo json_encode(array('error' => true));
                    exit();
                }
                //清空文件缓存
                clearstatcache();
                //如果文件修改时间大于用户那边的时间,就说明有更新
                $_newmtime = filemtime($_talklistfile);
                if ($_newmtime > $_mtime || $_mtime == 0) {
                    echo json_encode(array('error' => false, "data" => file_get_contents($_talklistfile), "mtime" => $_newmtime));

                    exit();
                }

            }

            break;
        case "sayword":
            $_words = $_POST['words'];
            $_uid = $_POST['uid'];
            writefile($_talklistfile, '用户'.$_uid." ".date('H:i:s', time())."说".$_words); //写入聊天文件
            echo json_encode(array('error' => false));
            exit();

            break;
        default:

            break;
    }

} else {
    ?>
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>ajax长轮询实现聊天</title>
        <script id="bfwone" data="dep=jquery.17&err=0" type="text/javascript" src="http://repo.bfw.wiki/bfwrepo/js/bfwone.js"></script>
        <script type="text/javascript">
            var mtime = 0;

            //生成唯一字符串

            function randomString(len) {
                len = len || 32;
                var $chars = '2345678';
                var maxPos = $chars.length;
                var pwd = '';
                for (i = 0; i < len; i++) {
                    pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
                }
                return pwd;
            }

            //发送消息
            function saywords() {
                var words = $("#words").val();
                if (words == "") {
                    alert('说点啥');
                    return;
                }
                $.ajax({
                    type: "POST",
                    dataType: "json",
                    url: "/ajaxtalk2.php",

                    data: {
                        action: "sayword",
                        words: words,
                        uid: $("#username").val()
                    }, //40秒后无论结果服务器都返回数据
                    success: function(ret, textStatus) {
                        if (ret.error == false) {
                            $("#words").val("");

                        }
                    },

                });
            }
            //长连接监听有没有新消息
            function listenwords() {
                $.ajax({
                    type: "POST",
                    dataType: "json",
                    url: "/ajaxtalk2.php",
                    timeout: 30000,
                    //ajax请求超时时间30秒
                    data: {
                        action: "getwords",
                        mtime: mtime,


                    },
                    //40秒后无论结果服务器都返回数据
                    success: function(ret, textStatus) {
                        if (ret.error == false) {
                            mtime = ret.mtime;
                            $("#msg").html(ret.data.replace(/\\n/g, "</br>"));

                        }
                        listenwords(); //循环监听
                    },
                    //Ajax请求超时,继续查询
                    error: function(XMLHttpRequest, textStatus, errorThrown) {
                        if (textStatus == "timeout") {
                            $("#msg").append("<br>[超时]");
                            listenwords();
                        }
                    }
                });
            }
            bready(function() {

                $("#username").val(randomString(6));

                listenwords();
                $("#btn").bind("click", function() {
                    saywords();
                });
            });
        </script>
        <style>
        </style>
    </head>
    <body>
        <h1>http长连接实现聊天室</h1>
        <div id="msg" style="height:300px;border:1px solid grey;"></div>
        <input type="text" id="username" style="width:50px;" /><input type="text" id="words" placeholder="请输入文字" />
        <input id="btn" type="button" value="发送" />
    </body>
</html>

<?php
}
?>

三、http短连接实现即时聊天

11短连接主要通过浏览器ajax定时轮训服务器查看是否有新消息,及时性取决于浏览器查询的时间间隔,间隔越短,及时性越高。

<?php
function writefile($file_name, $text) {
    $handle = fopen($file_name, "a");
    if (flock($handle, LOCK_EX)) {
        fwrite($handle, $text.'\n');
        flock($handle, LOCK_UN);
    }
    fclose($handle);
}

$_talklistfile = "../Data/talklist.txt";
if (!file_exists($_talklistfile)) {
    file_put_contents($_talklistfile, "");
}

if (!empty($_POST['action'])) {
    switch ($_POST['action']) {
        case "getwords":
            $_mtime = $_POST['mtime'];
            //清空文件缓存
            clearstatcache();
            //如果文件修改时间大于用户那边的时间,就说明有更新
            $_newmtime = filemtime($_talklistfile);
            if ($_newmtime > $_mtime || $_mtime == 0) {
                echo json_encode(array('error' => false, "data" => file_get_contents($_talklistfile), "mtime" => $_newmtime));

            } else {
                echo json_encode(array('error' => true));
            }
            exit();



            break;
        case "sayword":
            $_words = $_POST['words'];
            $_uid = $_POST['uid'];
            writefile($_talklistfile, '用户'.$_uid." ".date('H:i:s', time())."说".$_words); //写入聊天文件
            echo json_encode(array('error' => false));
            exit();

            break;
        default:

            break;
    }

} else {
    ?>
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>ajax短轮询实现聊天</title>
        <script id="bfwone" data="dep=jquery.17&err=0" type="text/javascript" src="http://repo.bfw.wiki/bfwrepo/js/bfwone.js"></script>
        <script type="text/javascript">
            var mtime = 0;

            //生成唯一字符串

            function randomString(len) {
                len = len || 32;
                var $chars = '2345678';
                var maxPos = $chars.length;
                var pwd = '';
                for (i = 0; i < len; i++) {
                    pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
                }
                return pwd;
            }

            //发送消息
            function saywords() {
                var words = $("#words").val();
                if (words == "") {
                    alert('说点啥');
                    return;
                }
                $.ajax({
                    type: "POST",
                    dataType: "json",
                    url: "/ajaxtalk.php",

                    data: {
                        action: "sayword",
                        words: words,
                        uid: $("#username").val()
                    }, //40秒后无论结果服务器都返回数据
                    success: function(ret, textStatus) {
                        if (ret.error == false) {
                            $("#words").val("");

                        }
                    },

                });
            }
            //短连接定时获取有没有新消息
            function getwords() {
                $.ajax({
                    type: "POST",
                    dataType: "json",
                    url: "/ajaxtalk.php",
                    timeout: 30000,
                    //ajax请求超时时间30秒
                    data: {
                        action: "getwords",
                        mtime: mtime,


                    },
                    //40秒后无论结果服务器都返回数据
                    success: function(ret, textStatus) {
                        if (ret.error == false) {
                            mtime = ret.mtime;
                            $("#msg").html(ret.data.replace(/\\n/g, "</br>"));

                        }

                    },
                    //Ajax请求超时,继续查询
                    error: function(XMLHttpRequest, textStatus, errorThrown) {
                        if (textStatus == "timeout") {
                            $("#msg").append("<br>[超时]");

                        }
                    }
                });
            }
            bready(function() {

                $("#username").val(randomString(6));

                setInterval(getwords, 2000); //2秒钟拉一次
                $("#btn").bind("click", function() {
                    saywords();
                });
            });
        </script>
        <style>
        </style>
    </head>
    <body>
<h1>http短连接实现聊天室</h1>
        <div id="msg" style="height:300px;border:1px solid grey;"></div>
        <input type="text" id="username" style="width:50px;" /><input type="text" id="words"  placeholder="请输入文字"/>
        <input id="btn" type="button" value="发送" />
    </body>
</html>

<?php
}
?>

四、websocket实现即时聊天通讯

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>websocket实现聊天室</title>
    <script id="bfwone" data="dep=jquery.17&err=0" type="text/javascript" src="http://repo.bfw.wiki/bfwrepo/js/bfwone.js"></script>
    <script type="text/javascript">

        var ws = null;

        //生成唯一字符串

        function randomString(len) {
            len = len || 32;
            var $chars = '2345678';
            var maxPos = $chars.length;
            var pwd = '';
            for (i = 0; i < len; i++) {
                pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
            }
            return pwd;
        }

        //发送消息
        function saywords() {
            var words = $("#words").val();
            if (words == "") {
                alert('说点啥');
                return;
            }
            var data = JSON.stringify({
                "uid": $("#username").val(), "word": words
            });
            ws.send(data);
            $("#msg").append("我说:"+words+"</br>");
            $("#words").val("");

        }

        bready(function() {

            // 创建一个 websocket 连接
            ws = new WebSocket("ws://web.debug.only.bfw.wiki:8089");

            // websocket 创建成功事件
            ws.onopen = function () {

                console.log("建立连接成功.");

            };

            // websocket 接收到消息事件
            ws.onmessage = function (e) {
                var ret = JSON.parse(e.data);
                console.log(ret);
                $("#msg").append("用户"+ret.uid+"说:"+ret.word+"</br>"); //收到新消息就新增
            }

            // websocket 错误事件
            ws.onerror = function () {

                alert("出错了,请重试.");
            };
            $("#username").val(randomString(6));
            $("#btn").bind("click", function() {
                saywords();
            });
        });
    </script>
    <style>
    </style>
</head>
<body>
    <h1>websocket实现聊天室</h1>
    <div id="msg" style="height:300px;border:1px solid grey;"></div>
    <input type="text" id="username" style="width:50px;" /><input type="text" id="words" placeholder="请输入文字" />
    <input id="btn" type="button" value="发送" />
</body>
</html>

后端我们使用workerman简历websocket,代码如下

<?php
use Workerman\Connection\AsyncTcpConnection;
use Workerman\Worker;
require_once __DIR__ . '/Autoloader.php';
$users = [];
// 创建一个Worker监听8088端口,使用websocket协议通讯
$ws_worker = new Worker("websocket://0.0.0.0:8089");

// 启动4个进程对外提供服务
$ws_worker->count = 4;
// 当收到客户端发来的数据后
$ws_worker->onConnect=function($connection){
    $_key = $connection->getRemoteIp() . $connection->getRemotePort();
    global $users;
    if (!isset($users[$_key])) {
        $users[$_key] = $connection;
    }
};
// 当收到客户端发来的数据后
$ws_worker->onMessage = function ($connection, $data) {
    $_key = $connection->getRemoteIp() . $connection->getRemotePort();

    global $users;
    if (!isset($users[$_key])) {
        $users[$_key] = $connection;
    }
    foreach ($users as $key => $user) {
        if ($key != $_key) {
            echo "send data";
            $user->send($data);
        }
    }

    // 设置连接的onClose回调
    $connection->onClose = function ($connection) // 客户端主动关闭
{
        $_key = $connection->getRemoteIp() . $connection->getRemotePort();
        global $users;

        if (isset($users[$_key])) {

            unset($users[$_key]);
        }

        echo "connection closed\n";
    };
};

// 运行worker
Worker::runAll();

五、总结

消息接收及时性:http短连接<http长连接<websocket  webscket的及时性最高。

资源消耗率:websocket<http短连接<http长连接 http长连接消耗服务器资源最大。

兼容性:websocket<http长短连接     由于websocket是html5新推出的技术规范,老的浏览器不支持,所以兼容性不是太好

三个技术方式demp例子代码的WEBIDE地址http://studio.bfw.wiki/Studio/Open.html?projectid=15771671791332100010,点开后直接运行和编辑代码,无需下载

{{collectdata}}

网友评论0