Python套接字服务器丢失消息

时间:2016-03-04 00:30:12

标签: python sockets

Python服务器:

import socket
import re
from base64 import b64encode
from hashlib import sha1
import base64
import struct
from queue import Queue
import threading
import select

def decodea(data):
    buf = data
    payload_start = 2
    if len(buf) < 3:
        return
    b = (buf[0])
    fin = b & 0x80
    opcode = b & 0x0f
    b2 = (buf[1])
    mask = b2 & 0x80
    length = b2 & 0x7f
    if len(buf) < payload_start + 4:
        return
    elif length == 126:
        length, = struct.unpack(">H", buf[2:4])
        payload_start += 2
    elif length == 127:
        length, = struct.unpack(">I", buf[2:6])
        payload_start += 4
    if mask:
        mask_bytes = [(b) for b in buf[payload_start:payload_start + 4]]
        payload_start += 4
    if len(buf) < payload_start + length:
        return
    payload = buf[payload_start:payload_start + length]
    if mask:
        unmasked = [mask_bytes[i % 4] ^ (b)
        for b, i in zip(payload, range(len(payload)))]
        payload = "".join([chr(c) for c in unmasked])
    return [payload.encode('latin-1'), length]

def status(decoded):
    status_ = ''
    status_16 = 0
    if(decoded[1] == 2):
        for c in decoded[0]:
            status_ += (str('%02x' % ord(chr(c))))
        status_16 = int(status_, 16)
    if(status_16 > 0):
            cases = {
                1000: "Normal Closure",
                1001: "Going Away",
                1002: "Protocol error",
                1003: "Unsupported Data",
                1004: "---Reserved----",
                1005: "No Status Rcvd",
                1006: "Abnormal Closure",
                1007: "Invalid frame payload data",
                1008: "Policy Violation",
                1009: "Message Too Big",
                1010: "Mandatory Ext.",
                1011: "Internal Server Error",
                1015: "TLS handshake"
            }
            if(status_16 in cases):
                return status_16
    return 0

def handshake(conn, globals__):
    data = conn.recv(1024)
    key = (re.search('Sec-WebSocket-Key:\s+(.*?)[\n\r]+', data.decode('utf-8'))
    .groups()[0]
    .strip())
    sha1f = sha1()
    sha1f.update(key.encode('utf-8') + globals__['GUID'].encode('utf-8'))
    response_key = b64encode(sha1f.digest()).decode('utf-8')
    response = '\r\n'.join(globals__['websocket_answer']).format(key=response_key)
    conn.send(response.encode('utf-8'))

def socket_accept__(lock__, globals__):
    lock__.acquire()
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind((globals__['socket_settings']['HOST'],globals__['socket_settings']['PORT']))
    s.listen(globals__['socket_settings']['LISTEN'])
    globals__['client_list'].append(s)
    lock__.release()
    while True:
        lock__.acquire()
        read_sockets,write_sockets,error_sockets = select.select(globals__['client_list'],[],[])
        for sock in read_sockets:
            if(sock == s):
                conn, addr = s.accept()
                handshake(conn, globals__)
                globals__['client_list'].append(conn)
            else:
                for client in globals__['client_list']:
                    try:
                        client.settimeout(0.001)
                        data = client.recv(1024)
                        print(decodea(data)[0].decode('UTF-8'))
                    except(socket.timeout):
                        continue
        lock__.release()

#thead_queue = Queue()
lock_ = threading.Lock()
globals_ = {
    'GUID':'258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
    'websocket_answer': (
        'HTTP/1.1 101 Switching Protocols',
        'Upgrade: websocket',
        'Connection: Upgrade',
        'Sec-WebSocket-Accept: {key}\r\n\r\n'
    ),
    'client_list': [],
    'socket_settings': {
        'HOST': '10.10.10.12',
        'PORT': 8999,
        'LISTEN': 200
    },
    'threads':[]
}

globals_['threads'].append(threading.Thread(target=socket_accept__, args=(lock_,globals_)))

globals_['threads'][0].setDaemon(True)

for threadi in globals_['threads']:
    threadi.start()

for threadi in globals_['threads']:
    threadi.join() 

#thread2.join()

HTML5:

<!DOCTYPE html>
<html>
<head>
    <title>test</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <script type="text/javascript">
        var s = new WebSocket('ws://10.10.10.12:8999');
        s.onmessage = function(t){console.log(t); alert(t); };
        s.onopen = function(){
          s.send('hello from client');
          s.send('my name is richard');
        }
        alert('load');
    </script>
</head>
<body>
</body>
</html>

输出:

  1. 来自客户
  2. 的问候

    预期产出:

    1. 来自客户
    2. 我的名字是richard
    3. 我确定这是因为client.settimout(0.001)不够快?

      我很遗憾,因为我不知道为什么会发生这种情况。

1 个答案:

答案 0 :(得分:1)

由于通信问题没有消息丢失,只是它没有被解码。它与client.settimeout(0.001)无关。

当来自客户端的两条或多条消息靠近(时间)时,两条消息将在一次data = client.recv(1024)呼叫中接收。

这意味着data可以包含多条消息。但是,decodea()函数只处理一条消息。解码器完全忽略了任何其他消息,这就是您似乎丢失消息的原因。

您可以编写解码器来解码并返回多条消息,可能会将其更改为生成器函数,以便您可以依次yield每条消息。然后,调用代码将遍历消息。

或者,您可以通过只读取前几个字节来检查传入消息,以确定消息的长度。然后从套接字读取剩余的字节并解码消息。在下一次迭代期间,任何其他消息都将被解码。

值得一提的是使用

迭代客户端列表
for client in globals__['client_list']:

似乎错了,因为无论如何每个客户端只是一个套接字对象,并且您已经知道哪些套接字有待处理的数据:read_sockets列表中的那些。您可以像这样编写代码:

while True:
    lock__.acquire()
    read_sockets,write_sockets,error_sockets = select.select(globals__['client_list'],[],[])
    for sock in read_sockets:
        if(sock == s):
            conn, addr = s.accept()
            handshake(conn, globals__)
            globals__['client_list'].append(conn)
        else:
            data = sock.recv(1024)
            print(decodea(data)[0].decode('UTF-8'))

但是你仍然需要弄清楚如何处理多个一起到达的消息 - 无论是在解码器中,还是确保你的代码一次只能读取一条消息。