【问题标题】:How to make it work for p2p?如何使其适用于 p2p?
【发布时间】:2014-10-25 21:15:55
【问题描述】:

有 3 个部分,服务器端,PC 的客户端 A 和 android 的客户端 B。
逻辑是:
1.当客户端A和B连接到服务器时,会通知对方的ip和端口。
2. 然后A给B打一个洞,告诉服务器它已经准备好接收来自B的消息了。
3. 服务器会告诉 B A 准备好了。
4. 然后B会向A发送消息。

这只是一个简单的测试,测试环境有:
1.服务器有公网IP和端口
2.Client A和Client B在不同的NAT中

下面的代码不起作用。两个对等方无法相互通信。如何让两个peer通信?我会感激每一条建议。

[编辑]
谢谢,@kha。我第一次在这里发帖。我会详细说明过程。
1. 3个部分也不例外。我没有错误。
2.两个客户端可以连接到服务器并从服务器接收另一个的IP和端口。
3. 我关闭了 Windows 的防火墙。对于android,我不知道该怎么做。
4. 两个客户端就是不能通信,没有其他异常。
5. 我是 P2P 新手,所以我想要更多帮助来找到问题。

服务器端代码:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;

public class Main {

    static EndPoint endPoint;
    static EndPoint endPoint2;

    /**
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) {
        try {
            DatagramSocket server = new DatagramSocket(5050);
            boolean isSend = false;
            while (true) {
                System.out.println("start receiving...");

                EndPoint ep = receiveMessage(server);

                if (endPoint == null) {
                    System.out.println("endPoint initialized");
                    endPoint = ep;
                } else if (endPoint2 == null) {
                    System.out.println("endPoint2 initialized");
                    endPoint2 = ep;
                } else {
                    if (!isSend) {//when A and B all connect to server, they will be notified another's ip and port
                        isSend = true;
                        System.out.println("send to each other");
                        sendEndPointMessage(server, endPoint, endPoint2);
                        sendEndPointMessage(server, endPoint2, endPoint);
                    } else {
                        if ("5".equals(ep.message)) {// client A is ready
                            System.out.println("client A is ready");
                            sendMessage(server, "6", endPoint2);// send B message that A is reay
                        }
                    }
                }
            }
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void sendEndPointMessage(DatagramSocket server, EndPoint epReceive, EndPoint epContent) {
        try {
            String sendStr = epContent.host + ";" + epContent.port;
            byte[] sendBuf = sendStr.getBytes();
            DatagramPacket sendPacket = new DatagramPacket(sendBuf, sendBuf.length, epReceive.addr, epReceive.port);
            server.send(sendPacket);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private static void sendMessage(DatagramSocket server, String msg, EndPoint ep) {
        try {
            byte[] sendBuf = msg.getBytes();
            DatagramPacket sendPacket = new DatagramPacket(sendBuf, sendBuf.length, ep.addr, ep.port);
            server.send(sendPacket);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private static EndPoint receiveMessage(DatagramSocket server) {
        try {
            byte[] recvBuf = new byte[1024];
            DatagramPacket recvPacket = new DatagramPacket(recvBuf, recvBuf.length);
            server.receive(recvPacket);

            String recvStr = new String(recvPacket.getData(), 0, recvPacket.getLength());
            int port = recvPacket.getPort();
            InetAddress addr = recvPacket.getAddress();

            System.out.println("EndPoint: host:" + recvPacket.getAddress() + ", port:" + port + ", message:" + recvStr);

            EndPoint endPoint = new EndPoint(addr, port, recvStr);
            return endPoint;
        } catch (IOException e) {

            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    static class EndPoint {
        InetAddress addr;
        String host;
        int port;
        String message;

        public EndPoint(String host, int port, String message) {
            this.host = host;
            this.port = port;
            this.message = message;

            try {
                addr = InetAddress.getByName(host);
            } catch (UnknownHostException e) {
                e.printStackTrace();
            }
        }

        public EndPoint(InetAddress host, int port, String message) {
            this.host = host.getHostAddress();
            this.port = port;
            this.message = message;

            addr = host;
        }
    }

}

PC 客户端 A:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;

public class Main {
    public static String serverAddr = "xxx.xxx.xxx.xx";
    public static int serverPort = 5050;
    public static String peerIP;
    public static int peerPort;

    /**
     * @param args
     */
    public static void main(String[] args) {

        try {
            final DatagramSocket client = new DatagramSocket();

            sendMessage(client, "1", serverAddr, serverPort);

            while (true) {
                System.out.println("start receiving...");

                String recvStr = receiveMessage(client);

                if ("3".equals(recvStr)) {
                    sendMessage(client, "4", peerIP, peerPort);
                } else {
                    String[] peer = recvStr.split(";");
                    if (peer.length > 1) {// received peer ip and port
                        peerIP = peer[0];
                        peerPort = Integer.parseInt(peer[1]);
                        sendMessage(client, "4", peerIP, peerPort);//punch a hole for client B
                        sendMessage(client, "5", serverAddr, serverPort);//tell server I am ready
                        new Thread(new Runnable() {

                            @Override
                            public void run() {
                                for (int i = 0; i < 10; i++) {
                                    sendMessage(client, "4", peerIP, peerPort);//keep punch a hole for client B
                                    sendMessage(client, "5", serverAddr, serverPort);//keep telling server I am ready
                                    try {
                                        Thread.sleep(500);
                                    } catch (InterruptedException e) {
                                        e.printStackTrace();
                                    }
                                }
                            }
                        }).start();
                    } else {
                        if ("7".equals(recvStr)) {// received from client B
                            sendMessage(client, "got from android", peerIP, peerPort);
                        }
                    }
                }
            }
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static String receiveMessage(final DatagramSocket client) {
        try {
            byte[] recvBuf = new byte[1024];
            DatagramPacket recvPacket = new DatagramPacket(recvBuf, recvBuf.length);
            client.receive(recvPacket);
            String recvStr = new String(recvPacket.getData(), 0, recvPacket.getLength());
            String ip = recvPacket.getAddress().getHostAddress();
            int port = recvPacket.getPort();
            System.out.println("received from: host:" + ip + ", port:" + port + ", content:" + recvStr);
            return recvStr;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private static void sendMessage(final DatagramSocket client, String sendStr, String ip, int port) {
        try {
            System.out.println("send out: host:" + ip + ", port: " + port + ", message:" + sendStr);
            byte[] sendBuf;
            sendBuf = sendStr.getBytes();
            InetAddress addr = InetAddress.getByName(ip);
            DatagramPacket sendPacket = new DatagramPacket(sendBuf, sendBuf.length, addr, port);
            client.send(sendPacket);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

Android 客户端 B:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;

public class MainActivity extends Activity {
    public String serverAddr = "xxx.xxx.xxx.xx";
    public int serverPort = 5050;
    public String peerIP;
    public int peerPort;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.connectServer).setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {

                    @Override
                    public void run() {
                        connectServer();
                    }
                }).start();

            }
        });
    }

    private void connectServer() {
        try {
            DatagramSocket client = new DatagramSocket();

            sendMessage(client, "2", serverAddr, serverPort);

            while (true) {
                System.out.println("start receiving...");

                String recvStr = receiveMessage(client);

                String[] peer = recvStr.split(";");
                if (peer.length > 1) {// received peer ip and port
                    peerIP = peer[0];
                    peerPort = Integer.parseInt(peer[1]);
                } else {
                    if ("6".equals(recvStr)) {//received from server that client A is ready                     
                        sendMessage(client, "7", peerIP, peerPort); // send message to client A                 
                    }
                }
            }

        } catch (SocketException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private String receiveMessage(DatagramSocket client) throws IOException {
        try {
            byte[] recvBuf = new byte[1024];
            DatagramPacket recvPacket = new DatagramPacket(recvBuf, recvBuf.length);
            client.receive(recvPacket);
            String ip = recvPacket.getAddress().getHostAddress();
            int port = recvPacket.getPort();
            String recvStr = new String(recvPacket.getData(), 0, recvPacket.getLength());
            System.out.println("received from: host:" + ip + ", port:" + port + ", content:" + recvStr);
            return recvStr;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private void sendMessage(DatagramSocket client, String sendStr, String ip, int port) {
        try {
            System.out.println("send out: host:" + ip + ", port:" + port + ", mesasge:" + sendStr);
            byte[] sendBuf;
            sendBuf = sendStr.getBytes();
            InetAddress addr = InetAddress.getByName(ip);
            DatagramPacket sendPacket = new DatagramPacket(sendBuf, sendBuf.length, addr, port);
            client.send(sendPacket);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

【问题讨论】:

  • 解释一下“不工作”是什么意思。
  • @kingdamian42 已修改,不工作意味着两个对等方无法相互通信。
  • 哪个部分不起作用?你得到什么错误?他们都可以连接到您的服务器并获取彼此的IP地址吗?有防火墙问题吗?你有网络异常吗?您是否尝试过缩小问题范围并找出原因?
  • 谢谢,@kha 我第一次在这里发帖。我会详细说明这个过程。 1. 3个部分也不例外。我没有错误。 2. 两个客户端可以连接到服务器,并从服务器接收另一个的 ip 和端口。 3. 我关闭了 Windows 的防火墙。对于android,我不知道该怎么做。 4. 两个客户端就是不能通信,没有其他异常。 5. 我是 P2P 新手,所以我想要更多的帮助来找到问题。
  • @kha,我又多了一台服务器进行测试。并且发现从具有相同端口的客户端B到服务器A和服务器B,NAT打开了两个不同的端口。我的 NAT 是对称 NAPT。所以我认为这是两个对等点无法通信的根本原因。感谢您的耐心。

标签: java android p2p


【解决方案1】:

我又多了一台服务器进行测试。并且发现从客户端B的同一个端口到服务器A和服务器B,有两个不同的端口被NAT打开。
我的 NAT 是对称 NAPT。所以我认为这是两个对等点无法通信的根本原因。
@KunjanThadani,你是对的。

【讨论】:

  • 好的。您可以尝试端口预测。但这仍然是一个复杂的过程,并且不确定是否有效。
  • @KunjanThadani,根据我的测试,打开的端口顺序不正常。所以我认为唯一的方法是通过服务器传输消息。
  • 好的。是的,通过服务器中继将起作用。但如果它只是关于“短信”,我建议你改用 tcp。它会更可靠。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-01-14
  • 2019-09-12
  • 2010-12-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多