質問

I've been trying to make a multi-client server and i finally have and it works perfectly but what I want to do now is to instead of getting the clients address, get the client to type their name in and then the program will say "Bob: Hi guys" instead of "127.0.0.1: Hi guys".

I used a pre-made server and client from python docs. Here's the server:

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()

And here's the client:

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))

Everything works fine but for some reason I get this in the server once the client has connected.

b'Bob'  wrote:
b'Bob'
b'hello my name is bob'  wrote:
b'hello my name is bob'

I want it to be like:

Bob wrote:
b'Hello my name is bob'

I hope someone can help me, thanks.

役に立ちましたか?

解決

You've got multiple problems here.


The first is that you're printing bytes objects directly:

print(name,"wrote:".format(self.client_address[0]))

That's why you get b'Bob' wrote: instead of Bob wrote:. When you print a bytes object in Python 3, this is what happens. If you want to decode it to a string, you have to do that explicitly.

You have code that does that all over the place. It's usually cleaner to use the decode and encode methods than the str and bytes constructors, and if you're already using format there are even nicer ways to deal with this, but sticking with your existing style:

print(str(name, "utf-8"), "wrote:".format(self.client_address[0]))

Next, I'm not sure why you're calling format on a string with no format parameters, or why you're mixing multi-argument print functions and format calls together. It looks like you're trying to get self.client_address[0], but you're not doing that. Then again, your desired output doesn't show it, so… just remove that format call if you don't want it, add a {} somewhere in the format string if you do. (You're also probably going to want to decode client_address[0], too.)


Next, you store the same value in name and data:

data = self.request[0].strip()
name = self.request[0].strip()

So, when you later do this:

print(data)

… that's just printing name again—and, again, without decoding it. So, even if you fix the decoding problem, you'll still get this:

Bob wrote:
Bob

… instead of just:

Bob wrote:

To fix that, just get rid of the data variable and the print(data) call.


Next, you're sending two separate packets, one with the name and the other with the data, but trying to recover both out of each packet. So, even if you fix all of the above, you're going to get Bob in one packet as the name, and hello my name is bob in the next packet, resulting in:

Bob wrote:
hello my name is bob wrote:

If you want this to be stateful, you need to actually store the state somewhere. In your case, the state is incredibly simple—just a flag saying whether this is the first message from a given client—but it still has to go somewhere. One solution is to associate a new state with each address using a dictionary—although in this case, since the state is either "seen before" or nothing at all, we can just use a set.

Putting it all together:

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)

Meanwhile, it seems like what you actually want is to store the name from the first request as your per-client state, so you can reuse it in every future request. That's almost as easy. For example:

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)
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top