Python:如何干净地关闭套接字以避免 'can not assign requested address' 错误(高频)
Python: how to close a socket cleanly to avoid 'can not assign requested address' error (high frequencies)
我在本地主机上有一个服务器 运行ning 在 c 中。
我不确定在这里显示相关代码是否相关(它受到 https://www.cs.utah.edu/~swalton/listings/sockets/programs/part2/chap6/simple-server.c 的强烈启发)
在同一台机器上,我有一个 python 客户端,其代码如下:
import socket,sys,struct,time,threading,random
from frequency_counter import Frequency_counter
# careful ! must be same as c counter parts
SL_PORT = 862
BUFFER_SIZE = 4096
REQUEST_CODE = -1
INT_SIZE = struct.calcsize('i')
FLOAT_SIZE = struct.calcsize('f')
class _open_socket(object):
def __init__(self):
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
#self._socket.bind(('localhost',0))
self._socket.connect(('localhost',SL_PORT))
def __enter__(self): return self._socket
def __exit__(self,type,value,trace): self._socket.close()
def send_floats(*floats):
with _open_socket() as socket_:
packed_data = struct.pack('i'+'f'*len(floats),len(floats),*floats)
packed_data = packed_data+'0'*(BUFFER_SIZE-len(packed_data))
socket_.sendall(packed_data)
def receive_floats():
with _open_socket() as socket_:
packed_data = struct.pack('i',REQUEST_CODE,)
packed_data = packed_data+'0'*(BUFFER_SIZE-len(packed_data))
socket_.sendall(packed_data)
data = socket_.recv(BUFFER_SIZE)
nb_received = struct.unpack('i',data[:INT_SIZE])[0]
received = struct.unpack('f'*nb_received,data[INT_SIZE:INT_SIZE+nb_received*FLOAT_SIZE])
return received
if __name__ == "__main__":
freq_counter = Frequency_counter()
while(True):
floats = [random.random() for _ in range(50)]
send_floats(*floats)
received = receive_floats()
freq_counter.tick()
print "frequency : ",freq_counter.get()
#time.sleep(0.001)
此代码 运行s 以非常高的频率(大约 5000Hz)持续几秒钟,然后频率在几秒钟内降低到大约 500Hz,最后 'self._socket.connect ...' 行抛出错误 'Cannot assign requested address'.
通过添加对 time.sleep 的调用来降低频率并没有解决问题(只是,抛出异常之前 运行 的时间更长)。
调查发生了什么我发现了这个:
https://idea.popcount.org/2014-04-03-bind-before-connect/
显然,"ephemeral" 范围内的所有端口都被使用 (?)。显然,关闭打开的套接字没有帮助(?)。
按照上面文档的建议,我开始使用SO_REUSEADDR选项(注释代码在class open_socket)。
没有抛出错误,但频率开始随着 4000Hz 的周期和 40Hz 的周期而变化(最新的不能满足我的需要)。
是这种方法不适合高频交换,还是我可以做些什么?
编辑:尝试在服务器端和客户端关闭套接字之前调用 'shutdown' 函数。没有改变任何东西。
感谢对问题的评论,找到了解决方案:
首先,c部分是相关的。当数据被发送给它时,它没有发回任何东西。当时我没有得到它是强制性的。我想结果是相关的套接字没有关闭,因此很难为新套接字找到新的端口。让c端总是回答问题就解决了。
禁用 nagle 允许从 3000Hz 到 4000Hz。它是按如下方式完成的。
c方:
int nodelay_flag = 1;
setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (void*) &nodelay_flag, sizeof(int));
python 边:
socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
- 重复使用同一个套接字(而不是为每次数据传输创建一个新套接字)将频率提高到惊人的 11000Hz。它需要对 C 和 Python 端进行(有限的)修改。我想这样做的副作用是只有一个客户端可以连接到服务器,但这对我的需要来说没问题。
我在本地主机上有一个服务器 运行ning 在 c 中。 我不确定在这里显示相关代码是否相关(它受到 https://www.cs.utah.edu/~swalton/listings/sockets/programs/part2/chap6/simple-server.c 的强烈启发)
在同一台机器上,我有一个 python 客户端,其代码如下:
import socket,sys,struct,time,threading,random
from frequency_counter import Frequency_counter
# careful ! must be same as c counter parts
SL_PORT = 862
BUFFER_SIZE = 4096
REQUEST_CODE = -1
INT_SIZE = struct.calcsize('i')
FLOAT_SIZE = struct.calcsize('f')
class _open_socket(object):
def __init__(self):
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
#self._socket.bind(('localhost',0))
self._socket.connect(('localhost',SL_PORT))
def __enter__(self): return self._socket
def __exit__(self,type,value,trace): self._socket.close()
def send_floats(*floats):
with _open_socket() as socket_:
packed_data = struct.pack('i'+'f'*len(floats),len(floats),*floats)
packed_data = packed_data+'0'*(BUFFER_SIZE-len(packed_data))
socket_.sendall(packed_data)
def receive_floats():
with _open_socket() as socket_:
packed_data = struct.pack('i',REQUEST_CODE,)
packed_data = packed_data+'0'*(BUFFER_SIZE-len(packed_data))
socket_.sendall(packed_data)
data = socket_.recv(BUFFER_SIZE)
nb_received = struct.unpack('i',data[:INT_SIZE])[0]
received = struct.unpack('f'*nb_received,data[INT_SIZE:INT_SIZE+nb_received*FLOAT_SIZE])
return received
if __name__ == "__main__":
freq_counter = Frequency_counter()
while(True):
floats = [random.random() for _ in range(50)]
send_floats(*floats)
received = receive_floats()
freq_counter.tick()
print "frequency : ",freq_counter.get()
#time.sleep(0.001)
此代码 运行s 以非常高的频率(大约 5000Hz)持续几秒钟,然后频率在几秒钟内降低到大约 500Hz,最后 'self._socket.connect ...' 行抛出错误 'Cannot assign requested address'.
通过添加对 time.sleep 的调用来降低频率并没有解决问题(只是,抛出异常之前 运行 的时间更长)。
调查发生了什么我发现了这个: https://idea.popcount.org/2014-04-03-bind-before-connect/
显然,"ephemeral" 范围内的所有端口都被使用 (?)。显然,关闭打开的套接字没有帮助(?)。
按照上面文档的建议,我开始使用SO_REUSEADDR选项(注释代码在class open_socket)。 没有抛出错误,但频率开始随着 4000Hz 的周期和 40Hz 的周期而变化(最新的不能满足我的需要)。
是这种方法不适合高频交换,还是我可以做些什么?
编辑:尝试在服务器端和客户端关闭套接字之前调用 'shutdown' 函数。没有改变任何东西。
感谢对问题的评论,找到了解决方案:
首先,c部分是相关的。当数据被发送给它时,它没有发回任何东西。当时我没有得到它是强制性的。我想结果是相关的套接字没有关闭,因此很难为新套接字找到新的端口。让c端总是回答问题就解决了。
禁用 nagle 允许从 3000Hz 到 4000Hz。它是按如下方式完成的。
c方:
int nodelay_flag = 1;
setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (void*) &nodelay_flag, sizeof(int));
python 边:
socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
- 重复使用同一个套接字(而不是为每次数据传输创建一个新套接字)将频率提高到惊人的 11000Hz。它需要对 C 和 Python 端进行(有限的)修改。我想这样做的副作用是只有一个客户端可以连接到服务器,但这对我的需要来说没问题。