zoukankan      html  css  js  c++  java
  • web聊天室

    原理:

    长轮询:

    客户端向服务器发送Ajax请求,服务器接到请求后hold住连接,直到有新消息才返回响应信息并关闭连接,客户端处理完响应信息后再向服务器发送新的请求。

    优点:在无消息的情况下不会频繁的请求,耗费资源小。兼容性好,所以目前为大多数

    缺点:服务器hold连接会消耗资源,返回数据顺序无保证,难于管理维护

    实例:Web微信


    Websocket:

    WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。

    浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

    优点:真正的双向通信,未来的趋势

    缺点:浏览器兼容性不好,IE9以下全部阵亡

    实现:

    1、Flask-WebSocket实现

     1 #coding=utf-8
     2 
     3 from flask import Flask, request, render_template
     4 from geventwebsocket.websocket import WebSocket
     5 from geventwebsocket.handler import WebSocketHandler
     6 from gevent.pywsgi import WSGIServer
     7 import json
     8 
     9 
    10 app = Flask(__name__)
    11 
    12 @app.route("/index/")
    13 def index():
    14     return render_template("ws.html")
    15 
    16 
    17 user_socket_dict = {}
    18 @app.route("/ws/<username>")
    19 def ws(username):
    20     user_socket = request.environ.get("wsgi.websocket") # type:
    21     if not user_socket:
    22         return "使用WebSocket方式连接"
    23     user_socket_dict[username] = user_socket
    24     print(user_socket_dict)
    25     while True:
    26         try:
    27             # 接收客户端传入数据
    28             user_msg = user_socket.receive()
    29             # 将以用户名为键,连接为值的字典迭代出所有的键值对
    30             for k,v in user_socket_dict.items():
    31                 # 将当前发送数据的用户以及数据放在一起组成字典
    32                 # print(v)
    33                 who_send_msg = {
    34                     "send_user": username,
    35                     "send_msg": user_msg
    36                 }
    37                 # print(who_send_msg)
    38                 # 如果当前连接与迭代出来的连接相同,就跳过本次循环
    39                 if user_socket == v:
    40                     continue
    41                 # 否则 将用户以及用户数据以json格式发送出去
    42                 v.send(json.dumps(who_send_msg))
    43         except Exception as  e:
    44             # 当捕捉到异常的时候就将当前用户从字典中删除
    45             user_socket_dict.pop(username)
    46 
    47 if __name__ == '__main__':
    48     http_serv = WSGIServer(("0.0.0.0",5000),app,handler_class=WebSocketHandler)
    49     http_serv.serve_forever()
    app.py
     1 <!DOCTYPE html>
     2 <html lang="en">
     3 <head>
     4     <meta charset="UTF-8">
     5     <title>Title</title>
     6     <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
     7 
     8 </head>
     9 <body>
    10 <div class="container-fluid">
    11     <div class="row">
    12         <div class="col-md-4">
    13             <h2 style="text-align: center">激情群聊</h2>
    14             <div class="form-group">
    15                 <label for="username">你是谁:</label>
    16                 <input class="form-control" type="text" id="username">
    17             </div>
    18             <button id="create_ws" onclick="go_to()" class="btn btn-warning">创建ws连接</button>
    19             <div style=" 100%; height: 300px; border: thick;background-color: cadetblue" id="chat_window" class="input-group">
    20             </div>
    21 
    22              <div class="input-group">
    23                   <input type="text" class="form-control" placeholder="" id="send_msg">
    24                   <span class="input-group-btn">
    25                     <button class="btn btn-default" type="button" id="btn_send">发送消息</button>
    26                   </span>
    27                 </div>
    28               </div>
    29         </div>
    30     </div>
    31 </div>
    32 
    33 <script type="application/javascript">
    34 
    35     var ws_url="ws://192.168.31.218:5000/ws/";
    36     var ws =null;
    37 
    38     function go_to() {
    39         var username = document.getElementById('username');
    40         ws = new WebSocket(ws_url+username.value);
    41         ws.onmessage=function(serv_msg){
    42              msg=JSON.parse(serv_msg.data);
    43              //console.log(serv_msg.data);
    44             create_chart('y',msg)
    45     };
    46     }
    47 
    48     function create_chart(self,content) {
    49         if (self == "w"){
    50             self = "right";
    51             var spantag = document.createElement("span");
    52             spantag.innerText= content.send_msg;
    53             var spantag1 = document.createElement("span");
    54             spantag1.innerText=':我';
    55         }else{
    56             self = "left";
    57             var spantag = document.createElement("span");
    58             spantag.innerText=content.send_user+':';
    59 
    60             var spantag1 = document.createElement("span");
    61             spantag1.innerText=content.send_msg;
    62 
    63         }
    64         var divtag = document.createElement("div");
    65         divtag.style="text-align:"+self;
    66         divtag.appendChild(spantag);
    67         divtag.appendChild(spantag1);
    68         var char_window = document.getElementById('chat_window');
    69         char_window.appendChild(divtag);
    70 
    71     }
    72     document.getElementById("btn_send").addEventListener("click",function () {
    73 
    74         var send_msg=document.getElementById("send_msg");
    75         ws.send(send_msg.value);
    76 
    77         var s_msg = {send_msg:send_msg.value};
    78         create_chart('w',s_msg);
    79         send_msg.value='';
    80     })
    81 
    82 </script>
    83 </body>
    84 </html>
    ws.html

    注意:这里Flask启动时不能使用pycharm启动,应使用命令行启动

    Flask不能原生支持WebSocket,需要引入三方模块

    2、Flask-长轮询实现

     1 #-*- coding: utf-8 -*-
     2 from flask import Flask,render_template,session,request,jsonify
     3 import queue,json
     4 
     5 app = Flask(__name__)
     6 app.secret_key = '1'
     7 
     8 @app.route('/')
     9 def hello_world():
    10     return 'Hello World!'
    11 
    12 user_dict = {}
    13 
    14 @app.route('/web_chat',methods=['GET','POST'])
    15 def web_chat():
    16     #前端发送过来信息,将信息群发给除发送用户外的所有用户
    17     if request.method == 'POST':
    18         msg = request.form.get('msg')
    19         username = request.form.get('username')
    20         msg = {'send_msg':msg,'send_user':username}
    21         current_username = session['current_username']
    22         for user,q in user_dict.items():
    23             if user!=current_username:
    24                 q.put(msg)
    25         return jsonify({'status': True})
    26     else:
    27         return render_template('ws.html')
    28 
    29 @app.route('/get_msg',methods=['GET','POST'])
    30 def get_msg():
    31     #前端长轮询,后端等待数据,把username存入session中
    32     if request.method == 'POST':
    33         username = session['current_username']
    34         ret = {'status': True, 'data': None}
    35         q = user_dict[username]
    36         try:
    37             msg = q.get(timeout=50)
    38             ret['data'] = msg
    39         except:
    40             ret['status'] = False
    41         return jsonify(ret)
    42 
    43 @app.route('/web_chat_join/<username>',methods=['GET','POST'])
    44 def web_chat_join(username):
    45     #加入聊天室,把用户加入user_dict,并为用户创建消息队列
    46     if request.method == 'POST':
    47         user_dict[username] = queue.Queue()
    48         session['current_username'] = username
    49         return jsonify({'status': True})
    50 
    51 if __name__ == '__main__':
    52     #使用长轮询时,由于消息队列会阻塞,所以必须使用多线程
    53     app.run(host='0.0.0.0',threaded=True)
    app.py
      1 <!DOCTYPE html>
      2 <html lang="en">
      3 <head>
      4     <meta charset="UTF-8">
      5     <title>Title</title>
      6     <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
      7 
      8 </head>
      9 <body>
     10 <div class="container-fluid">
     11     <div class="row">
     12         <div class="col-md-4">
     13             <h2 style="text-align: center">激情群聊</h2>
     14             <div class="form-group">
     15                 <label for="username">你是谁:</label>
     16                 <input class="form-control" type="text" id="username">
     17             </div>
     18             <button id="join_chat" onclick="go_to()" class="btn btn-warning">加入聊天室</button>
     19             <div style=" 100%; height: 300px; border: thick;background-color: cadetblue" id="chat_window" class="input-group">
     20             </div>
     21 
     22              <div class="input-group">
     23                   <input type="text" class="form-control" placeholder="" id="send_msg">
     24                   <span class="input-group-btn">
     25                     <button class="btn btn-default" type="button" id="btn_send">发送消息</button>
     26                   </span>
     27                 </div>
     28               </div>
     29         </div>
     30     </div>
     31 </div>
     32 
     33 <script type="application/javascript">
     34 
     35     //加入聊天室
     36     function go_to() {
     37         var username = document.getElementById('username');
     38         // 新建XMLHttpRequest对象
     39         var req = new XMLHttpRequest();
     40         // 状态发生变化时,函数被回调
     41         req.onreadystatechange=function () {
     42             //状态码为4代表请求完成
     43             if (req.readyState==4&&req.status==200) {
     44                 data = req.responseText;
     45                 data = JSON.parse(data);
     46                 get_msg();
     47             }
     48         };
     49         //post方式建立连接
     50         req.open('post','http://127.0.0.1:5000/web_chat_join/'+username.value,true);
     51         //post请求的编码方式,这是浏览器的原生 form 表单的默认编码方式
     52         req.setRequestHeader("Content-type","application/x-www-form-urlencoded");
     53         //发送请求
     54         req.send("chat_join");
     55     }
     56 
     57     //长轮询递归获取数据
     58     function get_msg() {
     59         // 新建XMLHttpRequest对象
     60         var req = new XMLHttpRequest();
     61         // 状态发生变化时,函数被回调
     62         req.onreadystatechange=function () {
     63             //状态码为4代表请求完成
     64             if (req.readyState==4&&req.status==200) {
     65                 data = req.responseText;
     66                 data = JSON.parse(data);
     67                 console.log(data);
     68                 //将收到的数据显示在前端
     69                 create_chart('',data.data);
     70                 //长轮询递归获取数据
     71                 get_msg();
     72             }
     73         };
     74         //post方式建立连接
     75         req.open('post','http://127.0.0.1:5000/get_msg',true);
     76         //post请求的编码方式,这是浏览器的原生 form 表单的默认编码方式
     77         req.setRequestHeader("Content-type","application/x-www-form-urlencoded");
     78         //发送请求
     79         req.send("get_msg");
     80     }
     81     //将数据显示在页面,我发的在右边,别人的在左边
     82     function create_chart(self,content) {
     83         if (self == "w"){
     84             self = "right";
     85             var spantag = document.createElement("span");
     86             spantag.innerText= content.send_msg;
     87             var spantag1 = document.createElement("span");
     88             spantag1.innerText=':我';
     89         }else{
     90             self = "left";
     91             var spantag = document.createElement("span");
     92             spantag.innerText=content.send_user+':';
     93 
     94             var spantag1 = document.createElement("span");
     95             spantag1.innerText=content.send_msg;
     96 
     97         }
     98         var divtag = document.createElement("div");
     99         divtag.style="text-align:"+self;
    100         divtag.appendChild(spantag);
    101         divtag.appendChild(spantag1);
    102         var char_window = document.getElementById('chat_window');
    103         char_window.appendChild(divtag);
    104 
    105     }
    106     //点击发送按钮,发送数据
    107     document.getElementById("btn_send").addEventListener("click",function () {
    108 
    109         var send_msg=document.getElementById("send_msg");
    110         var username = document.getElementById('username');
    111         // 新建XMLHttpRequest对象
    112         var req = new XMLHttpRequest();
    113         // 状态发生变化时,函数被回调
    114         req.onreadystatechange=function () {
    115             //状态码为4代表请求完成
    116             if (req.readyState==4&&req.status==200) {
    117                 data = req.responseText;
    118                 data = JSON.parse(data);
    119             }
    120         };
    121         //post方式建立连接
    122         req.open('post','http://127.0.0.1:5000/web_chat',true);
    123         //post请求的编码方式,这是浏览器的原生 form 表单的默认编码方式
    124         req.setRequestHeader("Content-type","application/x-www-form-urlencoded");
    125         //发送请求
    126         req.send('msg='+send_msg.value+"&username="+username.value);
    127         var s_msg = {send_msg:send_msg.value};
    128 
    129         create_chart('w',s_msg);
    130         send_msg.value='';
    131     })
    132 
    133 </script>
    134 </body>
    135 </html>
    ws.html

     注意:这里Flask启动时不能使用pycharm启动,应使用命令行启动

    3、Django-长轮询实现

     1 <!DOCTYPE html>
     2 <html lang="en">
     3 <head>
     4     <meta charset="UTF-8">
     5     <title>Title</title>
     6     <script src="static/jquery-3.3.1.js"></script>
     7     <link href="static/chatbar.css" rel="stylesheet" type="text/css">
     8 </head>
     9 <body>
    10 <div class="chat">
    11     <div class="chat-title">
    12         <div class="chat-title-font">聊天室</div>
    13     </div>
    14     <div class="chat-left">
    15         <div class="chat-content">
    16 
    17         </div>
    18         <div class="chat-input">
    19             <div>聊天栏</div>
    20             <div><input id="input_contnet" type="text" style=" 500px;height: 50px"></div>
    21             <div><input id="username" type="text"></div>
    22             <div><input id="input_btn" type="button" value="发送信息" style="margin-left: 490px"></div>
    23         </div>
    24     </div>
    25     <div class="chat-right">
    26 
    27     </div>
    28     <div class="clear"></div>
    29 </div>
    30 
    31 <script>
    32     $('#input_btn').click(function () {
    33         var input_contnet = document.getElementById('input_contnet');
    34         var date = new Date();
    35         $('.chat-content').append('<div>'+input_contnet.value + '      ' + 'Time:' + date +'</div>');
    36         $('#input_contnet').animate({scrollTop:$('#input_contnet').scrollHeight},500);
    37         $.ajax(
    38             {
    39                 type: 'post',
    40                 url: 'chatbar',
    41                 data : {
    42                     'from':$('#username').val(),
    43                     'input_contnet':input_contnet.value,
    44                 },
    45                 success: function (data) {
    46                     console.log(data);
    47                 }
    48             }
    49         )
    50     });
    51     $(document).ready(function () {
    52         get_msg()
    53     });
    54     function get_msg() {
    55         $.ajax(
    56             {
    57                 type:'post',
    58                 url:'get_msg',
    59                 dataType:'json',
    60                 data:{'from':$('#username').val(),},
    61                 success:function (data) {
    62                     console.log(data);
    63                     get_msg();
    64                 }
    65             }
    66         )
    67     }
    68 </script>
    69 </body>
    70 </html>
    HMTL
     1 .chat{
     2     border: 4px solid black;
     3     margin-top: 50px;
     4     margin-left: 200px;
     5     margin-right: 200px;
     6     height: 450px;
     7 }
     8 
     9 .chat-title{
    10     border: 1px solid red;
    11     margin-left: 20px;
    12     margin-top: 20px;
    13     margin-right: 20px;
    14     height: 30px;
    15 
    16 }
    17 
    18 .chat-title-font{
    19     text-align: center;
    20     margin-top: 1px;
    21     font-weight: bolder;
    22     color: darkblue;
    23     font-size: larger;
    24 }
    25 
    26 .chat-left{
    27     border: 1px solid blue;
    28     margin-left: 20px;
    29     margin-top: 10px;
    30     height: 350px;
    31     width: 600px;
    32     float: left;
    33 }
    34 
    35 .chat-content{
    36     border: 1px solid red;
    37     margin-left: 20px;
    38     margin-top: 10px;
    39     margin-right: 20px;
    40     overflow: auto;
    41     height: 200px;
    42 }
    43 
    44 .chat-input{
    45     border: 1px solid red;
    46     margin-left: 20px;
    47     margin-top: 10px;
    48     margin-right: 20px;
    49     height: 100px;
    50 }
    51 
    52 .clear{
    53     clear: both;
    54     display: none;
    55 }
    56 .chat-right{
    57     border: 1px solid rebeccapurple;
    58     margin-left: 640px;
    59     margin-top: 10px;
    60     height: 350px;
    61     width: 280px;
    62 }
    CSS
     1 # -*- coding: utf-8 -*-
     2 from __future__ import unicode_literals
     3 from django.shortcuts import render,HttpResponse
     4 import json,Queue
     5 
     6 # Create your views here.
     7 
     8 def ajax(req):
     9     if req.method == 'POST':
    10         print(req.POST)
    11         data = req.POST.get('text1')
    12         data_dic = {'data':data}
    13         return HttpResponse(json.dumps(data_dic))
    14     else:
    15         return render(req, 'test.html')
    16 
    17 def jsonp(req):
    18     callback = req.GET.get('callback')
    19     data={'1':'a','2':'b','3':'c'}
    20     return HttpResponse('%s(%s)'%(callback,json.dumps(data)))
    21 
    22 global_msg_dict={}
    23 
    24 def chatbar(req):
    25     if req.method == 'POST':
    26         from_user = req.POST.get('from')
    27         json_data_dic=json.dumps(req.POST)
    28         if not global_msg_dict.get(from_user):
    29             global_msg_dict[from_user] = Queue.Queue()
    30         global_msg_dict[from_user].put(json_data_dic)
    31 
    32         return HttpResponse('----收到消息----')
    33     else:
    34         return render(req,'chatbar.html')
    35 
    36 
    37 def get_msg(req):
    38     if req.method=='POST':
    39         msg_list = []
    40         from_user = 'chenxin'
    41         if not global_msg_dict.get(from_user):
    42             global_msg_dict[from_user] = Queue.Queue()
    43         msg_count = global_msg_dict[from_user].qsize()
    44         if msg_count>0:
    45             for msg in range(msg_count):
    46                 msg_list.append(global_msg_dict[from_user].get())
    47         else:
    48             try:
    49                 print('try')
    50                 msg_list.append(global_msg_dict[from_user].get(timeout=100))
    51                 print(msg_list)
    52             except Queue.Empty:
    53                 pass
    54         return HttpResponse(json.dumps(msg_list))
    python

    4、Tornado-WebSocket实现

     1 <!DOCTYPE html>
     2 <html lang="en">
     3 <head>
     4     <meta charset="UTF-8">
     5     <title>Title</title>
     6 </head>
     7 <body>
     8 <div id="contents" style="height:500px;overflow:auto;background-color: darkgray;"></div>
     9 <div>
    10     <textarea id="msg"></textarea>
    11     <input type="button" value="发送" onclick="sendmsg()">
    12 </div>
    13 </body>
    14 <script>
    15     var ws = new WebSocket("ws://"+location.host+"/chat");
    16     var contents = document.getElementById("contents");
    17     var msg = document.getElementById("msg");
    18     ws.onmessage = function (e) {
    19         var msg1 = "<p>"+e.data+"</p>";
    20         contents.innerHTML+=msg1;
    21     }
    22     function sendmsg() {
    23         ws.send(msg.value);
    24         msg.value = "";
    25     }
    26 </script>
    27 </html>
    chat.html
     1 # coding:utf-8
     2 
     3 import tornado.web
     4 import tornado.ioloop
     5 import tornado.httpserver
     6 import tornado.options
     7 import os
     8 import datetime
     9 
    10 from tornado.web import RequestHandler
    11 from tornado.options import define, options
    12 from tornado.websocket import WebSocketHandler
    13 
    14 define("port", default=8000, type=int)
    15 
    16 class IndexHandler(RequestHandler):
    17     def get(self):
    18         self.render("chat.html")
    19 
    20 class ChatHandler(WebSocketHandler):
    21 
    22     users = set()  # 用来存放在线用户的容器
    23 
    24     def open(self):
    25         self.users.add(self)  # 建立连接后添加用户到容器中
    26         for u in self.users:  # 向已在线用户发送消息
    27             u.write_message(u"[%s]-[%s]-进入聊天室" % (self.request.remote_ip, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
    28 
    29     def on_message(self, message):
    30         for u in self.users:  # 向在线用户广播消息
    31             u.write_message(u"[%s]-[%s]-说:%s" % (self.request.remote_ip, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), message))
    32 
    33     def on_close(self):
    34         self.users.remove(self) # 用户关闭连接后从容器中移除用户
    35         for u in self.users:
    36             u.write_message(u"[%s]-[%s]-离开聊天室" % (self.request.remote_ip, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
    37 
    38     def check_origin(self, origin):
    39         return True  # 允许WebSocket的跨域请求
    40 
    41 if __name__ == '__main__':
    42     tornado.options.parse_command_line()
    43     app = tornado.web.Application([
    44             (r"/", IndexHandler),
    45             (r"/chat", ChatHandler),
    46         ],
    47         #static_path = os.path.join(os.path.dirname(__file__), "static"),
    48         #template_path = os.path.join(os.path.dirname(__file__), "template"),
    49         debug = True
    50         )
    51     http_server = tornado.httpserver.HTTPServer(app)
    52     http_server.listen(options.port,address="0.0.0.0")
    53     tornado.ioloop.IOLoop.current().start()
    chat.py

     由于Tornado本身是异步的,原生支持WebSocket

  • 相关阅读:
    做人做事
    不骄不躁
    争取
    收入
    Windows 7下的Comodo Firewall免费防火墙
    成功水平
    成家立业
    Windows无法安装到GPT格式磁盘的根本解决办法
    安装Windows10操作系统
    安装操作系统的几种方式
  • 原文地址:https://www.cnblogs.com/cx59244405/p/10165203.html
Copyright © 2011-2022 走看看