udp打孔错误的端口?

时间:2015-11-16 20:16:54

标签: java sockets udp hole-punching

问题:端口是否错误(进行udp冲击)?

我有一台Java服务器,用于保存连接客户端和服务器的ip和端口。 ip和ports被发送到客户端和服务器,因此他们可以启动udp打孔。我使用2台计算机连接到同一个NAT。但它拒绝工作。

java服务器日志说:

Send server global ip and port 61721 to client
Send client global ip and port 63105 to server

服务器和客户端开始发送udp数据包,但一段时间后它们会超时。 我使用Wireshark检查打孔,客户端日志如下所示:

Source          Destination     Info
192.168.1.78    206.217.173.176 UDP source port: 50701 Destination Port: 61721
192.168.1.78    206.217.173.176 UDP source port: 50701 Destination Port: 61721
... + 50 more   

服务器日志:

Source          Destination     Info
192.168.1.73    206.217.173.176 UDP source port: 6510 Destination Port: 63105
192.168.1.73    206.217.173.176 UDP source port: 6510 Destination Port: 63105
... + 50 more   

在服务器日志中找不到来自客户端的数据包。并且在客户端日志中找不到服务器数据包。

这是Java服务器代码:

完整代码可在以下网址找到:https://github.com/Parakoopa/GMnet-GATE-PUNCH/tree/master/src/main/java/org/parakoopa/gmnetgate/punch

Server.java

package org.parakoopa.gmnetgate.punch;

import com.google.gson.annotations.Expose;
import java.net.Socket;

public class Server {
     /**
     * Contains the IP of this server
     */
    @Expose private String ip = "";
     /**
     * Contains the Ports of the Server.
     * A port is assigned to an ip, so only one ip per server is possible.
     */
    private Integer port = 0;

    /**
     * Contains the TCP Sockets of the Server.
     * For sending the connection requests to the servers.
     */
    private Socket tcp_socket = null;
    /**
     * The 8 data strings.
     */
    @Expose private String data1 = "";
    @Expose private String data2 = "";
    @Expose private String data3 = "";
    @Expose private String data4 = "";
    @Expose private String data5 = "";
    @Expose private String data6 = "";
    @Expose private String data7 = "";
    @Expose private String data8 = "";

    /**
     * Time the server was created
     */
    @Expose private long createdTime;

    public Server(String ip) {
        this.createdTime = System.currentTimeMillis() / 1000L;
        this.ip = ip;
    }

     /**
     * Contains the Ports of the Server.
     * A port is assigned to an ip, so only one ip per server is possible.
     */
    public Integer getPort() {
        return port;
    }

    public void setPort(Integer port) {
        this.port = port;
    }

    /**
     * Contains the TCP Sockets of the Server.
     * For sending the connection requests to the servers.
     */
    public Socket getTCPsocket() {
        return tcp_socket;
    }

    public void setTCPsocket(Socket tcp_socket) {
        this.tcp_socket = tcp_socket;
    }

    public String getData1() {
        return data1;
    }

    public void setData1(String data1) {
        this.data1 = data1;
    }

    public String getData2() {
        return data2;
    }

    public void setData2(String data2) {
        this.data2 = data2;
    }

    public String getData3() {
        return data3;
    }

    public void setData3(String data3) {
        this.data3 = data3;
    }

    public String getData4() {
        return data4;
    }

    public void setData4(String data4) {
        this.data4 = data4;
    }

    public String getData5() {
        return data5;
    }

    public void setData5(String data5) {
        this.data5 = data5;
    }

    public String getData6() {
        return data6;
    }

    public void setData6(String data6) {
        this.data6 = data6;
    }

    public String getData7() {
        return data7;
    }

    public void setData7(String data7) {
        this.data7 = data7;
    }

    public String getData8() {
        return data8;
    }

    public void setData8(String data8) {
        this.data8 = data8;
    }

    public long getCreatedTime() {
        return createdTime;
    }

}

TCPConnection.java:

package org.parakoopa.gmnetgate.punch;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * Handles incoming TCP connections.
 * Stores server sockets and on client request send server ports to client and
 * client ports to server via the stored socket.
 * @author Parakoopa
 */
public class TCPConnection implements Runnable {

    private Mediator main;
    private Socket client;
    private ServerSocket server;
    /** Game Maker Studio seperates strings in buffers with this char (buffer_string). */
    private char gm_string_seperator = 0;
    /** True, if the "reg" command was used on this connection **/
    private boolean isServer = false;

    /**
     * Set's up a new connection listener that handles all packets of one connection.
     * @param main Mediator class instance that this server was created with.
     * @param client Socket that the client is connected to.
     * @param server Our TCP server socket the client is connected to. (not actually used)
     */
    public TCPConnection(Mediator main, Socket client, ServerSocket server) {
        this.server = server;
        this.client = client;
        this.main = main;
    }

    /**
     * Starts listening for incoming packets and responds to it.
     */
    @Override
    public void run() {
        String debug_string = this.client.getInetAddress().getHostAddress()+":"+this.client.getPort()+" | TCP | ";
        Mediator.log(debug_string+" Connected!",true);
        try {
            //TcpNoDelay configures the socket to transfer messages immediately, otherwise GM:S won't pick them up
            this.client.setTcpNoDelay(true);
            //Input and Output streams. We write bytes out and take Strings in.
            OutputStream out = client.getOutputStream();
            BufferedReader in = new BufferedReader(
                    new InputStreamReader(client.getInputStream()));

            String inputLine;
            Server serverObj;
            //Process all packets. This while loop will stop when the peer disconnected.
            while ((inputLine = in.readLine()) != null) {
                //This will kill Threads (or commands) if the client don't send
                //all of the data expected. Needed otherwise this could lead
                //to many hanging threads.
                client.setSoTimeout(1000);
                //Cleans string, it might contain some garbage characters.
                inputLine = inputLine.replaceAll("\\p{C}", "");
                switch (inputLine) {
                    case "reg2":
                        Mediator.log(debug_string+" Server wants to register!",true);
                        //A server wants to register/reregister. We put the socket in the socket map so we can use it later.
                        //Check version compatibility
                        String version = in.readLine().replaceAll("\\p{C}", "");
                        Mediator.log(debug_string+" Version: "+version,true);
                        if (!(Mediator.versionCompare(version,Mediator.getUdphpMin()) >= 0)) {
                            //For now just silently end the connection.
                            //Proper error messages will follow in the next release
                            Mediator.log(debug_string+" Server not accepted. Version too old.",true);
                            client.close();
                            return;
                        }
                        Mediator.log(debug_string+" Server registered!",false);

                        serverObj = this.main.getServer(this.client.getInetAddress().getHostAddress());
                        this.isServer = true;
                        serverObj.setTCPsocket(this.client);
                        //Write the 8 data strings
                        serverObj.setData1(in.readLine().replaceAll("\\p{C}", ""));
                        Mediator.log(debug_string+" Data 1: "+serverObj.getData1(),true);
                        serverObj.setData2(in.readLine().replaceAll("\\p{C}", ""));
                        Mediator.log(debug_string+" Data 2: "+serverObj.getData2(),true);
                        serverObj.setData3(in.readLine().replaceAll("\\p{C}", ""));
                        Mediator.log(debug_string+" Data 3: "+serverObj.getData3(),true);
                        serverObj.setData4(in.readLine().replaceAll("\\p{C}", ""));
                        Mediator.log(debug_string+" Data 4: "+serverObj.getData4(),true);
                        serverObj.setData5(in.readLine().replaceAll("\\p{C}", ""));
                        Mediator.log(debug_string+" Data 5: "+serverObj.getData5(),true);
                        serverObj.setData6(in.readLine().replaceAll("\\p{C}", ""));
                        Mediator.log(debug_string+" Data 6: "+serverObj.getData6(),true);
                        serverObj.setData7(in.readLine().replaceAll("\\p{C}", ""));
                        Mediator.log(debug_string+" Data 7: "+serverObj.getData7(),true);
                        serverObj.setData8(in.readLine().replaceAll("\\p{C}", ""));
                        Mediator.log(debug_string+" Data 8: "+serverObj.getData8(),true);
                    break;
                    case "connect":
                        //A client wants to connect. Now the interesting part begins
                        //Wait for next line that contains the requested IP adress.
                        String requested_server = in.readLine().replaceAll("\\p{C}", "");
                        String debug_string2 = debug_string + " Client <-> "+requested_server+" ->";
                        Mediator.log(debug_string2+" Connecting...",false);
                        if (this.main.getServerMap().containsKey(requested_server)) {
                            //SERVER FOUND
                            serverObj = this.main.getServer(requested_server);
                            //get server connection socket from the map (stored above)
                            Socket gameserver = serverObj.getTCPsocket();
                            if (!gameserver.isClosed()) {
                                String connect_to_server = requested_server;
                                //Get server port
                                int connect_to_port = serverObj.getPort();
                                //Send server port to client
                                Mediator.log(debug_string2+" Found server",true);
                                Mediator.log(debug_string2+" Send server port "+connect_to_port+" to client",true);
                                out.write((byte) 255);
                                out.write((connect_to_server+this.gm_string_seperator).getBytes());
                                out.write((String.valueOf(connect_to_port)+this.gm_string_seperator).getBytes());
                                //Send buffer to client
                                out.flush();
                                //Get client port
                                Client clientObj = this.main.getClient(this.client.getInetAddress().getHostAddress());
                                int connect_to_port_server = clientObj.getPort();
                                Mediator.log(debug_string2+" Send client port "+connect_to_port_server+" to server",true);
                                //Get an output stream for the server socket. We will contact the server with this.
                                OutputStream out_server = gameserver.getOutputStream();
                                out_server.write((byte) 255);
                                out_server.write((this.client.getInetAddress().getHostAddress()+this.gm_string_seperator).getBytes());
                                out_server.write(String.valueOf(connect_to_port_server+this.gm_string_seperator).getBytes());
                                //Send buffer to server
                                out_server.flush();
                                //We are done! Client and Server now connect to each other and the hole is punched!
                                Mediator.log(debug_string2+" CONNECTED!",false);
                            } else {
                                //SERVER FOUND BUT SOCKET IS DEAD
                                Mediator.log(debug_string+" CONNECTION FAILED - Server not reachable",false);
                                out.write((byte) 254);
                                out.flush();
                            }   
                        } else {
                            //SERVER NOT FOUND
                            Mediator.log(debug_string+" CONECTION FAILED - Server not found",false);
                            out.write((byte) 254);
                            out.flush();
                        }
                        this.main.destroyClient(this.client.getInetAddress().getHostAddress());
                    break;
                    case "lobby2":
                        if (Mediator.isLobby() || Mediator.isTesting()) {
                            Mediator.log(debug_string+" Sending lobby based on requested filters",true);

                            HashMap<String, Server> servers =  new HashMap<String, Server>(main.getServerMap());

                            String filter_data1 = in.readLine().replaceAll("\\p{C}", "");
                            String filter_data2 = in.readLine().replaceAll("\\p{C}", "");
                            String filter_data3 = in.readLine().replaceAll("\\p{C}", "");
                            String filter_data4 = in.readLine().replaceAll("\\p{C}", "");
                            String filter_data5 = in.readLine().replaceAll("\\p{C}", "");
                            String filter_data6 = in.readLine().replaceAll("\\p{C}", "");
                            String filter_data7 = in.readLine().replaceAll("\\p{C}", "");
                            String filter_data8 = in.readLine().replaceAll("\\p{C}", "");
                            final String filter_sortby = in.readLine().replaceAll("\\p{C}", "");
                            final String filter_sortby_dir = in.readLine().replaceAll("\\p{C}", "");
                            String filter_limit = in.readLine().replaceAll("\\p{C}", "");

                            //Skip servers with <INV> gamename (this might happen if a server was created using UDP connection but never initialized via TCP)
                            //TODO: Remove these invalid servers after some time.
                            Iterator<Map.Entry<String, Server>> iterInv = servers.entrySet().iterator();
                            while (iterInv.hasNext()) {
                                Map.Entry<String, Server> entry = iterInv.next();
                                if (entry.getValue().getData1().equals("<INV>")) {
                                    iterInv.remove();
                                }
                            }

                            if (!"".equals(filter_data1)) {
                                Iterator<Map.Entry<String, Server>> iter = servers.entrySet().iterator();
                                while (iter.hasNext()) {
                                    Map.Entry<String, Server> entry = iter.next();
                                    if (!entry.getValue().getData1().equals(filter_data1)) {
                                        iter.remove();
                                    }
                                }
                            }

                            if (!"".equals(filter_data2)) {
                                Iterator<Map.Entry<String, Server>> iter = servers.entrySet().iterator();
                                while (iter.hasNext()) {
                                    Map.Entry<String, Server> entry = iter.next();
                                    if (!entry.getValue().getData2().equals(filter_data2)) {
                                        iter.remove();
                                    }
                                }
                            }

                            if (!"".equals(filter_data3)) {
                                Iterator<Map.Entry<String, Server>> iter = servers.entrySet().iterator();
                                while (iter.hasNext()) {
                                    Map.Entry<String, Server> entry = iter.next();
                                    if (!entry.getValue().getData3().equals(filter_data3)) {
                                        servers.remove(entry.getKey());
                                    }
                                }
                            }

                            if (!"".equals(filter_data4)) {
                                Iterator<Map.Entry<String, Server>> iter = servers.entrySet().iterator();
                                while (iter.hasNext()) {
                                    Map.Entry<String, Server> entry = iter.next();
                                    if (!entry.getValue().getData4().equals(filter_data4)) {
                                        iter.remove();
                                    }
                                }
                            }

                            if (!"".equals(filter_data5)) {
                                Iterator<Map.Entry<String, Server>> iter = servers.entrySet().iterator();
                                while (iter.hasNext()) {
                                    Map.Entry<String, Server> entry = iter.next();
                                    if (!entry.getValue().getData5().equals(filter_data5)) {
                                        iter.remove();
                                    }
                                }
                            }

                            if (!"".equals(filter_data6)) {
                                Iterator<Map.Entry<String, Server>> iter = servers.entrySet().iterator();
                                while (iter.hasNext()) {
                                    Map.Entry<String, Server> entry = iter.next();
                                    if (!entry.getValue().getData6().equals(filter_data6)) {
                                        iter.remove();
                                    }
                                }
                            }

                            if (!"".equals(filter_data7)) {
                                Iterator<Map.Entry<String, Server>> iter = servers.entrySet().iterator();
                                while (iter.hasNext()) {
                                    Map.Entry<String, Server> entry = iter.next();
                                    if (!entry.getValue().getData7().equals(filter_data7)) {
                                        iter.remove();
                                    }
                                }
                            }

                            if (!"".equals(filter_data8)) {
                                Iterator<Map.Entry<String, Server>> iter = servers.entrySet().iterator();
                                while (iter.hasNext()) {
                                    Map.Entry<String, Server> entry = iter.next();
                                    if (!entry.getValue().getData8().equals(filter_data8)) {
                                        iter.remove();
                                    }
                                }
                            }

                            Server[] arr = servers.values().toArray(new Server[servers.values().size()]);
                            Arrays.sort(arr, new Comparator<Server>() {
                                @Override
                                public int compare(Server o1, Server o2) {
                                    int mp = 1;
                                    int rt = 0;
                                    if ("ASC".equals(filter_sortby_dir)) {
                                        mp = -1;
                                    }
                                    switch (filter_sortby) {
                                        default:
                                        case "date":
                                            rt = new Long(o1.getCreatedTime()).compareTo(o2.getCreatedTime()) * mp;
                                        break;
                                        case "data1":
                                            rt = o1.getData1().compareTo(o2.getData1()) * mp;
                                        break;
                                        case "data2":
                                            rt = o1.getData2().compareTo(o2.getData2()) * mp;
                                        break;
                                        case "data3":
                                            rt = o1.getData3().compareTo(o2.getData3()) * mp;
                                        break;
                                        case "data4":
                                            rt = o1.getData4().compareTo(o2.getData4()) * mp;
                                        break;
                                        case "data5":
                                            rt = o1.getData5().compareTo(o2.getData5()) * mp;
                                        break;
                                        case "data6":
                                            rt = o1.getData6().compareTo(o2.getData6()) * mp;
                                        break;
                                        case "data7":
                                            rt = o1.getData7().compareTo(o2.getData7()) * mp;
                                        break;
                                        case "data8":
                                            rt = o1.getData8().compareTo(o2.getData8()) * mp;
                                        break;
                                    }
                                    return rt;
                                }
                            });

                            if (!"".equals(filter_limit) && Integer.valueOf(filter_limit) <= arr.length) {
                                arr = Arrays.copyOfRange(arr, 0, Integer.valueOf(filter_limit));
                            }

                            Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
                            String json = gson.toJson(arr);

                            out.write((byte) 249);
                            out.write(json.getBytes());
                            out.write(10);
                            //Send buffer to server
                            out.flush();
                        }
                    break;
                    case "istesting":
                        out.write((byte) 248);
                        if (Mediator.isTesting()) {
                            Mediator.log(debug_string+" Sending if testing is enabled",true);
                            out.write((byte) 1);
                        } else {
                            out.write((byte) 0);
                        }
                        //Send buffer
                        out.flush();
                    break;
                    case "testinginfos":
                        out.write((byte) 247);
                        if (Mediator.isTesting()) {
                            Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
                            out.write(Mediator.getName().getBytes());
                            out.write(10);
                            out.write(Mediator.getVersion().getBytes());
                            out.write(10);
                            out.write(Mediator.getUdphpMin().getBytes());
                            out.write(10);
                            Mediator.log(debug_string+" Sending testing information",true);
                        } else {
                            out.write((byte) 0);
                        }
                        //Send buffer
                        out.flush();
                    break;
                    case "version":
                        out.write((byte) 246);
                        out.write(Mediator.getVersion().getBytes());
                        out.write(10);
                        Mediator.log(debug_string+" Sending version information",true);
                        //Send buffer
                        out.flush();
                    break;
                    default:
                        //Ignore unknown commands (client disconnection will cause an unknown command)
                    break;
                }
                //Disable timout again and wait for next command
                client.setSoTimeout(0);
            }
            client.close();
            Mediator.log(debug_string+" Disconnected!",true);
            //Cleanup, when they loose TCP connection, this data can't be used anymore, so it's safe to remove
            if (this.isServer) {
                this.main.destroyServer(this.client.getInetAddress().getHostAddress());
                Mediator.log(debug_string+" Server deleted!",false);
            }
        } catch (Exception ex) {
            Mediator.log(debug_string+" Disconnected (e: "+ex.getClass().getName()+")",true);
            //Cleanup, when they loose TCP connection, this data can't be used anymore, so it's safe to remove
            if (this.isServer) {
                this.main.destroyServer(this.client.getInetAddress().getHostAddress());
                Mediator.log(debug_string+" Server deleted!",false);
            }
        }
    }
}

1 个答案:

答案 0 :(得分:0)

没有错的端口。我的提供商在我背后更新了NAT。他们使它更“安全”。我的NAT不再可能使用UDP Punch了,因为它将打卡视为攻击并阻止它。