recv() 函数太慢 [英] recv() function too slow

查看:62
本文介绍了recv() 函数太慢的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是 Python 的新手.我使用 pygame 模块编写了一个简单的局域网游戏(对我来说并不简单).

Hi i'm quite a newbie to Python. I' writting a simple LAN game (not simple for me) using a pygame module.

问题来了 - 我有两台电脑(一台旧的 intel Atom 上网本,另一台 intel i5 NTB).我想达到至少 5 FPS(上网本正在减慢 NTB,但不是那么多,现在我有大约 1,5 FPS),但是在主循环中两次调用 recv() 函数总共需要大约 0.5 秒机器.wifi信号很强,路由器是300Mbit/s,它发送一个大约500个字符的短字符串.正如你所看到的,我使用 time.clock() 来测量时间.

Here's the problem - I have two computers (one old intel Atom netbook, the other intel i5 NTB). I want to achieve at least 5 FPS (the netbook is slowering the NTB, but not so much, now i have around 1,5 FPS), but calling recv() function twice a main loop takes total around 0,5 seconds on each machine. The wifi signal is strong and the router is 300Mbit/s and it sends a short roughly 500-character string. As you can see for measuring time i use time.clock().

这是服务器"代码的一部分,我通常在 i5 NTB 上运行:

Here's the part of the "server" code, which i usually run on the i5 NTB:

while 1:
    start = time.clock()
    messagelen = c.recv(4)      #length of the following message (fixed 4 character)
    if " " in messagelen:
        messagelen = messagelen.replace(" ","")
    message = cPickle.loads(c.recv(int(messagelen))) #list of the arrows, other player position and changes in the game map
    arrowsmod = message[0]
    modtankposan = message[1]
    removelistmod = message[2]
    for i in removelistmod:
        try:
             randopos.remove(i)
        except ValueError:
            randopossv.remove(i)

    print time.clock()-start


    tosendlist=[]
    if len(arrows) == 0:  #if there are no arrows it appends only an empty list
        tosendlist.append([])
    else:
        tosendlist.append(arrows)
    tosendlist.append([zeltankpos, 360-angle])
    if len(removelist) == 0:   #if there are no changes of the map it appends only an empty list
        tosendlist.append([])
    else:
        tosendlist.append(removelist)
        removelist=[]
    tosend=cPickle.dumps(tosendlist)
    tosendlen = str(len(tosend))
    while len(tosendlen)<4:
        tosendlen+=" "
    c.sendall(tosendlen)   #sends the length to client
    c.sendall(tosend)      #sends the actual message(dumped list of lists) to client

    ...something else which takes <0,05 sec on the NTB

这是客户端"游戏代码的一部分(刚开始颠倒了 - 发送/接收部分):

Here's the part of the "client" game code (just inverted the beginning - sending/receiving parts):

while 1:
    tosendlist=[]
    if len(arrows) == 0:  #if there are no arrows it appends only an empty list
        tosendlist.append([])
    else:
        tosendlist.append(arrows)
    tosendlist.append([zeltankpos, 360-angle])
    if len(removelist) == 0:   #if there are no changes of the map it appends only an empty list
        tosendlist.append([])
    else:
        tosendlist.append(removelist)
        removelist=[]
    tosend=cPickle.dumps(tosendlist)
    tosendlen = str(len(tosend))
    while len(tosendlen)<4:
        tosendlen+=" "
    s.sendall(tosendlen)   #sends the length to server
    s.sendall(tosend)      #sends the actual message(dumped list of lists) to server

    start = time.clock()
    messagelen = s.recv(4)      #length of the following message (fixed 4 character)
    if " " in messagelen:
        messagelen = messagelen.replace(" ","")
    message = cPickle.loads(s.recv(int(messagelen))) #list of the arrows, other player position and changes in the game map
    arrowsmod = message[0]
    modtankposan = message[1]
    removelistmod = message[2]
    for i in removelistmod:
        try:
             randopos.remove(i)
        except ValueError:
            randopossv.remove(i)

    print time.clock()-start
    ... rest which takes on the old netbook <0,17 sec

当我在 i5 NTB 上的一台机器(没有插座模块)上运行单人游戏版本时,它在地图的左上角有 50 FPS,在右下角有 25 FPS(1000x1000 像素的地图包含 5x5 像素的方块,我认为它会因为更大的坐标而变慢,但我不敢相信这么多.顺便说一句,在地图的右下角作为 LAN 游戏运行时的 recv 需要大约相同的时间)在 Atom 上网本上,它有 4-8 FPS.

When I run let's say a single player version of the game on one machine (without the socket module) on the i5 NTB it has 50 FPS in the up left corner of the map and 25 FPS in the down right corner (the 1000x1000 pixel map contains 5x5 pixel squares, i think it's slower because of the bigger coordinates, but i can't believe that so much. BTW recv while ran as a LAN game in the down right corner of the map takes approx. the same time) on the Atom netbook it has 4-8 FPS.

所以你能告诉我,为什么它这么慢?计算机不同步,一个更快,另一个更慢,但不可能是它们在等待对方,最大延迟为 0.17 秒,对吗?再加上长时间的 recv 调用只能在更快的计算机上进行?我也不完全知道发送/接收功能是如何工作的.奇怪的是 sendall 几乎不需要时间,而接收需要 0.5 秒.也许发送正在尝试在后台发送,而程序的其余部分继续前进.

So could you please tell me, why it's so slow? The computers are not synchronized, one is faster, the other slower, but it can't be that they are waiting for each other, it would be max 0,17 secs delay, right? And plus the long recv calling would be only on the faster computer? Also I don't exactly know how the send/recv function work. It's weird the sendall takes literally no time while receiving takes 0,5 secs. Maybe sendall is trying to send in the background while the rest of the program continues forward.

推荐答案

正如 Armin Rigo 提到的,recv 会在 socket 收到数据包后返回,但数据包不一定需要调用 send 后立即发送.虽然 send 立即返回,但操作系统会在内部缓存数据,并且在实际传输数据之前可能会等待一些时间将更多数据写入套接字;这称为Nagle 算法,可避免通过网络发送大量小数据包.您可以禁用它并更快地将数据包推送到线路;尝试在 sending 套接字上启用 TCP_NODELAY 选项(如果您的通信是双向的,则两者都启用),调用:

As mentioned by Armin Rigo, recv will return after packets are received by the socket, but packets don't necessarily need to be transmitted immediately after calling send. While send returns immediately, OS caches the data internally and might wait some time for more data being written to the the socket before actually transmitting it; this is called Nagle's algorithm and avoids sending lots of small packets over the network. You can disable it and push packets quicker to the wire; try enabling TCP_NODELAY options on the sending socket (or both if your communication is bidirectional), by calling this:

sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)

这可能会减少 recv 由于没有数据而休眠的时间.

This could potentially reduce amount of time recv is sleeping due to no data.

正如维基百科所说:

该算法与 TCP 延迟确认的交互很差,一个早期几乎同时引入 TCP 的特性1980 年代,但由不同的团体创作.启用这两种算法后,对 TCP 连接进行两次连续写入的应用程序,紧跟在数据之后才会完成的读取从第二次写到目的地,体验一次高达 500 毫秒 的恒定延迟,即ACK 延迟".为了这原因,TCP 实现通常为应用程序提供禁用 Nagle 算法的接口.这通常被称为TCP_NODELAY 选项.

This algorithm interacts badly with TCP delayed acknowledgments, a feature introduced into TCP at roughly the same time in the early 1980s, but by a different group. With both algorithms enabled, applications that do two successive writes to a TCP connection, followed by a read that will not be fulfilled until after the data from the second write has reached the destination, experience a constant delay of up to 500 milliseconds, the "ACK delay". For this reason, TCP implementations usually provide applications with an interface to disable the Nagle algorithm. This is typically called the TCP_NODELAY option.

在您的基准测试中提到了 0.5 秒,所以这可能是一个原因.

There is a mention of 0.5s which you're seeing in your benchmark, so this might be a reason.

这篇关于recv() 函数太慢的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆