如何在GNU无线电中重新连接TCP源服务器?连接客户端一次后,它就无法接受新连接。研究tcp.py
之后很明显这是因为python部分只调用accept
一次。
此问题之前曾在邮件列表中被问到:https://lists.gnu.org/archive/html/discuss-gnuradio/2010-04/msg00501.html但答案有点不尽如人意,因为据说对某些" dup magic"很容易。
我设法通过以下方式使用文件描述符作为Python中的标识符来重新连接套接字:
#!/usr/bin/env python
import os
import socket
import sys
import threading
def get_server_socket_and_fd():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('127.0.0.1', 9999))
s.listen(1)
print "waiting for client connection on port 9999"
cs, address = s.accept()
return s, os.dup(cs.fileno())
class StoppableThread(threading.Thread):
def __init__(self):
super(StoppableThread, self).__init__()
self._stop = threading.Event()
def stop(self):
self._stop.set()
def stopped(self):
return self._stop.isSet()
class SocketWatcher(StoppableThread):
def __init__(self, server_socket, fd):
super(SocketWatcher, self).__init__()
self.setDaemon(True)
self._fd = fd
self._server_socket = server_socket
def run(self):
print "WWW: starting watcher on fd = {}".format(self._fd)
while not self.stopped():
#print "WWW: creating socket object from fd = {}".format(self._fd)
s = socket.fromfd(self._fd, socket.AF_INET, socket.SOCK_STREAM)
while not self.stopped():
print "WWW: trying to peek for new data"
data = s.recv(1024, socket.MSG_PEEK)
print "WWW: msg peek returned: {} bytes".format(len(data))
if len(data) == 0:
print "WWW: EOF? closing socket"
s.close()
print "WWW: waiting for new connection..."
cs, address = self._server_socket.accept()
print "WWW: new connection! fileno = {}".format(cs.fileno())
print "WWW: duplicating this client socket fd into the old one"
os.dup2(cs.fileno(), self._fd)
break
print "WWW: thread stopped, exiting run method"
server_socket, client_fd = get_server_socket_and_fd()
watcher = SocketWatcher(server_socket, client_fd)
watcher.start()
try:
while True:
s = socket.fromfd(client_fd, socket.AF_INET, socket.SOCK_STREAM)
data = s.recv(1024)
if len(data) > 0:
print " data received: {} bytes".format(len(data))
print repr(data)
except (KeyboardInterrupt, SystemExit):
print "stopping program..."
watcher.stop()
sys.exit()
工作方式与预期一样:启动python脚本,连接到端口,写东西,关闭连接,打开另一个连接,写更多东西,并注意它们继续打印。
然而,当我尝试将其集成到GNU无线电中时:它不起作用。这是我最好的尝试:
#
# Copyright 2009 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
# GNU Radio is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
#
# GNU Radio is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GNU Radio; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
from gnuradio import gr, blocks
import socket
import os
import threading
class StoppableThread(threading.Thread):
def __init__(self):
super(StoppableThread, self).__init__()
self._stop = threading.Event()
def stop(self):
self._stop.set()
def stopped(self):
return self._stop.isSet()
class SocketWatcher(StoppableThread):
def __init__(self, server_socket, fd):
super(SocketWatcher, self).__init__()
self.setDaemon(True)
self._fd = fd
self._server_socket = server_socket
def run(self):
while not self.stopped():
s = socket.fromfd(self._fd, socket.AF_INET, socket.SOCK_STREAM)
while not self.stopped():
data = s.recv(1024, socket.MSG_PEEK)
if len(data) == 0:
print "EOF detected. Closing socket and waiting for new connection..."
s.close()
cs, address = self._server_socket.accept()
print "got new connection!"
os.dup2(cs.fileno(), self._fd)
break
def _get_sock_fd(addr, port, server):
"""
Get the file descriptor for the socket.
As a client, block on connect, dup the socket descriptor.
As a server, block on accept, dup the client descriptor.
Args:
addr: the ip address string
port: the tcp port number
server: true for server mode, false for client mode
Returns:
the file descriptor number
"""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
if server:
sock.bind((addr, port))
sock.listen(1)
clientsock, address = sock.accept()
return os.dup(clientsock.fileno()), sock
else:
sock.connect((addr, port))
return os.dup(sock.fileno()), sock
class tcp_source(gr.hier_block2):
def __init__(self, itemsize, addr, port, server=True):
#init hier block
gr.hier_block2.__init__(
self, 'tcp_source',
gr.io_signature(0, 0, 0),
gr.io_signature(1, 1, itemsize),
)
if not server:
raise NotImplementedError
fd, server_socket = _get_sock_fd(addr, port, server)
watcher = SocketWatcher(server_socket, fd)
watcher.start()
self.connect(blocks.file_descriptor_source(itemsize, fd), self)
class tcp_sink(gr.hier_block2):
def __init__(self, itemsize, addr, port, server=False):
#init hier block
gr.hier_block2.__init__(
self, 'tcp_sink',
gr.io_signature(1, 1, itemsize),
gr.io_signature(0, 0, 0),
)
fd, _ = _get_sock_fd(addr, port, server)
self.connect(self, blocks.file_descriptor_sink(itemsize, fd))
我在命令行上看到了:
EOF detected. Closing socket and waiting for new connection...
got new connection!
这表明检测到EOF是成功的,但是,一旦我重新连接连接被接受,但是我写入套接字的任何内容都不会出现在另一端。我使用一个简单的GNU无线电程序来测试它,包括一个TCP源(我的版本),一个节流阀和一个TCP接收器。
<?xml version='1.0' encoding='utf-8'?>
<?grc format='1' created='3.7.8'?>
<flow_graph>
<timestamp>Fri Dec 18 14:18:32 2015</timestamp>
<block>
<key>options</key>
<param>
<key>author</key>
<value></value>
</param>
<param>
<key>window_size</key>
<value></value>
</param>
<param>
<key>category</key>
<value>Custom</value>
</param>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>description</key>
<value></value>
</param>
<param>
<key>_enabled</key>
<value>True</value>
</param>
<param>
<key>_coordinate</key>
<value>(8, 8)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
</param>
<param>
<key>generate_options</key>
<value>qt_gui</value>
</param>
<param>
<key>id</key>
<value>top_block</value>
</param>
<param>
<key>max_nouts</key>
<value>0</value>
</param>
<param>
<key>realtime_scheduling</key>
<value></value>
</param>
<param>
<key>run_options</key>
<value>prompt</value>
</param>
<param>
<key>run</key>
<value>True</value>
</param>
<param>
<key>thread_safe_setters</key>
<value></value>
</param>
<param>
<key>title</key>
<value></value>
</param>
</block>
<block>
<key>variable</key>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>_enabled</key>
<value>True</value>
</param>
<param>
<key>_coordinate</key>
<value>(8, 160)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
</param>
<param>
<key>id</key>
<value>samp_rate</value>
</param>
<param>
<key>value</key>
<value>32000</value>
</param>
</block>
<block>
<key>blks2_tcp_sink</key>
<param>
<key>addr</key>
<value>127.0.0.1</value>
</param>
<param>
<key>alias</key>
<value></value>
</param>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>affinity</key>
<value></value>
</param>
<param>
<key>_enabled</key>
<value>True</value>
</param>
<param>
<key>_coordinate</key>
<value>(696, 125)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
</param>
<param>
<key>id</key>
<value>blks2_tcp_sink_0</value>
</param>
<param>
<key>type</key>
<value>complex</value>
</param>
<param>
<key>server</key>
<value>True</value>
</param>
<param>
<key>port</key>
<value>9001</value>
</param>
<param>
<key>vlen</key>
<value>1</value>
</param>
</block>
<block>
<key>blks2_tcp_source</key>
<param>
<key>addr</key>
<value>127.0.0.1</value>
</param>
<param>
<key>alias</key>
<value></value>
</param>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>affinity</key>
<value></value>
</param>
<param>
<key>_enabled</key>
<value>True</value>
</param>
<param>
<key>_coordinate</key>
<value>(344, 136)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
</param>
<param>
<key>id</key>
<value>blks2_tcp_source_0</value>
</param>
<param>
<key>maxoutbuf</key>
<value>0</value>
</param>
<param>
<key>minoutbuf</key>
<value>0</value>
</param>
<param>
<key>server</key>
<value>True</value>
</param>
<param>
<key>type</key>
<value>complex</value>
</param>
<param>
<key>port</key>
<value>9000</value>
</param>
<param>
<key>vlen</key>
<value>1</value>
</param>
</block>
<block>
<key>blocks_throttle</key>
<param>
<key>alias</key>
<value></value>
</param>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>affinity</key>
<value></value>
</param>
<param>
<key>_enabled</key>
<value>True</value>
</param>
<param>
<key>_coordinate</key>
<value>(536, 112)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
</param>
<param>
<key>id</key>
<value>blocks_throttle_0</value>
</param>
<param>
<key>ignoretag</key>
<value>True</value>
</param>
<param>
<key>maxoutbuf</key>
<value>0</value>
</param>
<param>
<key>minoutbuf</key>
<value>0</value>
</param>
<param>
<key>samples_per_second</key>
<value>samp_rate</value>
</param>
<param>
<key>type</key>
<value>complex</value>
</param>
<param>
<key>vlen</key>
<value>1</value>
</param>
</block>
<connection>
<source_block_id>blks2_tcp_source_0</source_block_id>
<sink_block_id>blocks_throttle_0</sink_block_id>
<source_key>0</source_key>
<sink_key>0</sink_key>
</connection>
<connection>
<source_block_id>blocks_throttle_0</source_block_id>
<sink_block_id>blks2_tcp_sink_0</sink_block_id>
<source_key>0</source_key>
<sink_key>0</sink_key>
</connection>
</flow_graph>
答案 0 :(得分:0)
说实话,我总觉得tcp_sink
和_source
只是不完整的黑客行为,主要是为了表明如何快速地做一些简单而不是正确的事情;注意到版权年份,至少在过去的6年里,没有人真正尝试改进这种方法,这主要是因为它非常不完整。其中有quite some problems,正如您可能已经注意到的那样,构造函数只要没有连接就会阻塞,而不是让其他块也被实例化然后在其work
中阻塞。直到它可以消耗样品。
我不完全确定这里出了什么问题,但这很可能是一个python多线程问题 - GNU Radio调度程序为每个信号处理块产生一个自己的线程,并且在什么情况下它对于python并不总是透明的调用块的功能。
因此,第一个问题是:您是否需要网络或 TCP ?如果您需要任何运行良好的无状态服务器,请使用UDP服务器;它们使用异步IO实现,工作非常可靠。如果您需要优雅,完整性保证的网络,请尝试使用zeroMQ块。
假设你真的想要TCP:
我认为你最好的行动方案实际上是抛弃现有的基于hier_block和file_descriptor_sink
的方法,只需处理你自己的python或C ++接收器块中的连接。
那不是很难;你似乎对编写GNU Radio python块足够熟练,但作为参考(以及后来的读者),我想指出你在快速介绍SDR之后的Guided Tutorials和GRC用法介绍你编写Python块。
一般来说,它归结为
gr_modtool newmod
cd <modulename>
gr_modtool add
io_signature
,并具有有意义的work
功能,即一个