[读书笔记]WebSocket服务篇

本文详细介绍了WebSocket协议的概述,对比传统实时HTTP应用程序的复杂性和优势,并提供了简单的WebSocket服务器实现,包括握手过程和数据帧解析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.1.1 WebSocket概述
WebSockets通过一次握手建立连接,服务器就可以主动地向客户端发送消息。
一举淘汰了Comet和Ajax轮询(polling),长轮询(long-polling),已经流(streaming)解决方案的。 在全双工的实时浏览器通信中,大大减少了网络流量并降低了网络延迟。


1.1.2 传统实时HTTP应用程序的复杂性
如图所示
传统的实时,体验的代价非常高,包括额外的时间延迟,不必要的网络流量和CPU性能消耗。

1.1.3 WebSocket握手
为了建立WebSocket通信,客户端和服务器在初始握手时,将HTTP协议升级到WebSocket协议

1.2 简单的WebSocket服务器
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import asyncore
import socket
import struct
import time
from hashlib import sha1
from base64 import encodestring

class WebSocketConnection(asyncore .dispatcher_with_send):

    #初始化类
    def __init__(self , conn, server):
        asyncore .dispacher_with_send.__init__(self , conn) #每一个WebSocket连接都是一个支持缓冲发送的一步套接字包装程序
       
        self .server = server
        self .server. session.append (self)
        self .readystat = "connecting"
        self .buffer = ""

    #读取来自客户端的数据   
    def handle_read(self ):
        data = self. recv(1024 ) #接受客户端发来的数据
        self .buffer += data    #接收到的数据添加到缓冲区中
        if self. readystat == "connecting":
            self .parse_connnecting() #如果是连接状态,则进行连接数据解析
        elif self. readystat == "open":
            selft .parse_frametype() #如果是打开状态, 解析数据帧类型

    #关闭连接       
    def handle_close(self ):
        self .server. sessions.remove (self)
        self .close()
       
    #解客户端的连接数据
    def parse_connnecting(self ):
        header_end =  self.buffer.find ("\r\n\r\n") #计算数据结束的位置
        if header_end == -1 :
            return
        else:
            header = self. buffer[:header_end ] #取开头的数据
           
            self .buffer = self.buffer [header_end + 4:] #删除开头的数据及\r\n\r\n
            header_lines = header.split("\r\n" )
            headers = {}

            #HTTP请求header数据类似
            #GET /chat HTTP/1.1   (第一行数据)
            #Host: example.com
            #...
            method , path, protocal = header_lines [0]. split(" " )
            if method != "GET" or protocal != "HTTP/1.1" or path[ 0] != "/":
                self .terminate() #格式有误,终止连接
                return

            #解析头部信息key,value,类似Host: example.com
            for line in header_lines[1 :]:
                key , value = line.split (": ")
                headers [key] = value

            headers ["Location"] = "ws://" + headers["Host" ] + path

            self .readystate = "open"
            self .handler = self.server .handlers.get(path , None )(self)

            if "Sec-WebSocket-Key1" in headers.keys ():
                self .send_server_handshake_76(headers)
            else:
                self .send_server_handshake_75(headers)

    #终止连接       
    def terminate(self ):
        self .ready_state = "closed"
        self .close()

    #v.76版本的websocket协议返回信息  
    def send_server_handshake_76(self , headers):
        """
        Send the WebSocket Protocol v.76 handshake response
        """

        key1 = headers["Sec-WebSocket-Key1" ]
        key2 = headers["Sec-WebSocket-Key2" ]
        # read additional 8 bytes from buffer
        key3 , self. buffer = self .buffer[: 8], self .buffer[ 8:]

        #应答的token
        response_token = self. calculate_key(key1 , key2, key3)

        # write out response headers
        self .send_bytes("HTTP/1.1 101 Web Socket Protocol Handshake\r\n")
        self .send_bytes("Upgrade: WebSocket\r\n" )
        self .send_bytes("Connection: Upgrade\r\n" )
        self .send_bytes("Sec-WebSocket-Origin: %s\r\n" % headers["Origin"])
        self .send_bytes("Sec-WebSocket-Location: %s\r\n" % headers["Location"])

        if "Sec-WebSocket-Protocol" in headers:
            protocol = headers["Sec-WebSocket-Protocol" ]
            self .send_bytes("Sec-WebSocket-Protocol: %s\r\n" % protocol)

        self .send_bytes("\r\n" )
        # write out hashed response token
        self .send_bytes(response_token)

    #计算key
    def calculate_key(self , key1, key2, key3 ):
        # parse keys 1 and 2 by extracting numerical characters
        num1 = int( "".join ([digit for digit in list (key1) if digit.isdigit()]))
        spaces1 = len([ char for char in list( key1) if char == " " ])
        num2 = int( "".join ([digit for digit in list (key2) if digit.isdigit()]))
        spaces2 = len([ char for char in list( key2) if char == " " ])

        combined = struct.pack(">II" , num1/ spaces1, num2 /spaces2) + key3
        # md5 sum the combined bytes
        return hashlib.md5(combined ).digest()

    #v.75版本的websocket协议返回信息  
    def send_server_handshake_75(self , headers):
        """
        Send the WebSocket Protocol v.75 handshake response
        """

        self .send_bytes("HTTP/1.1 101 Web Socket Protocol Handshake\r\n")
        self .send_bytes("Upgrade: WebSocket\r\n" )
        self .send_bytes("Connection: Upgrade\r\n" )
        self .send_bytes("WebSocket-Origin: %s\r\n" % headers["Origin" ])
        self .send_bytes("WebSocket-Location: %s\r\n" % headers["Location"])

        if "Protocol" in headers:
            self .send_bytes("WebSocket-Protocol: %s\r\n" % headers["Protocol"])

        self .send_bytes("\r\n" )

    #解析 数据帧类型
    def parse_frametype(self ):
        while len( self.buffer ):
            type_byte = self. buffer[0 ]
            if type_byte == "\x00":
                if not self.parse_textframe ():
                    return

    #解析 文本帧
    def parse_textframe(self ):
        terminator_index = self. buffer.find ("\xFF")
        if terminator_index != -1 :
            frame = self. buffer[1 :terminator_index]
            self .buffer = self.buffer [terminator_index+1:]
            s = frame. decode("UTF8" )
            self .handler.dispatch(s )
            return True
        else:
            # incomplete frame
            return false

    #发送数据
    def send(self , s):
        if self. readystate == "open": #打开状态下,才发送
            self .send_bytes("\x00" ) #发送数据头部
            self .send_bytes(s.encode ("UTF8"))
            self .send_bytes("\xFF" ) #发送数据尾部

    def send_bytes(self , bytes):
        asyncore .dispatcher_with_send.send(self , bytes)

#输出控制器
class EchoHandler(object ):
    """
    The EchoHandler repeats each incoming string to the same websocket
    """
    def __init__(self , conn):
        self .conn = conn
       
    def dispatch(self , data):
        try:
            self .conn. send(data ) #发送数据
        except:
            pass

#web服务      
class WebSocketServer(asyncore .dispatcher):
    def __init__(self , port= 80, handlers =None):
        asyncore .dispatcher.__init__(self )
        self .handlers = handlers
        self .sessions = []
        self .port = port
        self .create_socket(socket.AF_INET , socket.SOCK_STREAM)
        self .set_reuse_addr()
        self .bind(( "", port ))
        self .listen( 5)

    def handle_accept(self ):
        conn , addr = self.accept ()
        session = WebSocketConnection(conn, self ) #处理websocket服务

#主函数
if __name__ == "__main__":
    print "Starting WebSocket Server"
    WebSocketServer(port =8080, handlers={"/echo" : EchoHandler})
    asyncore.loop ()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值