Reputation: 399
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.
Upvotes: 1
Views: 6230
Reputation: 366013
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)
Upvotes: 5