Je travaille sur une application de téléphone Android (H
) qui parle à un pair, un RPi 3 (P
), via UDP hole-punching. J'ai deux sockets (s
et s_vid
) sur H qui seront tous deux perforés grâce à un serveur maître (M
), et un socket (S3
pour plus de clarté, mais 's
' dans le code) sur P
. H est sur le réseau mobile, P sur ma connexion Wi-Fi et M sur Google Cloud Engine.Android UDP trou-poinçonnage échoue sur deuxième socket (Python + Java)
Déroulement du programme:
H
utilise s
ping M
, M
envoie adr/informations port à P
.
H
utilise s_vid
ping M
, M
envoie adr/informations port à P
.
P
utilise S3
ping M
, M
envoie adr/informations port à H
.
(nous devrions maintenant être perforé trou-)
H
utilise s
pour envoyer des paquets périodiques à P
sur le port de S3
.
P
utilise S3
pour envoyer des paquets périodiques à H
sur le port s
.
(tout cela fonctionne très bien sans problème ... ci-dessous ne fonctionne pas)
P
utilise S3
pour envoyer des paquets périodiques à H
sur le port de s_vid
. (ou, plutôt, il devrait).
Nous devrions avoir 3 flux de paquets: H @ s -> P @ S3, P @ S3 -> H @ s et P @ S3 -> H @ s_vid, mais pour une raison que la première deux travaux. Le dernier se bloque sur la ligne s_vid.receive()
(voir code).
Voici le code (correspondant) H:
public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback, MediaPlayer.OnPreparedListener, SensorEventListener{
public String messageStr;
public String masterMessageStr;
public int master_msg_length;
public int msg_length;
public String masterMessageVidStr;
public int master_msg_vid_length;
public DatagramPacket p_peer;
public DatagramPacket p_master;
public DatagramPacket p_master_vid;
public DatagramPacket p_rec;
public DatagramPacket p_rec_vid;
public DatagramSocket s;
public DatagramSocket s_vid;
public InetAddress return_peer_addr;
public int return_peer_port;
public InetAddress master_addr;
public int master_port;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public void onStart() {
super.onStart();
}
@Override
public void onStop() {
super.onStop();
}
@Override
protected void onResume() {
super.onResume();
//Define Sockets
try {
s = new DatagramSocket();
s.setReuseAddress(true);
}
catch (Exception e)
{
e.printStackTrace();
}
try {
s_vid = new DatagramSocket();
s_vid.setReuseAddress(true);
}
catch (Exception e)
{
e.printStackTrace();
}
try {
master_addr = InetAddress.getByName("xx.xxx.xxx.xxx");//hardcoded server address
} catch (Exception e)
{
e.printStackTrace();
}
master_port = 1111;//hardcoded server port
//listen for server or peer packets.
Runnable receive_thread_run = new Runnable() {
@Override
public void run() {
while(true){
byte[] buf = new byte[1024];
p_rec = new DatagramPacket(buf,buf.length);
try {
s.receive(p_rec);
//Do stuff...
//this works fine.
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
};
//listen for peer packets. PROBLEM IS HERE.
Runnable receive_thread_run_vid = new Runnable() {
@Override
public void run() {
byte[] buf_vid = new byte[1024];
p_rec_vid = new DatagramPacket(buf_vid,buf_vid.length);
try {
s_vid.receive(p_rec_vid);//this is reached, but never receives...
//Do stuff...never reached.
}
catch (Exception e)
{
e.printStackTrace();
}
}
};
//Start the threads
Thread receive_thread_vid = new Thread(receive_thread_run_vid);
receive_thread_vid.start();
Thread receive_thread = new Thread(receive_thread_run);
receive_thread.start();
//Send out pings to master server for hole-punching.
TimerTask return_master = new TimerTask(){
@Override
public void run() {
masterMessageStr = "someping";
master_msg_length = masterMessageStr.length();
byte[] msg = masterMessageStr.getBytes();
p_master = new DatagramPacket(msg,master_msg_length,master_addr,master_port);
try {
s.send(p_master);
}
catch (Exception e)
{
e.printStackTrace();
}
}
};
Timer return_master_timer = new Timer();
return_master_timer.scheduleAtFixedRate(return_master,1000,1000);
//Send out pings to master server for hole-punching (2nd socket).
TimerTask return_master_vid = new TimerTask(){
@Override
public void run() {
masterMessageVidStr = "someotherping";
master_msg_vid_length = masterMessageVidStr.length();
byte[] msg_vid = masterMessageVidStr.getBytes();
p_master_vid = new DatagramPacket(msg_vid,master_msg_vid_length,master_addr,master_port);
try {
s_vid.send(p_master_vid);
}
catch (Exception e)
{
e.printStackTrace();
}
}
};
Timer return_master_timer_vid = new Timer();
return_master_timer_vid.scheduleAtFixedRate(return_master_vid,1000,1000);
//Talk directly to the peer.
TimerTask return_peer = new TimerTask(){
@Override
public void run() {
//Do stuff...
messageStr = "some peer-directed message";
msg_length = messageStr.length();
byte[] msg = messageStr.getBytes();
p_peer = new DatagramPacket(msg,msg_length,return_peer_addr,return_peer_port);
//check we know where to send it
if (return_peer_addr != null && return_peer_port != 0) {
try {
s.send(p_peer);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
};
Timer return_peer_timer = new Timer();
return_peer_timer.scheduleAtFixedRate(return_peer,500,500);//time in milliseconds
}
}
Voici le code (correspondant) M:
import socket
import sys
import time
import threading
## peer-Master UDP Comms
def receive_peer():#gets peer addr and sends it to phone.
global peer_addr
global peer_port
while 1:
data, addr = s_peer.recvfrom(1024)
#figure out who is talking (should only be peer)
if data == 'somepeerping':
peer_addr = addr[0]
peer_port = addr[1]
#send it to phone
if phone_addr is not None and peer_addr is not None and peer_port is not None:
phone_ret_data = '{\"return_addr\":\"'+peer_addr+'\",\"return_port\":\"'+str(peer_port)+'\"}'
s_phone.sendto(phone_ret_data,(phone_addr,phone_port))
## Phone-Master UDP Comms
def receive_phone():#gets phone addr (and phone vid addr) and sends it to peer.
global phone_addr
global phone_port
global phone_addr_vid
global phone_port_vid
global peer_ret_data
global peer_ret_data_vid
while 1:
data, addr = s_phone.recvfrom(1024)
#figure out who is talking (whih of the two sockets)
if data == 'someping':
phone_addr = addr[0]
phone_port = addr[1]
if peer_addr is not None and phone_addr is not None and phone_port is not None:
peer_ret_data = '{\"return_addr\":\"'+phone_addr+'\",\"msg_type\":\"novid\",\"return_port\":\"'+str(phone_port)+'\"}'
s_peer.sendto(peer_ret_data,(peer_addr,peer_port))
elif data == 'someotherping':
phone_addr_vid = addr[0]
phone_port_vid = addr[1]
if peer_addr is not None and phone_addr_vid is not None and phone_port_vid is not None:
peer_ret_data_vid = '{\"return_addr\":\"'+phone_addr_vid+'\",\"msg_type\":\"vid\",\"return_port\":\"'+str(phone_port_vid)+'\"}'
s_peer.sendto(peer_ret_data_vid,(peer_addr,peer_port))
HOST = ''
PORT_peer = 2222
PORT_phone = 1111
peer_addr = None
peer_port = None
phone_addr = None
phone_port = None
phone_addr_vid = None
phone_port_vid = None
peer_ret_data = None
peer_ret_data_vid = None
phone_ret_data = None
## peer socket
try:
s_peer = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s_peer.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
print("Socket created.")
except socket.error, msg:
print("Failed. Error: " + str(msg))
sys.exit()
try:
s_peer.bind((HOST,PORT_peer))
print("Socket binding complete.")
except socket.error, msg:
print("Bind failed. Error: " + str(msg))
s_peer.close()
sys.exit()
## Phone socket
try:
s_phone = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s_phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
print("Socket created.")
except socket.error, msg:
print("Failed. Error: " + str(msg))
sys.exit()
try:
s_phone.bind((HOST,PORT_phone))
print("Socket binding complete.")
except socket.error, msg:
print("Bind failed. Error: " + str(msg))
s_phone.close()
sys.exit()
## Initiate threads
threading.Thread(target=receive_peer).start()
threading.Thread(target=receive_phone).start()
Voici le code (correspondant) P:
import socket
import sys
import time
import threading
import json
import time
import subprocess
## UDP Comms - thread to receive incoming packets.
def receive_thread():
global master_addr
global master_port
global return_phone_addr
global return_phone_port
global return_phone_vid_addr
global return_phone_vid_port
global phone_packet_count
while 1:
data, addr = s.recvfrom(1024)
if addr[0] == master_addr and addr[1] == master_port:#message from master server, update return addresses
master_msg_received = json.loads(data)
master_msg_received = {str(key):str(value) for key,value in master_msg_received.items()}#to remove unicode
if master_msg_received['msg_type'] == 'novid':
return_phone_addr = master_msg_received['return_addr']
return_phone_port = int(master_msg_received['return_port'])
elif master_msg_received['msg_type'] == 'vid':
return_phone_vid_addr = master_msg_received['return_addr']
return_phone_vid_port = int(master_msg_received['return_port'])
elif addr[0] == return_phone_addr and addr[1] == return_phone_port and return_phone_addr is not None:#message from phone
phone_packet_count = phone_packet_count + 1
msg_received = json.loads(data)
msg_received = json.dumps(msg_received)#to remove unicode
#Do stuff...no problems here or above.
## UDP Comms - threads to send out packets.
#send our address to master.
def ping_master():
global master_addr
global master_port
s.sendto('somepeerping',(master_addr,master_port))
threading.Timer(1,ping_master).start()
#return data to phone (for now packet count).
def return_phone():
global return_phone_addr
global return_phone_port
global phone_packet_count
if return_phone_addr is not None:
s.sendto(str(phone_packet_count),(return_phone_addr,return_phone_port))
threading.Timer(0.5,return_phone).start()
#send other data to phone on its second socket. POSSIBLE PROBLEM.
def return_phone_vid():
global return_phone_addr
global return_phone_port
global return_phone_vid_addr
global return_phone_vid_port
if return_phone_vid_addr is None:
threading.Timer(0.7,return_phone_vid).start()
else:
s.sendto('some second socket test ping',(return_phone_vid_addr,return_phone_vid_port))
threading.Timer(0.7,return_phone_vid).start()
HOST = ''
PORT_phone = 0 #this is where phone comms arrive.
phone_packet_count = 0
return_phone_addr = None #this is where phone comms go.
return_phone_port = None #this is where phone comms go.
return_phone_vid_addr = None #this is where phone comms go.
return_phone_vid_port = None #this is where phone comms go.
master_addr = 'xx.xxx.xxx.xxx'#this is where master comms go. hardcoded.
master_port = 2222#this is where master comms go.
PORT_master = 0 #this is where master comms arrive.
## UDP Comms - initialize sockets.
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
print("Socket created.")
except socket.error, msg:
print("Failed. Error: " + str(msg))
sys.exit()
try:
s.bind((HOST,PORT_phone))
print("Socket binding complete.")
except socket.error, msg:
print("Bind failed. Error: " + str(msg))
s.close()
sys.exit()
## Initiate threads
threading.Thread(target=receive_thread).start()
ping_master()
return_phone()
return_phone_vid()
Des idées sur ce qui pourrait en être la cause? J'ai déjà confirmé que P
reçoit 2 ports différents (même adresse) pour s
et s_vid
. Je m'attendrais à ce que les deux sockets échouent ou pas, pas seulement un. Peut-être un problème de pare-feu? Mais encore une fois, pourquoi une seule socket échoue? Merci pour l'aide!