1

質問: ポートが間違っていますか (UDP パンチを作成するため)?

クライアントとサーバーの接続からIPとポートを保存するJavaサーバーを入手しました。IP とポートがクライアントとサーバーに送信されるため、クライアントとサーバーは UDP ホール パンチを開始できます。同じNATに接続された2台のコンピューターを使用しています。しかし、それは働くことを拒否します。

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にあります。

サーバー.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);
            }
        }
    }
}
4

1 に答える 1