在线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,完美解决