I'm trying to create a simple multiplayer game in Python using pygame and socket modules. It just consists in two circles that are controlled with the W, A, S, D keys from two different computers.
At first I created a client with the recv() in the middle of the pygame loop. It worked well but the recv() blocked the loop, so the movement of the circles wasn't smooth and I had to increase the velocity to 6 (before it was set to 0.6) in order to have a normal speed. This is the code (I've summarised it a bit):
Client 1st version
#import modules
def main(sock):
pygame.init()
#Display screen and set initial me_x and me_y
vel = 0.6
while True:
keys_pressed = pygame.key.get_pressed()
#Change me_x and me_y (with A, D, W, S keys)
#Make sure me_x and me_y don't get off the screen
screen.fill(color)
if other_x and other_y:
pygame.draw.circle(screen, colorMe, (other_x, other_y), radi)
pygame.draw.circle(screen, colorOther, (int(me_x), int(me_y)), radi)
pygame.display.flip()
sock.send(int(me_x).to_bytes(3, byteorder = 'big') + int(me_y).to_bytes(3, byteorder = 'big'))
otherPos = sock.recv(BUFSIZ)
other_x = int.from_bytes(otherPos[:3], byteorder = 'big')
other_y = int.from_bytes(otherPos[3:], byteorder = 'big')
print(other_x, other_y)
#CONNECT TO TCP SOCKET
other_x = None
other_y = None
main(client_socket)
Then, I tried to put the recv() in a thread to stop blocking the loop:
Client 2nd version
#import modules
def main(sock):
pygame.init()
#Display screen and set initial me_x and me_y
vel = 0.6
while True:
for i in range(30):
keys_pressed = pygame.key.get_pressed()
#Change me_x and me_y (with A, D, W, S keys)
#Make sure me_x and me_y don't get off the screen
screen.fill(color)
if other_x and other_y:
pygame.draw.circle(screen, colorOther, (other_x, other_y), 15)
pygame.draw.circle(screen, colorMe, (int(me_x), int(me_y)), 15)
pygame.display.flip()
msg = int(me_x).to_bytes(3, byteorder = 'big') + int(me_y).to_bytes(3, byteorder = 'big')
sock.send(msg)
def recv_pos(sock):
while True:
other_pos = sock.recv(BUFSIZ)
other_x = int.from_bytes(other_pos[:3], byteorder = 'big')
other_y = int.from_bytes(other_pos[3:], byteorder = 'big')
print(other_x, other_y)
#CONNECT TO TCP SOCKET
other_x = None
other_y = None
receive_thread = threading.Thread(target = recv_pos, args = (client_socket,))
receive_thread.daemon = True
receive_thread.start()
main(client_socket)
However, when I start 2 instances of the client 2 in two different computers, it gives me an OverflowError:
OverflowError: Python int too large to convert to C long
I added the for i in range(30): because I thought that the server was getting collapsed because there were too much messages being sent at the same time. The output is the same: after 3 seconds more or less, the program crashes giving an OverflowError.
I added a print() statement in both versions just after the recv() to see which values of x and y I was receiving. In the 1st version, all of them were in the width and height range. However, in the 2nd version, 1/5 of the received messages was a big number such as 124381473265. If this number is even bigger, it gives the OverflowError. I don't understand why this is happening: I'm encoding and decoding the messages in the same way in both versions, but one works and the other doesn't.
I'm not including the server code because I don't think it's necessary. It's just just transmits the messages between both clients without modifying them. The server is not the error, as in the 1st client version the messages are sent and received properly.
Any help will be apprecieted. If you need more information about any point just tell me.
Thanks in advance!
Put your socket code into a thread, or use select.select() for non-blocking socket-reads. Then post custom event-messages back to the main loop when a data-gram arrives from the server.
import pygame
import enum
class NetworkEvents( enum.IntEnum ):
EVENT_HANGUP = pygame.USEREVENT + 1
EVENT_MESSAGE = pygame.USEREVENT + 2
Your socket-reading code should handle partial-packets, hang-ups and delays too. I think your error above is caused by trying to unpack a partial (or somehow junk) packet. Commonly people use the pickle module to encapsulate this data, but to begin with, I would do pre-testing with simple string data. It's easier to debug. Then once the transmission code is all bedded-down and tested, change to binary - if necessary.
I like to use select() in my socket code no matter what. It gives fine-grained control over what's happening with the socket.
Here's a simple threaded socket-conversation handler. It really only supports socket-hangup and incoming message. But it could be easily extended to handle further message types.
import threading
import pygame
import random
import enum
import socket
import select
import time
class ConversationHandlerThread( threading.Thread ):
""" A thread that handles a conversation with a single remote server.
Accepts commands of 'close', 'red', 'green' or 'blue', and posts messages
to the main PyGame thread for processing """
def __init__( self, server_address, server_port ):
threading.Thread.__init__(self)
self.server_address = server_address
self.server_port = server_port
self.server_socket = None
self.data_buffer = ''
self.daemon = True # exit with parent
self.done = False
def stop( self ):
self.done = True
def connect( self ):
self.server_socket = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
while True:
try:
self.server_socket.connect( ( self.server_address, self.server_port ) )
print( "Connected to %s:%d" % ( self.server_address, self.server_port ) )
break;
except:
print( "Failed to connect %s:%d" % ( self.server_address, self.server_port ) )
time.sleep( 12 )
print( "Retrying..." )
def run( self ):
""" Connects to Server, then Loops until the server hangs-up """
self.connect()
# Now we're connected, start reading commands
read_events_on = [ self.server_socket ]
while ( not self.done ):
# Wait for incoming data, or errors, or 0.3 seconds
(read_list, write_list, except_list) = select.select( read_events_on, [], [], 0.5 )
if ( len( read_list ) > 0 ):
# New data arrived, read it
incoming = self.server_socket.recv( 8192 )
if ( len(incoming) == 0):
# Socket has closed
new_event = pygame.event.Event( NetworkEvents.EVENT_HANGUP, { "address" : self.server_address } )
pygame.event.post( new_event )
self.server_socket.close()
self.done = True
else:
# Data has arrived
try:
new_str = incoming.decode('utf-8')
self.data_buffer += new_str
except:
pass # don't understand buffer
# Parse incoming message (trivial parser, not high quality)
# commands are '\n' separated
if (self.data_buffer.find('\n') != -1 ):
for line in self.data_buffer.split('\n'):
line = line.strip()
# client disconnect command
if ( line == 'close' ):
new_event = pygame.event.Event( NetworkEvents.EVENT_HANGUP, { "address" : self.server_address } )
pygame.event.post( new_event )
self.server_socket.close()
self.done = True
# only make events for valid commands
elif ( line in ( 'red', 'green', 'blue' ) ):
new_event = pygame.event.Event( NetworkEvents.EVENT_MESSAGE, { "address" : self.server_address, "message" : line } )
pygame.event.post( new_event )
self.data_buffer = '' # all used-up
The beauty of putting all this complexity into a thread, is that once it's started, you can forget about it.
# Start the network-handler thread
thread1 = ConversationHandlerThread( '127.0.0.1', 5555 )
thread1.start()
In your main loop, process the events like any others:
for event in pygame.event.get():
if ( event.type == pygame.QUIT ):
done = True
elif ( event.type == NetworkEvents.EVENT_HANGUP ):
print(" CLIENT DISCONNECTED %s " % ( str(event.address) ) )
elif ( event.type == NetworkEvents.EVENT_MESSAGE ):
print(" CLIENT MESSAGE FROM %s - %s " % ( str(event.address), event.message ) )
if ( event.message == 'red' ):
new_sprite = AlienSprite( RED )
SPRITES.add( new_sprite )
Related
I am trying to perform some remote audio processing with a NAO v4 using the version 2.1.4.13 of the python naoqi SDK, and I am having difficulties doing so. I have tried using the solution provided by Alexandre Mazel at NAO robot remote audio problems, but I am still having issues retrieving audio data from the buffer. The code referenced in the post is available below. I have also followed the instructions for remotely processing audio data found at https://www.generationrobots.com/media/NAO%20Next%20Gen/FeaturePaper(AudioSignalProcessing)%20(1).pdf
I would greatly appreciate any help or solutions, as I have been stuck on this problem for several days now.
# -*- coding: utf-8 -*-
###########################################################
# Retrieve robot audio buffer
# Syntaxe:
# python scriptname --pip <ip> --pport <port>
#
# --pip <ip>: specify the ip of your robot (without specification it will use the NAO_IP defined some line below
#
# Author: Alexandre Mazel
###########################################################
NAO_IP = "10.0.252.126" # Romeo on table
#~ NAO_IP = "10.0.253.99" # Nao Alex Blue
from optparse import OptionParser
import naoqi
import numpy as np
import time
import sys
class SoundReceiverModule(naoqi.ALModule):
"""
Use this object to get call back from the ALMemory of the naoqi world.
Your callback needs to be a method with two parameter (variable name, value).
"""
def __init__( self, strModuleName, strNaoIp ):
try:
naoqi.ALModule.__init__(self, strModuleName );
self.BIND_PYTHON( self.getName(),"callback" );
self.strNaoIp = strNaoIp;
self.outfile = None;
self.aOutfile = [None]*(4-1); # ASSUME max nbr channels = 4
except BaseException, err:
print( "ERR: abcdk.naoqitools.SoundReceiverModule: loading error: %s" % str(err) );
# __init__ - end
def __del__( self ):
print( "INF: abcdk.SoundReceiverModule.__del__: cleaning everything" );
self.stop();
def start( self ):
audio = naoqi.ALProxy( "ALAudioDevice", self.strNaoIp, 9559 );
nNbrChannelFlag = 0; # ALL_Channels: 0, AL::LEFTCHANNEL: 1, AL::RIGHTCHANNEL: 2; AL::FRONTCHANNEL: 3 or AL::REARCHANNEL: 4.
nDeinterleave = 0;
nSampleRate = 48000;
audio.setClientPreferences( self.getName(), nSampleRate, nNbrChannelFlag, nDeinterleave ); # setting same as default generate a bug !?!
audio.subscribe( self.getName() );
print( "INF: SoundReceiver: started!" );
# self.processRemote( 4, 128, [18,0], "A"*128*4*2 ); # for local test
# on romeo, here's the current order:
# 0: right; 1: rear; 2: left; 3: front,
def stop( self ):
print( "INF: SoundReceiver: stopping..." );
audio = naoqi.ALProxy( "ALAudioDevice", self.strNaoIp, 9559 );
audio.unsubscribe( self.getName() );
print( "INF: SoundReceiver: stopped!" );
if( self.outfile != None ):
self.outfile.close();
def processRemote( self, nbOfChannels, nbrOfSamplesByChannel, aTimeStamp, buffer ):
"""
This is THE method that receives all the sound buffers from the "ALAudioDevice" module
"""
#~ print( "process!" );
#~ print( "processRemote: %s, %s, %s, lendata: %s, data0: %s (0x%x), data1: %s (0x%x)" % (nbOfChannels, nbrOfSamplesByChannel, aTimeStamp, len(buffer), buffer[0],ord(buffer[0]),buffer[1],ord(buffer[1])) );
#~ print( "raw data: " ),
#~ for i in range( 8 ):
#~ print( "%s (0x%x), " % (buffer[i],ord(buffer[i])) ),
#~ print( "" );
aSoundDataInterlaced = np.fromstring( str(buffer), dtype=np.int16 );
#~ print( "len data: %s " % len( aSoundDataInterlaced ) );
#~ print( "data interlaced: " ),
#~ for i in range( 8 ):
#~ print( "%d, " % (aSoundDataInterlaced[i]) ),
#~ print( "" );
aSoundData = np.reshape( aSoundDataInterlaced, (nbOfChannels, nbrOfSamplesByChannel), 'F' );
#~ print( "len data: %s " % len( aSoundData ) );
#~ print( "len data 0: %s " % len( aSoundData[0] ) );
if( False ):
# compute average
aAvgValue = np.mean( aSoundData, axis = 1 );
print( "avg: %s" % aAvgValue );
if( False ):
# compute fft
nBlockSize = nbrOfSamplesByChannel;
signal = aSoundData[0] * np.hanning( nBlockSize );
aFft = ( np.fft.rfft(signal) / nBlockSize );
print aFft;
if( False ):
# compute peak
aPeakValue = np.max( aSoundData );
if( aPeakValue > 16000 ):
print( "Peak: %s" % aPeakValue );
if( True ):
bSaveAll = True;
# save to file
if( self.outfile == None ):
strFilenameOut = "/out.raw";
print( "INF: Writing sound to '%s'" % strFilenameOut );
self.outfile = open( strFilenameOut, "wb" );
if( bSaveAll ):
for nNumChannel in range( 1, nbOfChannels ):
strFilenameOutChan = strFilenameOut.replace(".raw", "_%d.raw"%nNumChannel);
self.aOutfile[nNumChannel-1] = open( strFilenameOutChan, "wb" );
print( "INF: Writing other channel sound to '%s'" % strFilenameOutChan );
#~ aSoundDataInterlaced.tofile( self.outfile ); # wrote the 4 channels
aSoundData[0].tofile( self.outfile ); # wrote only one channel
#~ print( "aTimeStamp: %s" % aTimeStamp );
#~ print( "data wrotten: " ),
#~ for i in range( 8 ):
#~ print( "%d, " % (aSoundData[0][i]) ),
#~ print( "" );
#~ self.stop(); # make naoqi crashes
if( bSaveAll ):
for nNumChannel in range( 1, nbOfChannels ):
aSoundData[nNumChannel].tofile( self.aOutfile[nNumChannel-1] );
# processRemote - end
def version( self ):
return "0.6";
# SoundReceiver - end
def main():
""" Main entry point
"""
parser = OptionParser()
parser.add_option("--pip",
help="Parent broker port. The IP address or your robot",
dest="pip")
parser.add_option("--pport",
help="Parent broker port. The port NAOqi is listening to",
dest="pport",
type="int")
parser.set_defaults(
pip=NAO_IP,
pport=9559)
(opts, args_) = parser.parse_args()
pip = opts.pip
pport = opts.pport
# We need this broker to be able to construct
# NAOqi modules and subscribe to other modules
# The broker must stay alive until the program exists
myBroker = naoqi.ALBroker("myBroker",
"0.0.0.0", # listen to anyone
0, # find a free port and use it
pip, # parent broker IP
pport) # parent broker port
# Warning: SoundReceiver must be a global variable
# The name given to the constructor must be the name of the
# variable
global SoundReceiver
SoundReceiver = SoundReceiverModule("SoundReceiver", pip)
SoundReceiver.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print
print "Interrupted by user, shutting down"
myBroker.shutdown()
sys.exit(0)
if __name__ == "__main__":
main()
Maybe this project can help you as orientation:
https://github.com/UNSWComputing/rUNSWift-2015-release/wiki/Whistle-Detection
It offers a whistle_detector.py Python module that
also runs on the Nao under the 2.1 toolchain / Nao V4.
Also this project is worth a visit:
https://www.ibm.com/blogs/watson/2016/07/getting-robots-listen-using-watsons-speech-text-service/
It uses an approach by calling the linux command arecord instead of using ALAudioDevice
Have you tried the example from the documentation?
Try to run this on the robot:
#! /usr/bin/env python
# -*- encoding: UTF-8 -*-
"""Example: Get Signal from Front Microphone & Calculate its rms Power"""
import qi
import argparse
import sys
import time
import numpy as np
class SoundProcessingModule(object):
"""
A simple get signal from the front microphone of Nao & calculate its rms power.
It requires numpy.
"""
def __init__( self, app):
"""
Initialise services and variables.
"""
super(SoundProcessingModule, self).__init__()
app.start()
session = app.session
# Get the service ALAudioDevice.
self.audio_service = session.service("ALAudioDevice")
self.isProcessingDone = False
self.nbOfFramesToProcess = 20
self.framesCount=0
self.micFront = []
self.module_name = "SoundProcessingModule"
def startProcessing(self):
"""
Start processing
"""
# ask for the front microphone signal sampled at 16kHz
# if you want the 4 channels call setClientPreferences(self.module_name, 48000, 0, 0)
self.audio_service.setClientPreferences(self.module_name, 16000, 3, 0)
self.audio_service.subscribe(self.module_name)
while self.isProcessingDone == False:
time.sleep(1)
self.audio_service.unsubscribe(self.module_name)
def processRemote(self, nbOfChannels, nbOfSamplesByChannel, timeStamp, inputBuffer):
"""
Compute RMS from mic.
"""
self.framesCount = self.framesCount + 1
if (self.framesCount <= self.nbOfFramesToProcess):
# convert inputBuffer to signed integer as it is interpreted as a string by python
self.micFront=self.convertStr2SignedInt(inputBuffer)
#compute the rms level on front mic
rmsMicFront = self.calcRMSLevel(self.micFront)
print "rms level mic front = " + str(rmsMicFront)
else :
self.isProcessingDone=True
def calcRMSLevel(self,data) :
"""
Calculate RMS level
"""
rms = 20 * np.log10( np.sqrt( np.sum( np.power(data,2) / len(data) )))
return rms
def convertStr2SignedInt(self, data) :
"""
This function takes a string containing 16 bits little endian sound
samples as input and returns a vector containing the 16 bits sound
samples values converted between -1 and 1.
"""
signedData=[]
ind=0;
for i in range (0,len(data)/2) :
signedData.append(data[ind]+data[ind+1]*256)
ind=ind+2
for i in range (0,len(signedData)) :
if signedData[i]>=32768 :
signedData[i]=signedData[i]-65536
for i in range (0,len(signedData)) :
signedData[i]=signedData[i]/32768.0
return signedData
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--ip", type=str, default="127.0.0.1",
help="Robot IP address. On robot or Local Naoqi: use '127.0.0.1'.")
parser.add_argument("--port", type=int, default=9559,
help="Naoqi port number")
args = parser.parse_args()
try:
# Initialize qi framework.
connection_url = "tcp://" + args.ip + ":" + str(args.port)
app = qi.Application(["SoundProcessingModule", "--qi-url=" + connection_url])
except RuntimeError:
print ("Can't connect to Naoqi at ip \"" + args.ip + "\" on port " + str(args.port) +".\n"
"Please check your script arguments. Run with -h option for help.")
sys.exit(1)
MySoundProcessingModule = SoundProcessingModule(app)
app.session.registerService("SoundProcessingModule", MySoundProcessingModule)
MySoundProcessingModule.startProcessing()
I am developing a multiplayer game using python (pygame and podsixnet). Every time the client has to connect to the server, it has to type the server's hostname and port. I want to bypass this stage by automatically providing the client with this information. How can I find the address of my server? The game has been developed by following the tutorial:
https://www.raywenderlich.com/38732/multiplayer-game-programming-for-teens-with-python
def __init__(self):
self.justplaced=10
n = self.dialog()
print n
pygame.init()
self.clock = pygame.time.Clock()
self.screen = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
pygame.display.set_caption('Scrabble')
self.gameid=None
self.num=None
self.lastx, self.lasty = 0, 0
self.mousex = 0 # used to store x coordinate of mouse event
self.mousey = 0 # used to store y coordinate of mouse event
#print BGCOLOR
self.screen.fill(BGCOLOR)
#pygame.display.update()
self.is_Placed = self.generateBoxData(False)
self.placed_Letter = self.generateBoxData("")
self.is_Formed = self.generateBoxData(False)
self.owner = self.generateBoxData(99)
self.is_Clicked = self.generateBoxData(False)
address=raw_input("Address of Server: ")
try:
if not address:
host, port="localhost", 8000
else:
host,port=address.split(":")
self.Connect(("localhost" ,int(port)))
except:
print "Error Connecting to Server"
print "Usage:", "host:port"
print "e.g.", "localhost:31425"
exit()
print "Boxes client started"
I like making maltiplayer games too! :)
This is mostly what you may be looking for socket.gethostbyname(hostname)
a full small script would look like this:
import socket
hostname = 'maps.google.com'
addr = socket.gethostbyname(hostname)
print 'The address of ', hostname, 'is', addr
Hope this helps!
I am trying to get an acknowledgement from my subscriber back to my publisher using ZeroMQ in Python.
I tried a few code examples, using a zmq.PUSH and zmq.PULL code sequence, but to no avail.
My code as it stands:
PUB_SERVER.PY
import zmq
import random
import sys
import time
port = "5556"
if len( sys.argv ) > 1:
port = sys.argv[1]
int( port )
context = zmq.Context()
socket = context.socket( zmq.PUB )
socket.bind( "tcp://*:%s" % port )
while True:
# topic = random.randrange( 9999, 10005 )
topic = 10000
messagedata = random.randrange( 1, 215 ) - 80
print "%d %d" % ( topic, messagedata )
socket.send( "%d %d" % ( topic, messagedata ) )
time.sleep( 1 )
SUB_CLIENT.PY
import sys
import zmq
port = "5556"
if len( sys.argv ) > 1:
port = sys.argv[1]
int( port )
if len( sys.argv ) > 2:
port1 = sys.argv[2]
int( port1 )
# Socket to talk to server
context = zmq.Context()
socket = context.socket( zmq.SUB )
print "Collecting updates from weather server..."
socket.connect( "tcp://192.168.0.21:%s" % port )
if len( sys.argv ) > 2:
socket.connect( "tcp://192.168.0.21:%s" % port1 )
# Subscribe to zipcode, default is NYC, 10001
topicfilter = "10000"
socket.setsockopt( zmq.SUBSCRIBE, topicfilter )
# Process 5 updates
total_value = 0
for update_nbr in range( 5 ):
string = socket.recv()
topic, messagedata = string.split()
total_value += int( messagedata )
print topic, messagedata
print "Average messagedata value for topic '%s' was %dF" % ( topicfilter, total_value / update_nbr )
That code gives me the output of the server in one SSH window (on a Parallella), and the received filtered messages of the client in another SSH window (on a RaspberryPi) which is working great.
Where I am lost is, once the client has gotten a filtered message from the server, how would it acknowledge that filtered message being received, and then have the server log those acknowledged messages?
Eventually, I'd want to do some intelligent decision making of sending a file to the subscriber who acknowledges.
How to acknowledge?
May create a parallel messaging structure for a soft-signalling for that purpose.
Extend PUB_SERVER.PY with a .SUB Rx access point:
anAckRxSOCKET = context.socket( zmq.SUB ) # create SUB side
anAckRxSOCKET.bind( "tcp://*:%s" % aServerAckPORT ) ) # .bind()
anAckRxSOCKET.setsockopt( zmq.SUBSCRIBE, "" ) # SUB to *-anything
# ...
anAckRxSTRING = anAckRxSOCKET.recv() # .recv()
Extend SUB_CLIENT.PY with a .PUB Tx socket to the Server side access point:
anAckTxSOCKET = context.socket( zmq.PUB ) # create PUB side(s)
anAckTxSOCKET.connect( "tcp://192.168.0.21:%s" % aServerAckPORT ) )
and
send ACK(s) with "a-proxy-ID" for any server-side processing you may want or need
anAckTxSOCKET.send( topicfilter ) # ACK with an "identity"-proxy
I'm trying to transmit TCP/IP over a radio that is connected to my computer (specifically, the USRP). Right now, it's done very simply using Tun/Tap to set up a new network interface. Here's the code:
from gnuradio import gr, gru, modulation_utils
from gnuradio import usrp
from gnuradio import eng_notation
from gnuradio.eng_option import eng_option
from optparse import OptionParser
import random
import time
import struct
import sys
import os
# from current dir
from transmit_path import transmit_path
from receive_path import receive_path
import fusb_options
#print os.getpid()
#raw_input('Attach and press enter')
# Linux specific...
# TUNSETIFF ifr flags from <linux/tun_if.h>
IFF_TUN = 0x0001 # tunnel IP packets
IFF_TAP = 0x0002 # tunnel ethernet frames
IFF_NO_PI = 0x1000 # don't pass extra packet info
IFF_ONE_QUEUE = 0x2000 # beats me ;)
def open_tun_interface(tun_device_filename):
from fcntl import ioctl
mode = IFF_TAP | IFF_NO_PI
TUNSETIFF = 0x400454ca
tun = os.open(tun_device_filename, os.O_RDWR)
ifs = ioctl(tun, TUNSETIFF, struct.pack("16sH", "gr%d", mode))
ifname = ifs[:16].strip("\x00")
return (tun, ifname)
# /////////////////////////////////////////////////////////////////////////////
# the flow graph
# /////////////////////////////////////////////////////////////////////////////
class my_top_block(gr.top_block):
def __init__(self, mod_class, demod_class,
rx_callback, options):
gr.top_block.__init__(self)
self.txpath = transmit_path(mod_class, options)
self.rxpath = receive_path(demod_class, rx_callback, options)
self.connect(self.txpath);
self.connect(self.rxpath);
def send_pkt(self, payload='', eof=False):
return self.txpath.send_pkt(payload, eof)
def carrier_sensed(self):
"""
Return True if the receive path thinks there's carrier
"""
return self.rxpath.carrier_sensed()
# /////////////////////////////////////////////////////////////////////////////
# Carrier Sense MAC
# /////////////////////////////////////////////////////////////////////////////
class cs_mac(object):
"""
Prototype carrier sense MAC
Reads packets from the TUN/TAP interface, and sends them to the PHY.
Receives packets from the PHY via phy_rx_callback, and sends them
into the TUN/TAP interface.
Of course, we're not restricted to getting packets via TUN/TAP, this
is just an example.
"""
def __init__(self, tun_fd, verbose=False):
self.tun_fd = tun_fd # file descriptor for TUN/TAP interface
self.verbose = verbose
self.tb = None # top block (access to PHY)
def set_top_block(self, tb):
self.tb = tb
def phy_rx_callback(self, ok, payload):
"""
Invoked by thread associated with PHY to pass received packet up.
#param ok: bool indicating whether payload CRC was OK
#param payload: contents of the packet (string)
"""
if self.verbose:
print "Rx: ok = %r len(payload) = %4d" % (ok, len(payload))
if ok:
os.write(self.tun_fd, payload)
def main_loop(self):
"""
Main loop for MAC.
Only returns if we get an error reading from TUN.
FIXME: may want to check for EINTR and EAGAIN and reissue read
"""
min_delay = 0.001 # seconds
while 1:
payload = os.read(self.tun_fd, 10*1024)
if not payload:
self.tb.send_pkt(eof=True)
break
if self.verbose:
print "Tx: len(payload) = %4d" % (len(payload),)
delay = min_delay
while self.tb.carrier_sensed():
sys.stderr.write('B')
time.sleep(delay)
if delay < 0.050:
delay = delay * 2 # exponential back-off
self.tb.send_pkt(payload)
# /////////////////////////////////////////////////////////////////////////////
# main
# /////////////////////////////////////////////////////////////////////////////
def main():
mods = modulation_utils.type_1_mods()
demods = modulation_utils.type_1_demods()
parser = OptionParser (option_class=eng_option, conflict_handler="resolve")
expert_grp = parser.add_option_group("Expert")
parser.add_option("-m", "--modulation", type="choice", choices=mods.keys(),
default='gmsk',
help="Select modulation from: %s [default=%%default]"
% (', '.join(mods.keys()),))
parser.add_option("-v","--verbose", action="store_true", default=False)
expert_grp.add_option("-c", "--carrier-threshold", type="eng_float", default=30,
help="set carrier detect threshold (dB) [default=%default]")
expert_grp.add_option("","--tun-device-filename", default="/dev/net/tun",
help="path to tun device file [default=%default]")
transmit_path.add_options(parser, expert_grp)
receive_path.add_options(parser, expert_grp)
for mod in mods.values():
mod.add_options(expert_grp)
for demod in demods.values():
demod.add_options(expert_grp)
fusb_options.add_options(expert_grp)
(options, args) = parser.parse_args ()
if len(args) != 0:
parser.print_help(sys.stderr)
sys.exit(1)
if options.rx_freq is None or options.tx_freq is None:
sys.stderr.write("You must specify -f FREQ or --freq FREQ\n")
parser.print_help(sys.stderr)
sys.exit(1)
# open the TUN/TAP interface
(tun_fd, tun_ifname) = open_tun_interface(options.tun_device_filename)
# Attempt to enable realtime scheduling
r = gr.enable_realtime_scheduling()
if r == gr.RT_OK:
realtime = True
else:
realtime = False
print "Note: failed to enable realtime scheduling"
# If the user hasn't set the fusb_* parameters on the command line,
# pick some values that will reduce latency.
if options.fusb_block_size == 0 and options.fusb_nblocks == 0:
if realtime: # be more aggressive
options.fusb_block_size = gr.prefs().get_long('fusb', 'rt_block_size', 1024)
options.fusb_nblocks = gr.prefs().get_long('fusb', 'rt_nblocks', 16)
else:
options.fusb_block_size = gr.prefs().get_long('fusb', 'block_size', 4096)
options.fusb_nblocks = gr.prefs().get_long('fusb', 'nblocks', 16)
#print "fusb_block_size =", options.fusb_block_size
#print "fusb_nblocks =", options.fusb_nblocks
# instantiate the MAC
mac = cs_mac(tun_fd, verbose=True)
# build the graph (PHY)
tb = my_top_block(mods[options.modulation],
demods[options.modulation],
mac.phy_rx_callback,
options)
mac.set_top_block(tb) # give the MAC a handle for the PHY
if tb.txpath.bitrate() != tb.rxpath.bitrate():
print "WARNING: Transmit bitrate = %sb/sec, Receive bitrate = %sb/sec" % (
eng_notation.num_to_str(tb.txpath.bitrate()),
eng_notation.num_to_str(tb.rxpath.bitrate()))
print "modulation: %s" % (options.modulation,)
print "freq: %s" % (eng_notation.num_to_str(options.tx_freq))
print "bitrate: %sb/sec" % (eng_notation.num_to_str(tb.txpath.bitrate()),)
print "samples/symbol: %3d" % (tb.txpath.samples_per_symbol(),)
#print "interp: %3d" % (tb.txpath.interp(),)
#print "decim: %3d" % (tb.rxpath.decim(),)
tb.rxpath.set_carrier_threshold(options.carrier_threshold)
print "Carrier sense threshold:", options.carrier_threshold, "dB"
print
print "Allocated virtual ethernet interface: %s" % (tun_ifname,)
print "You must now use ifconfig to set its IP address. E.g.,"
print
print " $ sudo ifconfig %s 192.168.200.1" % (tun_ifname,)
print
print "Be sure to use a different address in the same subnet for each machine."
print
tb.start() # Start executing the flow graph (runs in separate threads)
mac.main_loop() # don't expect this to return...
tb.stop() # but if it does, tell flow graph to stop.
tb.wait() # wait for it to finish
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
pass
(Anyone familiar with GNU Radio will recognize this as tunnel.py)
My question is, is there a better way to move packets to and from the kernel than tun/tap? I've been looking at ipip or maybe using sockets, but I'm pretty sure those won't be very fast. Speed is what I'm most concerned with.
Remember that tunnel.py is a really, really rough example, and hasn't been updated in a while. It's not really meant to be a basis for other code, so be careful of how much you rely on the code.
Also, remember that TCP over unreliable radio links has significant issues:
http://en.wikipedia.org/wiki/Transmission_Control_Protocol#TCP_over_wireless_networks
I trying to write a client server application in Python, but I faced a problem, in the client side I'm not getting all the sent data. First I tried to send the numbers 1 to 10 and I received 1,2,5,6,10, so there are missing a lot of numbers.
Server side:
def __init__( self ):
super( MCCommunication, self ).__init__()
HOST, PORT = socket.gethostbyname( socket.gethostname() ), 31000
self.server = SocketServer.ThreadingTCPServer( ( HOST, PORT ), MCRequestHandler )
ip, port = self.server.server_address
# Start a thread with the server
# Future task: Make the server a QT-Thread...
self.server_thread = threading.Thread( target = self.server.serve_forever )
# Exit the server thread when the main thread terminates
self.server_thread.setDaemon( True )
self.textUpdated.emit( 'Server Started!' )
print( 'Server Started!' )
self.server_thread.start()
def handle( self ):
#self.request.setblocking( 0 )
i = 10;
while True:
if( self.clientname == 'MasterClient' ):
try:
#ans = self.request.recv( 4096 )
#print( 'after recv' )
""" Sendign data, testing purpose """
while i:
mess = str( i );
postbox['MasterClient'].put( self.creatMessage( 0, 0 , mess ) )
i = i - 1
while( postbox['MasterClient'].empty() != True ):
sendData = postbox['MasterClient'].get_nowait()
a = self.request.send( sendData )
print( a );
#dic = self.getMessage( sendData )
#print 'Sent:%s\n' % str( dic )
except:
mess = str( sys.exc_info()[0] )
postbox['MasterClient'].put( self.creatMessage( 1, 0 , mess ) )
pass
def creatMessage( self, type1 = 0, type2 = 0, message = ' ', extra = 0 ):
return pickle.dumps( {"type1":type1, "type2":type2, "message":message, "extra":extra} );
Where the postbox['MasterClient'] is a Queue with the serialized message.
And this is the client:
def run( self ):
sock = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
addr = ( self.ip, self.port )
#print addr
sock.connect( addr )
#sock.setblocking( 0 )
while True:
try:
ans = sock.recv( 4096 )
dic = self.getMessage( ans )
self.recvMessageHandler( dic )
print 'Received:%s\n' % str( dic )
except:
pass
The server may have sent multiple messages by the time the client attempts to read them, and if these fit within the same 4k buffer, the recv() call will obtain both of them.
You don't show the getMessage code, but I'd guess you're doing something like pickle.loads(msg), but this will only give you the first message and discard the rest of the string, hence the dropped messages. You'll also get another issue if more than 4096 bytes are buffered by the time you read, as you could end up getting a fragment of a message and thus an unpickling error.
You'll need to break up the string you get back into seperate messages, or better, just treat the socket as a stream and let pickle.load pull a single message from it.