在線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/wangzhu/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,完美解決