websocket 聊天室 demo ( tornado + nginx + wss + 在线demo)

在线demo(已停止服务)

 

前端代码(来源网友,自己稍作修改): 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title> WebSocket ChatRoom </title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .box{
            width: 800px;
            margin-left: auto;
            margin-right: auto;
            margin-top: 25px;
        }
        #text{
            width: 685px;
            height: 130px;
            border: 1px solid skyblue;
            border-radius: 10px;
            font-size: 20px;
            text-indent: 1em;
            resize:none;
            outline: none;
        }
        #text::placeholder{
            color: skyblue;
        }
        .btn{
            width: 100px;
            margin: -27px 0 0px 8px;
        }
        #messages{
            padding-left: 10px;
            font-size: 25px;
        }
        #messages li{
            list-style: none;
            color: #000;
            line-height: 30px;
            font-size: 18px;

        }
    </style>
</head>
<body>
    <div class="box">
        <div>
            <textarea id="text" placeholder="请输入您的内容"></textarea>
            <a href="javascript:WebSocketSend();" class="btn btn-primary">发送</a>
        </div>
        <ul id="messages">
        </ul>
    </div>
    
    <script type="text/javascript">
        var mes = document.getElementById('messages');

        if('WebSocket' in window){  /*判断浏览器是否支持WebSocket接口*/
            /*创建创建 WebSocket 对象,协议本身使用新的ws://URL格式*/
            var Socket = new WebSocket("wss://10.0.0.189/wss/chat")
            /*连接建立时触发*/
            Socket.onopen = function () {
                mes.innerHTML += "<br>连接已建立!╰( ̄▽ ̄)╮";
                //alert("连接已建立,可以进行通信")
            };
            /*客户端接收服务端数据时触发*/
            Socket.onmessage = function (ev) {
                var received_msg = ev.data; /*接受消息*/
                var aLi = "<li>" + received_msg + "</li>";
                mes.innerHTML += aLi;
                /*jq方式*/
                // $(mes).append($(aLi));
            };
            /*连接关闭时触发*/
            Socket.onclose = function () {
                mes.innerHTML += "<br>连接已经关闭...(┬_┬)";
            };
        }else{
            /*浏览器不支持 WebSocket*/
            alert("您的浏览器不支持 WebSocket!");
        }
        function WebSocketSend() {
            /*form 里的Dom元素(input select checkbox textarea radio)都是value*/
            var send_msg = document.getElementById('text').value;
            //或者JQ中获取
            // var send_msg = $("#text").val();
            /*使用连接发送消息*/
            Socket.send(send_msg);
        }
    </script>
</body>
</html>

后端代码(来源网友,自己稍作修改):

# coding:utf-8

import tornado.web
import tornado.ioloop
import tornado.httpserver
import tornado.options
import os
import datetime

from tornado.web import RequestHandler
from tornado.options import define, options
from tornado.websocket import WebSocketHandler

define("port", default=8000, type=int)

class IndexHandler(RequestHandler):
    def get(self):
        self.render("index.html")

class ChatHandler(WebSocketHandler):

    users = set()  # 用来存放在线用户的容器

    def open(self):
        self.users.add(self)  # 建立连接后添加用户到容器中
        for u in self.users:  # 向已在线用户发送消息
            print("user count {}".format(len(self.users)))
            u.write_message(u"[%s]-[%s]-进入聊天室" % (self.request.remote_ip, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
            print("{} enter room".format(self.request.remote_ip))

    def on_message(self, message):
        for u in self.users:  # 向在线用户广播消息
            u.write_message(u"[%s]-[%s]-说:%s" % (self.request.remote_ip, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), message))
            print("{} says {}".format(self.request.remote_ip, message))

    def on_close(self):
        self.users.remove(self) # 用户关闭连接后从容器中移除用户
        for u in self.users:
            u.write_message(u"[%s]-[%s]-离开聊天室" % (self.request.remote_ip, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
            print("{} leave room".format(self.request.remote_ip))

    def check_origin(self, origin):
        # 接受所有跨源流量
        return True
        
        # 若要允许来自站点任何子域的连接,您可以执行如下操作:
        # parsed_origin = urllib.parse.urlparse(origin)
        # return parsed_origin.netloc.endswith(".mydomain.com")

if __name__ == '__main__':
    tornado.options.parse_command_line()
    app = tornado.web.Application([
            (r"/", IndexHandler),
            (r"/wss/chat", ChatHandler),
        ],
        static_path = os.path.join(os.path.dirname(__file__), "static"),
        template_path = os.path.join(os.path.dirname(__file__), "template"),
        debug = True
        )
    http_server = tornado.httpserver.HTTPServer(app, xheaders=True)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.current().start()

配置NGINX,运行代码:

1. 准备工作: 找一台虚拟机或服务器, 安装nginx, python3.6, tornado,把后端代码拷贝上去。

2. nginx配置:

/etc/nginx/conf.d 下新增个文件 wss.conf

server_tokens off;

upstream wss_svr {
    server 127.0.0.1:8000;
}


server{
    listen 80;
    server_name 10.0.0.189;
    add_header Strict-Transport-Security max-age=15768000;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443;
    server_name 10.0.0.189;

    ssl on;
    index index.html index.htm;
    ssl_certificate   cert/10.0.0.189.crt;
    ssl_certificate_key  cert/10.0.0.189.key;
    ssl_session_timeout 5m;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;

    location /ws {
        alias /var/www/web_chat/;
        index index.html;
    }
    
    location /wss {
        proxy_redirect off;
        proxy_pass http://wss_svr;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $remote_addr:$remote_port;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection upgrade;
        proxy_read_timeout 180s;
    }
}

3. 本地生成证书,放在 /etc/nginx/cert 目录下, 证书脚本如下,生成时输入服务器ip地址

read -p "Enter your domain [www.example.com]: " DOMAIN

echo "Create server key..."
openssl genrsa -des3 -out $DOMAIN.key 1024

echo "Create server certificate signing request..."
SUBJECT="/C=US/ST=Mars/L=iTranswarp/O=iTranswarp/OU=iTranswarp/CN=$DOMAIN"
openssl req -new -subj $SUBJECT -key $DOMAIN.key -out $DOMAIN.csr

echo "Remove password..."
mv $DOMAIN.key $DOMAIN.origin.key
openssl rsa -in $DOMAIN.origin.key -out $DOMAIN.key

echo "Sign SSL certificate..."
openssl x509 -req -days 3650 -in $DOMAIN.csr -signkey $DOMAIN.key -out $DOMAIN.crt

echo "TODO:"
echo "Copy $DOMAIN.crt to /etc/nginx/ssl/$DOMAIN.crt"
echo "Copy $DOMAIN.key to /etc/nginx/ssl/$DOMAIN.key"

echo "Add configuration in nginx:"
echo "server {"
echo "    ..."
echo "    listen 443 ssl;"
echo "    ssl_certificate     /etc/nginx/ssl/$DOMAIN.crt;"
echo "    ssl_certificate_key /etc/nginx/ssl/$DOMAIN.key;"
echo "}"

4. 启动nginx

service nginx start

5. 运行代码

python wss.py

6. 打开前端代码写的html,即可看到界面

效果

 打开时页面:

输入文字时:

服务器打印:

 问题:

服务器打印的远端IP成了127.0.0.1,不是真实的IP地址。

这个问题涉及2个地方,一个是nginx配置,一个是tornado代码

nginx配置是没得问题的:

那就是tornado了,因为没加  xheaders=True

http_server = tornado.httpserver.HTTPServer(app, xheaders=True)

OK,完美解决

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值