1

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.

asked Jun 3, 2013 at 19:54

1 Answer 1

5

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)
answered Jun 3, 2013 at 20:13
Sign up to request clarification or add additional context in comments.

8 Comments

Thank you for explaining what I had been doing wrong, I have now corrected the code and it works how I want it to now.
When I used the code that you added last it started perfectly but when I try to get a client to connect to the server I get this: TypeError: __init__() takes 1 positional argument but 4 were given @abarnert
@user2387537: OK, edited to pass through any arguments given to __init__, which will solve that problem.
I have another error now, because of myUPDHandler it says this: if not addr in self.clients: AttributeError: 'MyUDPHandler' object has no attribute 'clients' @abarnert
@user2387537: OK, maybe the constructor calls handle before returning or something. Just move the self.clients = {} above the super call, as in the edited version. But you're going to need to understand the basics of how classes work in Python well enough to debug this kind of thing yourself, or this code isn't going to be any good for you.
|

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.