0

I've written a small program that sends some numbers over sockets. I run the client program on my computer, and the server program on a virtual machine (hosted with google cloud platform). The code I'm using:

client.py:

import socket
from time import time

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

client.connect(("xx.xxx.xxx.xxx", 5555))

for i in "1234567890":
    print(f"Sending {i} at time {time()}")
    client.sendall(i.encode())

    data = client.recv(64)
    print(f"Received {data.decode()} at time {time()}")

server.py:


import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.bind(("0.0.0.0", 5555))
s.listen(2)

print("Server listening")

conn, addr = s.accept()
print("Connected to:", addr)

while True:
    data = conn.recv(2048).decode()
    if(not data):
        print("Server closing")
        break
    conn.sendall(data.encode())

The output from client.py shows that the round trip of sending data to the server, and then receiving it and decoding it takes around 0.2 seconds. This seems rather high to me, shouldn't sockets run much faster? From this post it seems tcp sockets in java can make a round trip in 2 microseconds, which is alot faster than my 0.2 seconds. I am a newbie when it comes to sockets, so I'm not too sure about anything. If someone could provide me with some insight or maybe make a suggestion on how to enable sockets to run faster (if it's possible, maybe a different module or type of socket?) that would be great.

I started working with sockets because I wanted to make a basic multiplayer game from a tutorial that someone gave me (his sockets seem to run very fast), but after running this code, the amount of time it takes for data to go between clients seems like it would render the game non-playable, or at least severely slow and not fun at all. I did see something about using UDP instead of TCP, but after trying someone's UDP example, I found it ran just as slow.

Note:

  1. The server code is on a linux machine
  2. The client code is on a windows machine
  3. My internet speed is decent so I don't think that's the main problem
  4. I tried using socket.TCP_NODELAY but it had no effect

Just some extra bits of information that someone might find useful

Max
  • 160
  • 3
  • 15
  • Have you tried getting rid of `print` calls here ? Writing to standard output is very slow by default, _cf_ https://stackoverflow.com/questions/3857052/why-is-printing-to-stdout-so-slow-can-it-be-sped-up – michaeldel May 05 '20 at 01:13
  • A couple of things, First get rid of the `print` statements, those make your codes lag and second consider using asyncio or gevent since sockets are IO bound that should speed them up considerable – maestro.inc May 05 '20 at 01:24

1 Answers1

2

You are sending a bunch of little bits of information one at a time instead of aggregating them together and sending them at once. This forces the implementation to be inefficient.

When the transport layer sees the first piece of data, it has no idea a second piece of data is coming, so it sends it immediately. But when it sees the second piece of data, it now has no idea how much data it's going to need to send (since it now sees it shouldn't have sent the first bit immediately), so it waits to see if you're going to send more by setting a 200 millisecond timer.

If you have a bunch of things you need to send at the same time, you need to combine into a single call into the underlying transport layer. Otherwise, you will force it to be inefficient because it never knows when it should send and when it should accumulate data.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • Ah I see. But if I had a game loop running, where every frame I would need to send the new position of the player, I would have to send individual information wouldn't I? Is there any way to specify something that tells the transport layer to not wait for new data? – Max May 05 '20 at 01:56
  • There's nothing special you need to do except not do what you did. Aggregate all the data you need to send at the same time and send it at one time. The transport layer will figure out the rest. (You don't need a buffer over 4KB or so. It's fine to send chunks that size. It's just small chunks that cause problems. Easy solution is to write to a buffer and flush the buffer to the connection when it hits 8KB or when you're done for now.) – David Schwartz May 05 '20 at 01:58
  • One last thing: If the other side responds to each "message" with at least one byte of data, the transport layer will figure it out based on that. Part if your problem is that you write a bunch of little messages that the other side doesn't parse and respond to coherently. (You can google "ACK piggyback" or "application level acknowledments" if you want to understand how that works. Protocols layered over TCP have to be designed to work with TCP.) – David Schwartz May 05 '20 at 02:01
  • I just modified the code so it only sends the first number, 1. That is the _only_ thing it sends, but it still takes 200 milliseconds. From what you said, shouldn't it just send quickly, since I'm not sending any other data? – Max May 05 '20 at 02:56
  • @Max Yes, it should. Maybe your actual round trip time to the google cloud and back is 200 milliseconds? That wouldn't be too unusual if it was US to Australia or something like that. (Punch "speed of light in fiber" into your favorite search engine.) – David Schwartz May 05 '20 at 17:27