zoukankan      html  css  js  c++  java
  • Web聊天室的实现

    Tornado普通方式实现聊天室

    普通的http方式连接的话,基本思路是前端页面通过JS重复连接后端服务器.

    核心文件:app.py

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import tornado.ioloop
    import tornado.web
    import time
    import json
    
    
    class IndexHandler(tornado.web.RequestHandler):
    
        def get(self, *args, **kwargs):
            self.render('index.html')
    
    li = [
        {'id':123123, 'content': 'asdfasdfasdf'}
    ]
    class MsgHandler(tornado.web.RequestHandler):
    
        def get(self, *args, **kwargs):
            index = self.get_argument('index')
            index = int(index)
            if index == 0:
                self.write(json.dumps(li))
            else:
                self.write(json.dumps(li[index:]))
    
    settings = {
        'template_path': 'views',
        'static_path': 'static',
    }
    
    application = tornado.web.Application([
        (r"/index.html", IndexHandler),
        (r"/msg.html", MsgHandler),
    
    ], **settings)
    
    if __name__ == "__main__":
    
        print('http://127.0.0.1:8006')
        application.listen(8006)
        tornado.ioloop.IOLoop.instance().start()
    

    index.html聊天页面:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <div id="container">
    
        </div>
    
        <script src="/static/jquery-1.12.4.js"></script>
        <script>
            INDEX = 0;
    
            function get_message(){
                $.ajax({
                    url: '/msg.html',
                    type: 'get',
                    data: {'index': INDEX},
                    dataType: 'json',
                    success: function (arg) {
                        $.each(arg, function(k,v){
                            INDEX +=1;
                            var tag = document.createElement('p');
                            tag.innerHTML = v.content;
                            $('#container').append(tag);
                        });
                        get_message()
                    }
                })
            }
    
            get_message()
        </script>
    </body>
    </html>
    

    tornado异步非阻塞方式

    主要处理文件:

    import logging
    import tornado.escape
    import tornado.ioloop
    import tornado.web
    import os.path
    import uuid
    
    from tornado.concurrent import Future
    from tornado import gen
    from tornado.options import define, options, parse_command_line
    
    define("port", default=8888, help="run on the given port", type=int)
    define("debug", default=False, help="run in debug mode")
    
    
    class MessageBuffer(object):
        def __init__(self):
            self.waiters = set()
            self.cache = []
            self.cache_size = 200
    
        def wait_for_messages(self, cursor=None):
            # Construct a Future to return to our caller.  This allows
            # wait_for_messages to be yielded from a coroutine even though
            # it is not a coroutine itself.  We will set the result of the
            # Future when results are available.
            result_future = Future()
            if cursor:
                new_count = 0
                for msg in reversed(self.cache):
                    if msg["id"] == cursor:
                        break
                    new_count += 1
                if new_count:
                    result_future.set_result(self.cache[-new_count:])
                    return result_future
            self.waiters.add(result_future)
            return result_future
    
        def cancel_wait(self, future):
            self.waiters.remove(future)
            # Set an empty result to unblock any coroutines waiting.
            future.set_result([])
    
        def new_messages(self, messages):
            logging.info("Sending new message to %r listeners", len(self.waiters))
            for future in self.waiters:
                future.set_result(messages)
            self.waiters = set()
            self.cache.extend(messages)
            if len(self.cache) > self.cache_size:
                self.cache = self.cache[-self.cache_size:]
    
    
    # Making this a non-singleton is left as an exercise for the reader.
    global_message_buffer = MessageBuffer()
    
    
    class MainHandler(tornado.web.RequestHandler):
        def get(self):
            self.render("index.html", messages=global_message_buffer.cache)
    
    
    class MessageNewHandler(tornado.web.RequestHandler):
        def post(self):
            message = {
                "id": str(uuid.uuid4()),
                "body": self.get_argument("body"),
            }
            # to_basestring is necessary for Python 3's json encoder,
            # which doesn't accept byte strings.
            message["html"] = tornado.escape.to_basestring(
                self.render_string("message.html", message=message))
            if self.get_argument("next", None):
                self.redirect(self.get_argument("next"))
            else:
                self.write(message)
            global_message_buffer.new_messages([message])
    
    
    class MessageUpdatesHandler(tornado.web.RequestHandler):
        @gen.coroutine
        def post(self):
            cursor = self.get_argument("cursor", None)
            # Save the future returned by wait_for_messages so we can cancel
            # it in wait_for_messages
            self.future = global_message_buffer.wait_for_messages(cursor=cursor)
            messages = yield self.future
            if self.request.connection.stream.closed():
                return
            self.write(dict(messages=messages))
    
        def on_connection_close(self):
            global_message_buffer.cancel_wait(self.future)
    
    
    def main():
        parse_command_line()
        app = tornado.web.Application(
            [
                (r"/", MainHandler),
                (r"/a/message/new", MessageNewHandler),
                (r"/a/message/updates", MessageUpdatesHandler),
                ],
            cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__",
            template_path=os.path.join(os.path.dirname(__file__), "templates"),
            static_path=os.path.join(os.path.dirname(__file__), "static"),
            xsrf_cookies=True,
            debug=options.debug,
            )
        app.listen(options.port)
        tornado.ioloop.IOLoop.current().start()
    
    
    if __name__ == "__main__":
        main()
    

    页面index.html

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="UTF-8">
        <title>Tornado Chat Demo</title>
        <link rel="stylesheet" href="{{ static_url("chat.css") }}" type="text/css">
      </head>
      <body>
        <div id="body">
          <div id="inbox">
            {% for message in messages %}
              {% module Template("message.html", message=message) %}
            {% end %}
          </div>
          <div id="input">
            <form action="/a/message/new" method="post" id="messageform">
              <table>
                <tr>
                  <td><input type="text" name="body" id="message" style="500px"></td>
                  <td style="padding-left:5px">
                    <input type="submit" value="{{ _("Post") }}">
                    <input type="hidden" name="next" value="{{ request.path }}">
                    {% module xsrf_form_html() %}
                  </td>
                </tr>
              </table>
            </form>
          </div>
        </div>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js" type="text/javascript"></script>
        <script src="{{ static_url("chat.js") }}" type="text/javascript"></script>
      </body>
    </html>
    

    消息页面message.html

    <div class="message" id="m{{ message["id"] }}">{% module linkify(message["body"]) %}</div>
    

    css页面chat.css

    body {
      background: white;
      margin: 10px;
    }
    
    body,
    input {
      font-family: sans-serif;
      font-size: 10pt;
      color: black;
    }
    
    table {
      border-collapse: collapse;
      border: 0;
    }
    
    td {
      border: 0;
      padding: 0;
    }
    
    #body {
      position: absolute;
      bottom: 10px;
      left: 10px;
    }
    
    #input {
      margin-top: 0.5em;
    }
    
    #inbox .message {
      padding-top: 0.25em;
    }
    
    #nav {
      float: right;
      z-index: 99;
    }
    

    js处理页面chat.js

    $(document).ready(function() {
        if (!window.console) window.console = {};
        if (!window.console.log) window.console.log = function() {};
    
        $("#messageform").on("submit", function() {
            newMessage($(this));
            return false;
        });
        $("#messageform").on("keypress", function(e) {
            if (e.keyCode == 13) {
                newMessage($(this));
                return false;
            }
            return true;
        });
        $("#message").select();
        updater.poll();
    });
    
    function newMessage(form) {
        var message = form.formToDict();
        var disabled = form.find("input[type=submit]");
        disabled.disable();
        $.postJSON("/a/message/new", message, function(response) {
            updater.showMessage(response);
            if (message.id) {
                form.parent().remove();
            } else {
                form.find("input[type=text]").val("").select();
                disabled.enable();
            }
        });
    }
    
    function getCookie(name) {
        var r = document.cookie.match("\b" + name + "=([^;]*)\b");
        return r ? r[1] : undefined;
    }
    
    jQuery.postJSON = function(url, args, callback) {
        args._xsrf = getCookie("_xsrf");
        $.ajax({url: url, data: $.param(args), dataType: "text", type: "POST",
                success: function(response) {
            if (callback) callback(eval("(" + response + ")"));
        }, error: function(response) {
            console.log("ERROR:", response);
        }});
    };
    
    jQuery.fn.formToDict = function() {
        var fields = this.serializeArray();
        var json = {};
        for (var i = 0; i < fields.length; i++) {
            json[fields[i].name] = fields[i].value;
        }
        if (json.next) delete json.next;
        return json;
    };
    
    jQuery.fn.disable = function() {
        this.enable(false);
        return this;
    };
    
    jQuery.fn.enable = function(opt_enable) {
        if (arguments.length && !opt_enable) {
            this.attr("disabled", "disabled");
        } else {
            this.removeAttr("disabled");
        }
        return this;
    };
    
    var updater = {
        errorSleepTime: 500,
        cursor: null,
    
        poll: function() {
            var args = {"_xsrf": getCookie("_xsrf")};
            if (updater.cursor) args.cursor = updater.cursor;
            $.ajax({url: "/a/message/updates", type: "POST", dataType: "text",
                    data: $.param(args), success: updater.onSuccess,
                    error: updater.onError});
        },
    
        onSuccess: function(response) {
            try {
                updater.newMessages(eval("(" + response + ")"));
            } catch (e) {
                updater.onError();
                return;
            }
            updater.errorSleepTime = 500;
            window.setTimeout(updater.poll, 0);
        },
    
        onError: function(response) {
            updater.errorSleepTime *= 2;
            console.log("Poll error; sleeping for", updater.errorSleepTime, "ms");
            window.setTimeout(updater.poll, updater.errorSleepTime);
        },
    
        newMessages: function(response) {
            if (!response.messages) return;
            updater.cursor = response.cursor;
            var messages = response.messages;
            updater.cursor = messages[messages.length - 1].id;
            console.log(messages.length, "new messages, cursor:", updater.cursor);
            for (var i = 0; i < messages.length; i++) {
                updater.showMessage(messages[i]);
            }
        },
    
        showMessage: function(message) {
            var existing = $("#m" + message.id);
            if (existing.length > 0) return;
            var node = $(message.html);
            node.hide();
            $("#inbox").append(node);
            node.slideDown();
        }
    };
    

    可在这下载:下载地址

  • 相关阅读:
    解决windows 下Java编译和运行版本不一致的错误has been compiled by a more recent version
    解决两个OpenCV 报错 (raise.c and GTK) ,重新安装和编译
    Java|如何使用“Java”爬取电话号码(转载)
    Java手机号码工具类(判断运营商、获取归属地)以及简要的原理跟踪(转载)
    Caused by: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
    mysql分库分表
    python将html文件转换为pdf
    mysql获取字段名和对应的注释
    mysql大表查询慢对应方案
    harbor安装
  • 原文地址:https://www.cnblogs.com/ccorz/p/6001269.html
Copyright © 2011-2022 走看看