python套接字和socketserver

use*_*537 1 python sockets client socketserver

我一直在尝试制作一个多客户端服务器,并且最终可以正常运行,但是我现在要做的是代替获取客户端地址,让客户端输入名称,然后程序会说“鲍勃:嗨,大家好”,而不是“ 127.0.0.1:嗨,大家好”。

我使用了python文档中的预制服务器和客户端。这是服务器:

import socketserver

class MyUDPHandler(socketserver.BaseRequestHandler):


    def handle(self):
        data = self.request[0].strip()
        name = self.request[0].strip()
        socket = self.request[1]
        print(name,"wrote:".format(self.client_address[0]))
        print(data)
        socket.sendto(data.upper(), self.client_address)

if __name__ == "__main__":
    HOST, PORT = "localhost", 9999
    server = socketserver.UDPServer((HOST, PORT), MyUDPHandler)
    server.serve_forever()
Run Code Online (Sandbox Code Playgroud)

这是客户:

import socket
import sys

HOST, PORT = "localhost", 9999
data = "".join(sys.argv[1:])
name = "".join(sys.argv[1:])


sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

sock.sendto(bytes(name + "Bob", 'utf-8'), (HOST, PORT))
sock.sendto(bytes(data + "hello my name is Bob", "utf-8"), (HOST, PORT))
received = str(sock.recv(1024), "utf-8")

print("Sent:     {}".format(data))
print("Received: {}".format(received))
Run Code Online (Sandbox Code Playgroud)

一切正常,但由于某种原因,一旦客户端连接,我就会在服务器中得到此信息。

b'Bob'  wrote:
b'Bob'
b'hello my name is bob'  wrote:
b'hello my name is bob'
Run Code Online (Sandbox Code Playgroud)

我希望它像:

Bob wrote:
b'Hello my name is bob'
Run Code Online (Sandbox Code Playgroud)

希望有人能帮助我,谢谢。

aba*_*ert 5

您在这里遇到了多个问题。


首先是bytes直接打印对象:

print(name,"wrote:".format(self.client_address[0]))
Run Code Online (Sandbox Code Playgroud)

这就是为什么您得到b'Bob' wrote:而不是的原因Bob wrote:。当您bytes在Python 3中打印对象时,就会发生这种情况。如果要将其解码为字符串,则必须明确地执行此操作。

您有可以在整个地方执行此操作的代码。使用decodeand encode方法通常比strand bytes构造函数更干净,并且如果您已经在使用format,则可以使用更好的方法来处理此问题,但要坚持使用现有样式:

print(str(name, "utf-8"), "wrote:".format(self.client_address[0]))
Run Code Online (Sandbox Code Playgroud)

接下来,我不确定为什么要调用format没有格式参数的字符串,或者为什么要混合使用多参数print函数和format调用。看来您正在尝试获取self.client_address[0],但您并未这样做。再一次,您想要的输出没有显示出来,所以…… format如果您不想要它,只需删除该调用,然后{}在格式字符串中添加一个位置即可。(您也可能也想解码client_address[0]。)


接下来,将相同的值存储在name和中data

data = self.request[0].strip()
name = self.request[0].strip()
Run Code Online (Sandbox Code Playgroud)

因此,当您稍后执行此操作时:

print(data)
Run Code Online (Sandbox Code Playgroud)

……这只是name再次打印-并且再次进行解码。因此,即使解决了解码问题,您仍然会得到以下信息:

Bob wrote:
Bob
Run Code Online (Sandbox Code Playgroud)

……而不仅仅是:

Bob wrote:
Run Code Online (Sandbox Code Playgroud)

要解决此问题,只需删除data变量和print(data)调用即可。


接下来,您要发送两个独立的包,一个与name和其他与data,但要恢复既出每个数据包的。因此,即使您修复了上述所有问题,您也将获得Bob一个数据包作为名称,而hello my name is bob在下一个数据包中得到:

Bob wrote:
hello my name is bob wrote:
Run Code Online (Sandbox Code Playgroud)

如果希望此状态是有状态的,则需要将状态实际存储在某处。在您的情况下,状态非常简单-只是一个标志,表明这是否是来自给定客户端的第一条消息-但它仍然必须传递到某个地方。一种解决方案是使用字典将新状态与每个地址相关联,尽管在这种情况下,由于状态是“先见过”或什么都没有,因此我们可以只使用一个集合。

放在一起:

class MyUDPHandler(socketserver.BaseRequestHandler):
    def __init__(self, *args, **kw):
        self.seen = set()
        super().__init__(*args, **kw)

    def handle(self):
        data = self.request[0].strip()
        addr = self.client_address[0]
        if not addr in self.seen:
            print(str(data, "utf-8"), "wrote:")
            self.seen.add(addr)
        else:
            print(str(data, "utf-8"))
        socket.sendto(data.upper(), self.client_address)
Run Code Online (Sandbox Code Playgroud)

同时,似乎您真正想要的是将第一个请求中的名称存储为每个客户端状态,因此您可以在以后的每个请求中重用它。那几乎一样容易。例如:

class MyUDPHandler(socketserver.BaseRequestHandler):
    def __init__(self, *args, **kw):
        self.clients = {}
        super().__init__(*args, **kw)

    def handle(self):
        data = str(self.request[0].strip(), 'utf-8')
        addr = self.client_address[0]
        if not addr in self.clients:                
            print(data, "joined!")
            self.clients[addr] = data
        else:
            print(self.clients[addr], 'wrote:', data)
        socket.sendto(data.upper(), self.client_address)
Run Code Online (Sandbox Code Playgroud)