2020年3月7日土曜日

WebSocketでチャット(Tornadoを使う)

暇なので、WebSocketでチャットしてみた。
(Tornadoを使う)
クライアントからアクセスするIPアドレスやポートは適当に変更する。

※追記3(2020/03/09 20:40)※
HTTPとWebSocketサーバを一つにしてみた。

サーバ(HTTPサーバ及びWebSocketサーバ)
例えば:tornado_server.py というファイル名にする。
このファイルを置いたディレクトリに templates と static という名の子ディレクトリを作っておく。
  1. #!/usr/bin/env python
  2. #-*- coding:utf-8 -*-
  3.  
  4. import tornado.ioloop
  5. import tornado.web
  6. import tornado.websocket
  7. import datetime
  8. import os.path
  9.  
  10. cl = []
  11. dic = {}
  12. todaydetail = datetime.datetime.today()
  13. today = todaydetail.strftime("%Y_%m_%d")
  14. print(today)
  15. today_log = str(today) + "_log.txt"
  16. print(today_log)
  17. if not os.path.exists(today_log):
  18. f = open(today_log, "w")
  19.  
  20. #クライアントからメッセージを受けるとopen → on_message → on_closeが起動する
  21. class WebSocketHandler(tornado.websocket.WebSocketHandler):
  22. def check_origin(self, origin):
  23. return True
  24.  
  25. #websocketオープン
  26. def open(self):
  27. print("open")
  28. if self not in cl:
  29. cl.append(self)
  30. print(len(dic))
  31. dic[self] = len(dic)
  32. print(self)
  33.  
  34. f = open(today_log, "r")
  35. for row in f:
  36. self.write_message(row.strip())
  37. f.close()
  38.  
  39. self.write_message("Welcome !;;")
  40. #処理
  41. def on_message(self, message):
  42. print("on_message")
  43. mess = str(message) + "; ID=" + str(self)[37:47]
  44. f = open(today_log, "a")
  45. f.write(mess + "\n")
  46. f.close()
  47.  
  48. for client in cl:
  49. print(str(self))
  50. print(message)
  51. #クライアントへメッセージを送信
  52. client.write_message(mess)
  53. #websockeクローズ
  54. def on_close(self):
  55. print("close")
  56. if self in cl:
  57. cl.remove(self)
  58.  
  59. #HTTPサーバ
  60. class MainHandler(tornado.web.RequestHandler):
  61. def get(self):
  62. self.render("index.html")
  63.  
  64. app = tornado.web.Application([
  65. (r"/", MainHandler),
  66. (r"/websocket", WebSocketHandler),
  67. ],
  68.  
  69. #HTTPサーバで使うpathを設定
  70. template_path = os.path.join(os.getcwd(), "templates"),
  71. static_path = os.path.join(os.getcwd(), "static"),
  72. )
  73.  
  74. if __name__ == "__main__":
  75. app.listen(8888)
  76. tornado.ioloop.IOLoop.instance().start()
  77.  

インデックス
index.html と言うファイル名にする。これを templates ディレクトリに置く。
HTTPサーバにアクセスすると、この index.html が呼ばれる。
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8"/>
  5. <meta name="viewport" content="width=device-width,
  6. initial-scale=1.0,user-scalable=yes" />
  7. </head>
  8. <body>
  9. Tornado is awesome !<br>
  10. <a href="{{static_url("websocket_chat_client.html")}}">Chat Client</a>
  11. </body>
  12. </html>
  13.  

クライアント
websocket_chat_client.html と言うファイル名にする。これを static ディレクトリに置く。
インデックスのリンクから呼び出される。
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8"/>
  5. <meta name="viewport" content="width=device-width,
  6. initial-scale=1.0,user-scalable=yes" />
  7. <title>WebSocketでチャットしてみよう</title>
  8. <style>
  9. #name{
  10. width:100px;
  11. height:20px;
  12. }
  13.  
  14. #message{
  15. width:250px;
  16. height:20px;
  17. }
  18. </style>
  19. <script langage="JavaScript">
  20. ws = new WebSocket("ws://192.168.1.11:8888/websocket");
  21.  
  22. ws.onopen = function(e) {
  23. // ws.send("Hi Open; ");
  24. }
  25.  
  26. ws.onmessage = function(e) {
  27. var mon = document.getElementById("monitor");
  28. var mes = e.data;
  29. mes_line = mes.split(";");
  30.  
  31. var div = document.createElement("div");
  32. mon.appendChild(div);
  33. div.style.width = "500px";
  34. div.style.padding = "10px 10px 10px 10px";
  35. div.style.margin = "10px 0px 10px 0px";
  36. div.style.border = "solid 1px #0000ff";
  37. var div_att = "<div class='mess' style='overflow:auto;padding:5px 5px 5px 15px'>";
  38. div.innerHTML = mes_line[0] + mes_line[2] + div_att + mes_line[1] + "</div>";
  39.  
  40. //alert(document.body.clientHeight);
  41. var c_height = document.body.clientHeight;
  42. if(mes_line[0] == "Welcome !"){
  43. c_height = 0;
  44. }
  45. window.scrollTo(0, c_height);
  46. }
  47.  
  48. function button00(mes){
  49. var weeks = new Array('日','月','火','水','木','金','土');
  50. var now = new Date();
  51.  
  52. var year = now.getYear(); // 年
  53. var month = now.getMonth() + 1; // 月
  54. var day = now.getDate(); // 日
  55. var week = weeks[ now.getDay() ]; // 曜日
  56. var hour = now.getHours(); // 時
  57. var min = now.getMinutes(); // 分
  58. var sec = now.getSeconds(); // 秒
  59.  
  60. if(year < 2000) { year += 1900; }
  61.  
  62. // 数値が1桁の場合、頭に0を付けて2桁で表示する指定
  63. if(month < 10) { month = "0" + month; }
  64. if(day < 10) { day = "0" + day; }
  65. if(hour < 10) { hour = "0" + hour; }
  66. if(min < 10) { min = "0" + min; }
  67. if(sec < 10) { sec = "0" + sec; }
  68.  
  69. var now_date = year + "/" + month + "/" + day + "(" + week + ")";
  70. var now_time = hour + ":" + min + ":" + sec;
  71. var nam = document.getElementById("name").value;
  72. ws.send(nam + " " + now_date + " " + now_time + ";" + mes);
  73.  
  74. document.getElementById("message").value = "";
  75. }
  76.  
  77. </script>
  78. </head>
  79. <body>
  80. <h1>WebSocketでチャットしてみよう</h1>
  81. <h3>メッセージ</h3>
  82. <div id="monitor"></div>
  83.  
  84. <p>
  85. 表示する名前
  86. <input type="text" id="name" value="ななし" class="name" />
  87. </p>
  88.  
  89. <p>
  90. <input type="text" id="message" value="text" class="message"/>
  91. <button onClick="button00(document.getElementById('message').value)">送信</button>
  92. </p>
  93. <p>
  94. ※ WebSocketのテストです ※
  95. </p>
  96. </body>
  97. </html>
  98.