在Python3中使用WebSocket的Telnet服务器

2 投票
1 回答
782 浏览
提问于 2025-04-18 14:03

我一直在用Python3开发一个游戏服务器。我的目标是让这个服务器的通信方式非常灵活,这样不同的客户端都能轻松连接。目前,所有的通信都是通过telnet和miniboa进行的。我希望能支持基于网页的客户端连接。看起来最简单的方法就是使用websocket连接。我试过使用websockify,它是可以工作的,但我更希望不使用代理,因为那样所有的连接看起来都是来自代理的。理想情况下,我希望能在我的telnet服务器中加入一些东西,能够识别websocket的握手请求(和普通请求不同),返回正确的握手,然后保持连接,这样通过telnet和websocket发送和接收的命令就能保持一致。我还没找到能自动做到这一点的东西,所以我在尝试自己写代码来识别websocket的握手并回复相应的握手。我看了很多其他的帖子和例子,特别是python websocket握手(RFC 6455),我对它进行了修改,变成了下面这个测试程序。

#!/usr/bin/env python3

from miniboa import TelnetServer
from base64 import b64encode
from hashlib import sha1

clientlist = []


def client_connects(client):
    clientlist.append(client)


def client_disconnects(client):
    clientlist.remove(client)

def process_clients():
    for client in clientlist:
        if client.active and client.cmd_ready:
            total_cmd = client.get_command()
            print("incoming = {}" .format(total_cmd))
            if total_cmd.find(" ") != -1: # breaking apart incoming command
                cmd, cmd_var = total_cmd.split(" ", 1)
            else:
                cmd = total_cmd
                cmd_var = ""
            if cmd == "Sec-WebSocket-Key:":
                GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
                cmd_var = cmd_var + GUID
                encoded = cmd_var.encode('utf-8')
                response_key = b64encode(sha1(encoded).digest())
                websocket_answer = (
                    'HTTP/1.1 101 Switching Protocols',
                    'Upgrade: websocket',
                    'Connection: Upgrade',
                    'Sec-WebSocket-Accept: {key}\r\n\r\n',
                )
                handshake = '\r\n'.join(websocket_answer).format(key=response_key)
                client.handshakestr = handshake
            if cmd == "Upgrade:":
                print("Sending handshake:")
                print(client.handshakestr)
                print("End of Handshake")
                client.send(client.handshakestr)

server = TelnetServer(port=6112, on_connect=client_connects, on_disconnect=client_disconnects)

while True:
    process_clients()
    server.poll()

这个程序似乎能让我通过初始的握手,但之后连接就立刻断开了。从输出看,浏览器似乎在期待进一步的内容,但我搞不清楚是什么。作为客户端,我在Firefox 29.0上使用了以下代码,这个版本是我从http://opiate.github.io/SimpleWebSocketServer/下载的。

<!DOCTYPE html>

<meta charset="utf-8" />

<title>WebSocket Test</title>

<script language="javascript" type="text/javascript">


  function init()
  {
    document.myform.url.value = "ws://localhost:8000/"
    document.myform.inputtext.value = "Hello World!"
    document.myform.disconnectButton.disabled = true;
  }

  function doConnect()
  {
    websocket = new WebSocket(document.myform.url.value);
    websocket.onopen = function(evt) { onOpen(evt) };
    websocket.onclose = function(evt) { onClose(evt) };
    websocket.onmessage = function(evt) { onMessage(evt) };
    websocket.onerror = function(evt) { onError(evt) };
  }

  function onOpen(evt)
  {
    writeToScreen("connected\n");
    document.myform.connectButton.disabled = true;
    document.myform.disconnectButton.disabled = false;
  }

  function onClose(evt)
  {
    writeToScreen("disconnected\n");
    document.myform.connectButton.disabled = false;
    document.myform.disconnectButton.disabled = true;
  }

  function onMessage(evt)
  {
    writeToScreen("response: " + evt.data + '\n');
  }

  function onError(evt)
  {
    writeToScreen('error: ' + evt.data + '\n');

    websocket.close();

    document.myform.connectButton.disabled = false;
    document.myform.disconnectButton.disabled = true;

  }

  function doSend(message)
  {
    writeToScreen("sent: " + message + '\n'); 
    websocket.send(message);
  }

  function writeToScreen(message)
  {
    document.myform.outputtext.value += message
    document.myform.outputtext.scrollTop = document.myform.outputtext.scrollHeight;

  }

  window.addEventListener("load", init, false);


   function sendText() {
        doSend( document.myform.inputtext.value );
   }

  function clearText() {
        document.myform.outputtext.value = "";
   }

   function doDisconnect() {
        websocket.close();
   }


</script>

<div id="output"></div>

<form name="myform">
<p>
<textarea name="outputtext" rows="20" cols="50"></textarea>
</p>
<p>
<textarea name="inputtext" cols="50"></textarea>
</p>
<p>
<textarea name="url" cols="50"></textarea>
</p>
<p>
<input type="button" name=sendButton value="Send" onClick="sendText();">
<input type="button" name=clearButton value="Clear" onClick="clearText();">
<input type="button" name=disconnectButton value="Disconnect" onClick="doDisconnect();">
<input type="button" name=connectButton value="Connect" onClick="doConnect();">
</p>


</form>
</html> 

所以,有人知道: 1. 有没有更简单的方法可以让我的telnet服务器支持websocket? 2. 我在回应websocket连接时哪里出错了? 3. 我是否应该放弃,选择使用代理来处理网页连接?

1 个回答

0

Tornado有一个很不错的符合标准的WebSocket处理器。你可以直接使用这个处理器,或者在自己构建的时候参考一下它的代码

撰写回答