文章目录
WebSocket 是一种网络通信协议,很多高级功能都需要它
为什么需要WebSocket?
初次接触 WebSocket 的人,都会问同样的问题:我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来什么好处? 答案很简单,因为 HTTP 协议有一个缺陷:通信只能由客户端发起。 举例来说,我们想了解今天的天气,只能是客户端向服务器发出请求,服务器返回查询结果。HTTP 协议做不到服务器主动向客户端推送信息。 这种单向请求的特点,注定了如果服务器有连续的状态变化(比如客户端发一次请求消息之后,服务器就要连续不断的返回一些指定消息),客户端要获知就非常麻烦。我们只能使用"轮询":每隔一段时候,就发出一个询问,了解服务器有没有新的信息。最典型的场景就是聊天室。 轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。因此,工程师们一直在思考,有没有更好的方法。WebSocket 就是这样发明的。
简介
WebSocket 协议在2008年诞生,2011年成为国际标准。所有浏览器都已经支持了。 它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
其他特点包括:
(1)建立在 TCP 协议之上,服务器端的实现比较容易。
(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
(3)数据格式比较轻量,性能开销小,通信高效。
(4)可以发送文本,也可以发送二进制数据。
(5)没有同源限制,客户端可以与任意服务器通信。
(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL
客户端的简单示例
客户端的简单示例
import tornado.web
import tornado.websocket
import tornado.ioloop
import datetime
class WebSocketHandler(tornado.websocket.WebSocketHandler):
users = set()
def open(self):
self.users.add(self)
self.write_message(u"[%s]-[%s]-进入" % (self.request.remote_ip, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
print(u"[%s]-[%s]-进入" % (self.request.remote_ip, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
def on_message(self, message):
for u in self.users:
u.write_message("收到消息"+message)
def on_close(self):
self.users.remove(self) # 用户关闭连接后从容器中移除用户
print(u"[%s]-[%s]-离开" % (self.request.remote_ip, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
def check_origin(self, origin):
return True
application = tornado.web.Application([
(r"/", WebSocketHandler),
])
if __name__ == "__main__":
application.listen(9006)
tornado.ioloop.IOLoop.instance().start()
具体函数
tornado这个第三方库里面的websocket是一个比较成熟的。
第6行是对于父类tornado.websocket.WebSocketHandler的一个继承。
open
def open(self)
self.users.add(self)
self.write_message(u"[%s]-[%s]-进入" % (self.request.remote_ip, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
print(u"[%s]-[%s]-进入" % (self.request.remote_ip, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
此函数在websocket服务器被客户端连接时被触发,此后执行此函数下内容:
将用户添加到元素集中
向该用户发送xxx进入的消息
打印进入消息
on_message
def on_message(self, message):
for u in self.users:
u.write_message("收到消息"+message)
此函数在websocket服务器接收到客户端消息时被触发,此后执行此函数下内容:
向所有用户发送消息:收到消息xxx
message为服务器接收到的客户端的消息
on_close
def on_close(self):
self.users.remove(self) # 用户关闭连接后从容器中移除用户
print(u"[%s]-[%s]-离开" % (self.request.remote_ip, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
此函数在websocket服务器接收到客户端用户离开消息时被触发,此后执行此函数下内容:
打印用户离开的消息
application
这是必须有的一个语句,WebSocketHandler为之前定义的类名
application = tornado.web.Application([
(r"/", WebSocketHandler),
])
启动websocket
if __name__ == "__main__":
application.listen(9006)
tornado.ioloop.IOLoop.instance().start()
#函数监听端口application.listen(9006)
url为ws://127.0.0.1:9006
第三行tornado.ioloop.IOLoop.instance().start()为启动此websocket
调用
如果想在另一个py文件中来启动我们所写的websocket服务器该怎么办?
答:
from webweb.py import application
application.listen(9006)
tornado.ioloop.IOLoop.instance().start()
webweb.py为websocket服务器所在文件名字
关于users
不管是在之前定义的:
users = set()
还是之后的:
for u in users:
u.write_message("这是第"+i+"个数据")
其中的users通过握手两个客户端print后的输出为:
{
<__main__.WebSocketHandler object at 0x000001DB65E97190>, <__main__.WebSocketHandler object at 0x000001DB65E6EBE0>}
可以显然的看出来for u in users是在遍历当前所在线用户,u是集合中的每一个用户。
可以通过此方式群发或者向指定的用户发送消息(目前博主只试过群发,单独指定发送还需要各位大佬进行尝试)。
实战演练
给大家推荐一个好的在线测试网站:
WebSocket在线测试工具 (wstool.js.org)
这个网站不同于其他网站的是可以收包JSON解码,可能有一些小白不太理解这个JSON有什么用,但是当你进一步接触一些项目之后会发现无论是数据传输还是数据存储JSON都是一种很好的数据格式。
大多数的项目使用websocket传输信息时,都会使用JSON格式的数据。
如何连接
在服务地址中输入
ws://127.0.0.1:9006 (9006为刚才代码里面设置的端口号)
发送消息
此时就完成了一个最基本的通信
但是websocket的威力在此时还未展现出来
真正的双向传输(需要很好的掌握python面向对象)
全局变量和类初始化的置放位置是很神奇的东西!!注意看!
import tornado.web
import tornado.websocket
import tornado.ioloop
import datetime
import json
from threading import Thread
import asyncio
import json
import time
#设置全局变量
users = set()
remessage = ''
class answer:
def send(self,ques):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
dic = {
"问题":ques,
"答案":"您的答案"
}
dics = json.dumps(dic)
for i in range(10):
time.sleep(0.5)
for u in users:
u.write_message("这是第{}条消息".format(i))
u.write_message(dics)
class web_add:
def sear(self,mess):
t1 = Thread(target=an.send(mess))
t1.start()
def stop(self):
pass
def mse(self):#处理命令
message = json.loads(remessage)
type = message["type"]
mess = message["mess"]
if not mess:
mess = ''
if type == "查询":
self.sear(mess)
elif type == "停止":
self.stop()
class WebSocketHandler(tornado.websocket.WebSocketHandler):
def open(self):
users.add(self)
self.write_message(u"[%s]-[%s]-进入" % (self.request.remote_ip, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
print(u"[%s]-[%s]-进入" % (self.request.remote_ip, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
def on_message(self, message):
global remessage
remessage = message
add.mse()
def on_close(self):
users.remove(self) # 用户关闭连接后从容器中移除用户
print(u"[%s]-[%s]-离开" % (self.request.remote_ip, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
def check_origin(self, origin):
return True
application = tornado.web.Application([
(r"/", WebSocketHandler),
])
if __name__ == "__main__":
add = web_add()
an = answer()
application.listen(9006)
tornado.ioloop.IOLoop.instance().start()
可以试着启动一下代码,打开websocket在线测试,会发现在客户端只发送一条消息之后,服务器就可根据这条消息自由设置自己想发送的信息与信息条数、信息发送时间间隔等。真正做到了双向平等通信。
注:
这两行是个神奇的东西,待我弄懂之后再来细说
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
关于websocket的应用知识固然还有更多,博主也是写文章的新手,如果有哪些地方错误或者不明白的话都可以指出来大家相互学习~ 加油!