【问题标题】:Android UDP hole-punching fails on second socket (Python+Java)Android UDP 打孔在第二个套接字上失败(Python+Java)
【发布时间】:2017-12-17 15:40:58
【问题描述】:

我正在开发一个 Android 手机应用程序 (H),该应用程序通过 UDP 打孔与 Peer、RPi 3 (P) 对话。我在 H 上有两个套接字(ss_vid),这要归功于主服务器(M)和一个套接字(S3 为清楚起见,但 's' 在代码)在P。 H 在移动网络上,P 在我的 wifi 上,M 在 Google Cloud Engine 上。

程序流程:


H 使用s ping MM 将地址/端口信息发送到P

H 使用s_vid ping MM 将地址/端口信息发送到P

P 使用S3 ping MM 将地址/端口信息发送到H

(我们现在应该被打孔了)

H 使用sS3 的端口上定期向P 发送数据包。

P 使用S3s 的端口上定期向H 发送数据包。

(所有这些都可以正常工作,没有任何问题...下面不工作)

P 使用S3s_vid 的端口上定期向H 发送数据包。 (或者,更确切地说,它应该)。


所以我们应该有 3 个数据包流:H@s -> P@S3、P@S3 -> H@s 和 P@S3 -> H@s_vid,但由于某种原因,只有前两个有效。最后一个挂在s_vid.receive() 行(见代码)。

这是 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
    }
}

这是 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()

这是 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()

任何想法可能导致这种情况?我已经确认P 正在为ss_vid 接收2 个不同的端口(相同的地址)。我希望两个套接字都失败或没有,而不仅仅是一个。也许是防火墙问题?但同样,为什么只有一个套接字失败?感谢您的帮助!

【问题讨论】:

    标签: java android python sockets udp


    【解决方案1】:

    这对我来说是一个打孔错误。打孔的插座需要直接相互通信。所以按照我的问题风格,我有:

    P@S3 -> H@s_vid 但不是 H@s_vid -> P@S3(我在另一个套接字上有两个方向)。解决方法是将该方向添加到电话应用程序中,类似于

    TimerTask return_peer_vid = new TimerTask(){
                @Override
                public void run() {
    
                    retMessageVidStr = "test";
    
                    ret_msg_vid_length = retMessageVidStr.length();
                    byte[] msg = retMessageVidStr.getBytes();
    
                    p_peer_vid = new DatagramPacket(msg,ret_msg_vid_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_vid.send(p_peer_vid);
                        }
                        catch (Exception e)
                        {
                            e.printStackTrace();
                        }
                    }
                }
            };
    
            Timer return_peer_timer_vid = new Timer();
            return_peer_timer_vid.scheduleAtFixedRate(return_peer_vid,500,500);//time in milliseconds
    

    【讨论】:

      猜你喜欢
      • 2017-08-27
      • 2014-10-17
      • 2016-07-23
      • 2015-11-30
      • 1970-01-01
      • 2015-08-02
      • 2013-01-15
      • 2015-01-23
      • 1970-01-01
      相关资源
      最近更新 更多