2016-01-05 29 views
0

如何在GNU無線電中重新連接TCP源服務器?一旦連接了一個客戶端,它就不能接受新的連接。在研究了tcp.py之後,很明顯這是因爲python部分只調用了accept一次。如何在GNU無線電中使TCP源重新連接?

以前在郵件列表上詢問過這個問題:https://lists.gnu.org/archive/html/discuss-gnuradio/2010-04/msg00501.html但是答案有點不滿意,因爲它被認爲很容易處理一些「重複魔法」。

我設法使用文件描述符在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

要完全誠實的,我總覺得tcp_sink_source不過是不完整的黑客,大多是做展示它是很容易做一些快速和基本的,而不是正確的;注意版權年,至少在過去的6年中沒有人真的試圖改進這種方法,主要是因爲它不完整。有一些quite some problems,你可能已經注意到了,事實上,只要沒有連接,構造函數就會阻塞,而不是讓其他塊也被實例化,然後在其work中阻塞,直到它可以消耗樣本。

我並不完全確定這裏出了什麼問題,但這很可能是一個python多線程問題 - GNU Radio調度程序爲每個信號處理塊產生一個自己的線程,它並不總是透明的上下文來調用塊的功能。

所以,第一個問題是:你需要聯網TCP具體是?如果您需要任何運行良好的無狀態服務器,請使用UDP;它們使用異步IO來實現並且工作非常可靠。如果您需要優雅的,完整性保證的網絡,請嘗試zeroMQ塊。

假設你真的 TCP:

我覺得你最好的行動當然實際上將被扔掉現有hier_block和基於file_descriptor_sink方法,並簡單地處理你自己的Python或C++散熱塊連接。

這並不難;你似乎在編寫GNU Radio python塊時已經足夠熟練了,但作爲參考資料(以及後來的讀者),我想指出Guided Tutorials,在簡單介紹SDR和GRC用法之後,向您介紹如何編寫Python塊。

通常,沸騰到

  1. 生成樹外的一個模塊gr_modtool newmod
  2. cd <modulename>
  3. 添加蟒水槽塊gr_modtool add
  4. 修改塊有正確io_signature,和具有有意義的work功能,即一個
    • 來檢查現有的TCP連接,
    • 試圖發送的工作被稱爲與樣品,
    • 返回如果> 0,
    • 嘗試建立一個新的連接發送的項目數,如果沒有連接,並且阻止,直到至少一個物品被髮送爲止