Python 如何在GNU无线电中重新连接TCP源?

Python 如何在GNU无线电中重新连接TCP源?,python,sockets,gnuradio,Python,Sockets,Gnuradio,如何在GNU无线电中重新连接TCP源服务器?一旦您连接了客户端一次,它就不能接受新的连接。在研究了tcp.py之后,很明显这是因为python部分只调用accept一次 以前有人在邮件列表中问过这个问题:但答案有点不令人满意,因为据说用一些“dup魔术”很容易做到 我使用文件描述符作为Python中的标识符,通过以下方式创建了一个重新连接套接字: #!/usr/bin/env python import os import socket import sys import threading

如何在GNU无线电中重新连接TCP源服务器?一旦您连接了客户端一次,它就不能接受新的连接。在研究了
tcp.py
之后,很明显这是因为python部分只调用
accept
一次

以前有人在邮件列表中问过这个问题:但答案有点不令人满意,因为据说用一些“dup魔术”很容易做到

我使用文件描述符作为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>

2015年12月18日星期五14:18:32
选择权
作者
窗口大小
类别
习俗
评论
描述
_启用
真的
_协调
(8, 8)
_轮换
0
生成\u选项
qt_-gui
身份证件
顶块
最大值
0
实时调度
运行选项
促使
跑
真的
螺纹安全设置器
标题
变量
评论
_启用
真的
_协调
(8, 160)
_轮换
0
身份证件
抽样率
价值
32000
blks2_tcp_接收器
地址
127.0.0.1
别名
评论
密切关系
_启用
真的
_协调
(696, 125)
_轮换
0
身份证件
blks2\u tcp\u接收器\u 0
类型
复杂的
服务器
真的
港口
9001
弗伦
1.
blks2_tcp_源
地址
127.0.0.1
别名
评论
密切关系
_启用
真的
_协调
(344, 136)
_轮换
0
身份证件
blks2\u tcp\u源\u 0
马克斯堡
0
米努特布夫
0
服务器
真的
类型
复杂的
港口
9000
弗伦
1.
节流阀
别名
评论
密切关系
_启用
真的
_协调
(536, 112)
_轮换
0
身份证件
阻塞\u节流阀\u 0
忽略标签
真的
马克斯堡
0
米努特布夫
0
每秒采样数
抽样率
类型
复杂的
弗伦
1.
blks2\u tcp\u源\u 0
阻塞\u节流阀\u 0
0
0
阻塞\u节流阀\u 0
blks2\u tcp\u接收器\u 0
0
0

老实说,我一直觉得
tcp\u sink
\u source
不过是不完整的黑客行为,主要是为了说明快速、基本而不是正确地做某事是多么容易;请注意版权年,至少在过去6年中,没有人真正尝试改进这种方法,这主要是因为它非常不完整。正如您可能注意到的,其中有一个事实,即只要没有连接,构造函数就会阻塞,而不是让其他块也被实例化,然后在其
工作时阻塞,直到它可以使用样本为止

我不完全确定这里出了什么问题,但这很可能是一个python多线程问题——GNU无线电调度器为每个信号处理块生成一个自己的线程,在什么上下文中调用块的函数对python来说并不总是透明的

所以,第一个问题是:你需要网络还是TCP?如果您需要任何运行良好的无状态服务器,请使用UDP服务器;它们使用异步IO实现,工作非常可靠。如果您需要优雅、完整性保证的网络,请尝试zeroMQ块

假设您确实想要TCP:

<>我认为你最好的做法实际上是扔掉现有的SHILLIX块和 FielyDealthRoxSux<代码>方法,简单地处理你自己的Python或C++的Cink块中的连接。 这并不难;您似乎非常擅长编写GNU Radio python块,但作为参考(以及以后的读者),我想在简要介绍了SDR和GRC用法之后,向您介绍一下编写python块

一般说来,这都是因为

  • 生成树外模块
    gr_modtool newmod
  • cd
  • 添加python接收器块
    gr\u modtool add
  • 修改该块以具有正确的
    io_签名
    ,并具有有意义的
    工作
    功能,即
    • 检查现有TCP连接
    • 尝试发送工作调用的样本
    • 如果>0,则返回发送的项目数
    • 如果没有连接,则尝试建立新连接,b