[java] mutliplayer via P2P

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Gamebuster
  • Registratie: Juli 2007
  • Laatst online: 01-08 10:05
Ik ben bezig met een simpel spelletje in java en wil daarin een multiplayer feature verwerken.

Dit zou ik simpelweg kunnen uitwerken via een client/server structuur, maar daarvoor moeten er gameservers draaien. Een andere mogelijkheid is P2P, waarbij er slechts een heel klein centraal punt hoeft te zijn die de Peers met elkaar in verbinding brengt.

Het probleem is echter: hoe werkt zoiets? Ik dacht zelf eraan om gewoon een random iemand als server aan te wijzen, maar als men achter een firewall of router zit moeten er poorten open gezet/ge-forward worden. Nou weet ik dat MW2 met een vergelijkbaar P2P systeem werkt, aangezien er geen dedicated gameservers zijn.

Voor MW2 hoef je enkel je poorten niet open te zetten of te laten forwarden. Ik weet dat dat iets te maken heeft met het feit dat je UPnP wel aan heb moeten staan om je game als "server" te laten functioneren, maar als ik informatie over UPnP zoek, is de informatie verwarrend en zie ik geen link tussen P2P netwerken en UPnP.

Ook bij P2P software als uTorrent is het geen eis dat je je poorten openzet om te kunnen downloaden en uploaden naar anderen.

Mijn vraag is dus:
Hoe kan ik het beste uitwerken dat willekeurige spelers aangewezen worden als "server" zonder dat men poorten open hoeft te zetten?

Zover ben ik bezig het wikipedia-artikel over P2P aan het lezen:
> Wikipedia: Universal Plug and Play

[ Voor 6% gewijzigd door Gamebuster op 25-03-2010 00:38 ]

Let op: Mijn post bevat meningen, aannames of onwaarheden


Acties:
  • 0 Henk 'm!

  • RayNbow
  • Registratie: Maart 2003
  • Laatst online: 09:17

RayNbow

Kirika <3

Gamebuster schreef op donderdag 25 maart 2010 @ 00:34:
Ook bij P2P software als uTorrent is het geen eis dat je je poorten openzet om te kunnen downloaden en uploaden naar anderen.
Dat komt omdat zodra je een uitgaande verbinding hebt gemaakt met iemand anders [small](die iemand anders moet dus wel een verbindbare port hebben, hetzij handmatig opengezet, hetzij via UPnP)[small], er verkeer mogelijk is tussen jullie beiden.
Mijn vraag is dus:
Hoe kan ik het beste uitwerken dat willekeurige spelers aangewezen worden als "server" zonder dat men poorten open hoeft te zetten?
Er moet iig altijd 1 speler zijn die een port heeft opengezet. Dit kan handmatig zijn gedaan of via UPnP door het spel. Om te bepalen wie er server wordt moet je eerst weten wie er überhaupt server kan worden.

Een mogelijke oplossing is als volgt. De server heeft spelers A, B en C in een party gestopt en stuurt ze elk de volgende informatie:
Party 1:
A: 1.1.1.1:8080
B: 2.2.2.2:3124
C: 3.3.3.3:40123

Speler A controleert of hij kan verbinden met B en C, B controleert of hij kan verbinden met A en C, en C controleert of hij kan verbinden met A en B. Deze informatie sturen A, B en C terug naar de server, bijv.:
Van: A
B: Verbindbaar
C: Niet verbindbaar
-------------------------
Van: B
A: Niet verbindbaar
C: Niet verbindbaar
-------------------------
Van: C
A: Verbindbaar
B: Verbindbaar

De server kan met deze informatie (door bijv. een gerichte graaf te construeren) bepalen welke spelers geschikt zijn als host. De server kiest er een en vertelt A, B en C wie de host is.

Ipsa Scientia Potestas Est
NNID: ShinNoNoir


Acties:
  • 0 Henk 'm!

  • Gamebuster
  • Registratie: Juli 2007
  • Laatst online: 01-08 10:05
Owke; als ik het goed begrijp kan je dus via een UPnP kit poorten tijdelijk openzetten voor je applicatie, waarna de server die poort kan gebruiken. Bedankt voor de info.

Ik zal even uitzoeken hoe die UPnP development kit werkt.
Deze ga ik proberen: http://www.cybergarage.or...iew/Main/CyberLinkForJava

edit
sjonge jonge, waarom moet het altijd ingewikkeld zijn :P
Waarom is er niet gewoon 1 "createServerSocket" functie waarbij-ie een server-socket aanmaakt op een random door-UPnP-opengegooide poort, waarna ik de poort kan opvragen en kan terugsturen naar de "hoofdserver", welke het stuurt naar de andere clients.

[ Voor 62% gewijzigd door Gamebuster op 25-03-2010 02:57 ]

Let op: Mijn post bevat meningen, aannames of onwaarheden


Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Kijk eens naar NAT Hole Punching

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • Gamebuster
  • Registratie: Juli 2007
  • Laatst online: 01-08 10:05
Ik heb het een paar keer gelezen maar wordt er weinig wijzer van. Begrijp ik goed dat de er gewoon "gescant" wordt naar een open poort welke gebruikt kan worden?

Let op: Mijn post bevat meningen, aannames of onwaarheden


Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Gamebuster schreef op donderdag 25 maart 2010 @ 02:42:
[...]

Ik heb het een paar keer gelezen maar wordt er weinig wijzer van. Begrijp ik goed dat de er gewoon "gescant" wordt naar een open poort welke gebruikt kan worden?
Nee, dat begrijp je verkeerd. Heeeeel kort: Als je A met B wil verbinden, beiden achter een firewall, dan moet je een derde partij C hebben welke A en B gebruiken om wat afspraken te maken onderling. Nadat ze deze afspraken gemaakt hebben middels C gaat de communicatie rechtstreeks van A naar B nadat ze beiden wat zaken hebben geregeld zodat B bij A naar binnen kan en A bij B.

Het is eigenlijk best simpel. Een wat uitgebreider verhaal zoals Skype 't bijvoorbeeld doet vind je hier.

[ Voor 13% gewijzigd door RobIII op 25-03-2010 03:51 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • Gamebuster
  • Registratie: Juli 2007
  • Laatst online: 01-08 10:05
Dus...

client A: 1.1.1.1:1234
client B: 2.2.2.2:3456

client A en B staan in verbinding met een centrale server (onder een vaste poort) en de server weet het IP + gewenste poort van A en B. Vervolgens stuurt de server de informatie naar beide clients. Client A verzendt een UDP pakket naar 2.2.2.2 via poort 3456, welke nooit aankomt, maar gooit hierbij zijn eigen firewall open om een antwoord terug te krijgen. Client B verzendt een UDP pakket naar 1.1.1.1:1234 en komt opnieuw niet aan, maar maakt opnieuw de firewall open. Vervolgens gaat Client A UDP pakketten uitzenden naar B via 1234, wat aankomt omdat B nog steeds op antwoord wacht, en stuurt client B UDP pakketten naar A via 3456, wat ook aankomt omdat er nog op antwoord gewacht wordt. Gevolg: succesvolle UDP verbinding.

Lekker protocol, dat UDP :P

Nou nog uitvissen hoe UDP werkt in java. Ik heb tot nu toe alleen nog maar met TCP gewerkt.

> http://java.sun.com/docs/...datagrams/definition.html
Bingo :)

offtopic:
Kut.... moet ik nu slapen (6:08) of verder kloten met java....


Ik lees overigens dat er geen garantie is dat de data niet altijd aankomt. Betekend dit dat er zomaar een serie verzonden bytes niet aankomt? Ik heb op dit moment een eigen binair-formaat "geschreven" welke totaal ontspoort bij een gemiste byte. Dit verklaart de zogeheten "lag" in games of rare haperingen in voice-chat :P

Als het zo is dat er bytes overgeslagen kunnen worden, zal ik dus mijn data-stroompje moeten aanpassen met soort-van keyframes die de client kan herkennen tussen een willekeurige stroom aan bytes. Ik kom er later wel achter of er daadwerkelijk bytes overgeslagen worden bij UDP.

Heb voorlopig dus weer leuke scripts te maken :D

[ Voor 30% gewijzigd door Gamebuster op 25-03-2010 06:23 ]

Let op: Mijn post bevat meningen, aannames of onwaarheden


Acties:
  • 0 Henk 'm!

  • Gamebuster
  • Registratie: Juli 2007
  • Laatst online: 01-08 10:05
Zit met een raar probleem: heb het proberen uit te werken en het lijkt nu goed te werken, maar er wordt geen data ontvangen door geen van de clients... Nou is dit mijn 1e keer met UDP, dus ik zal vast iets totaal verkeerd doen.

De server:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import java.net.ServerSocket;
import java.net.Socket;

import java.io.IOException;
import java.io.DataOutputStream;
import java.io.DataInputStream;

/**
 * A sample Server
 * @author tobyhinloopen
 */
public class Main {

    private static String getClientName(Socket client) {
        return client.getInetAddress().getHostAddress()+":"+client.getPort();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        try {
            ServerSocket server = new ServerSocket(22345);
            Socket partner=null;
            while(true) {
                try {
                    System.out.println("Waiting for a client...");
                    final Socket client = server.accept();
                    System.out.println(getClientName(client)+" connected...");
                    if(partner == null) {
                        System.out.println(getClientName(client)+" assigned to A");
                        partner = client;
                    } else {
                        System.out.println(getClientName(client)+" assigned to B");
                        final Socket A = partner;
                        final Socket B = client;
                        new Thread(new Runnable() {
                            public void run() {
                                try {
                                    System.out.println(getClientName(A)+" and "+getClientName(B)+" are meeting each other...");

                                    DataInputStream AIn = new DataInputStream(A.getInputStream());
                                    DataInputStream BIn = new DataInputStream(B.getInputStream());

                                    int APort = AIn.readInt();
                                    int BPort = BIn.readInt();

                                    DataOutputStream AOut = new DataOutputStream(A.getOutputStream());
                                    DataOutputStream BOut = new DataOutputStream(B.getOutputStream());

                                    byte[] AIp = A.getInetAddress().getAddress();
                                    byte[] BIp = B.getInetAddress().getAddress();

                                    byte AIpLength = (byte)AIp.length;
                                    byte BIpLength = (byte)BIp.length;

                                    AOut.writeByte(BIpLength);
                                    BOut.writeByte(AIpLength);

                                    AOut.write(BIp, 0, BIpLength);
                                    BOut.write(AIp, 0, AIpLength);

                                    AOut.writeInt(BPort);
                                    BOut.writeInt(APort);
                                } catch(IOException e) {
                                    e.printStackTrace();
                                } finally {
                                    try{A.close();}catch(IOException e2){}
                                    try{B.close();}catch(IOException e2){}
                                }
                            }
                        }).start();
                        partner = null;
                    }
                } catch(IOException e) {
                    e.printStackTrace();
                }
            }
        } catch(IOException e) {
            e.printStackTrace();
        }
    }
}


De client(s)
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import java.net.Socket;
import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.ServerSocket;

import java.io.IOException;
import java.io.DataInputStream;
import java.io.DataOutputStream;

/**
 * A sample Client
 * @author tobyhinloopen
 */
public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        try {
            String host;
            if(args.length > 0) {
                host = args[0];
            } else {
                host = "localhost";
            }
            
            int port = -1;
            while(true) {
                try {
                    port = (int)(Math.random()*65535);
                    ServerSocket socket = new ServerSocket(port);
                    socket.close();
                    break;
                } catch(Exception e){}
            }
            final int portOut = port;

            System.out.println("Port "+portOut+" is available!");

            //Connect to the server and wait for a partner
            System.out.println("Connecting to "+host);
            Socket server = new Socket(host,22345);

            //Get the OutputStream and write the listening port...
            DataOutputStream out = new DataOutputStream(server.getOutputStream());
            out.writeInt(portOut);

            //Assign a DataInputStream to read the serverdata.
            DataInputStream in = new DataInputStream(server.getInputStream());

            System.out.println("Waiting for a partner...");

            //Read the raw IP bytes.
            byte rawAddressLength = in.readByte();
            byte[] rawAddress = new byte[rawAddressLength];
            for(int readBytes = 0;readBytes<rawAddress.length;) {
                readBytes+=in.read(rawAddress);
            }

            //Create the InetAddress instance based on the IP bytes.
            final InetAddress partnerAddress = InetAddress.getByAddress(rawAddress);

            //Read the port.
            final int portIn = in.readInt();

            System.out.println("Partner found: "+partnerAddress.getHostAddress()+":"+portOut);

            //Close the connection with the server. We don't need it anymore.
            server.close();

            System.out.println("Send data to "+partnerAddress+":"+portOut);
            System.out.println("Listen data from "+partnerAddress+":"+portIn);

            DatagramSocket pincher = new DatagramSocket();
            pincher.send(new DatagramPacket(new byte[256],256,partnerAddress,portIn));
            pincher.close();

            final DatagramSocket socketIn = new DatagramSocket();
            final DatagramSocket socketOut = new DatagramSocket();

            new Thread(new Runnable() {
                public void run() {
                    while(true) {
                        System.out.println("Waiting for input...");
                        try {
                            byte[] buffer = new byte[256];
                            DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
                            socketIn.receive(packet);
                            System.out.println("Input received: "+packet.getData());
                        } catch(IOException e) {
                            e.printStackTrace();
                        }
                        //try{Thread.sleep(500);}catch(InterruptedException e){}
                    }
                }
            }).start();

            new Thread(new Runnable() {
                public void run() {
                    while(true) {
                        System.out.println("Sending output...");
                        try {
                            byte[] buffer = "Hi there :D".getBytes();
                            DatagramPacket packet = new DatagramPacket(buffer, buffer.length, partnerAddress, portOut);
                            socketOut.send(packet);
                        } catch(IOException e) {
                            e.printStackTrace();
                        }
                        try{Thread.sleep(500);}catch(InterruptedException e){}
                    }
                }
            }).start();

        } catch(IOException e) {
            e.printStackTrace();
        }
    }
}


Output:

Client #1:
code:
1
2
3
4
5
6
7
8
9
10
11
Port 24533 is available!
Connecting to 192.168.1.102
Waiting for a partner...
Partner found: 192.168.1.103:24533
Send data to /192.168.1.103:24533
Listen data from /192.168.1.103:32632
Waiting for input...
Sending output...
Sending output...
Sending output...
...


Client #2:
code:
1
2
3
4
5
6
7
8
9
10
11
Port 32632 is available!
Connecting to 192.168.1.102
Waiting for a partner...
Partner found: 192.168.1.102:32632
Send data to /192.168.1.102:32632
Listen data from /192.168.1.102:24533
Waiting for input...
Sending output...
Sending output...
Sending output...
...


Maar ze ontvangen niets van elkaar :'(

[ Voor 16% gewijzigd door Gamebuster op 25-03-2010 09:23 ]

Let op: Mijn post bevat meningen, aannames of onwaarheden


Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
Probeer je output eens te flushen, wil nog wel 'es helpen.

https://niels.nu


Acties:
  • 0 Henk 'm!

  • Domdo
  • Registratie: Juni 2009
  • Laatst online: 30-06 20:29
Voor zover ik kan zien gebruik je TCP en UDP doorelkaar.

ServerSocket en Socket wordt gebruikt voor TCP-verkeer.
DatagramSocket wordt gebruikt voor UDP-verkeer waar je alleen state-less verbindingen mee kunt opzetten (dus verbinden, datagrampacket versturen en verbinding is weer weg).

Check http://java.sun.com/docs/...tagrams/clientServer.html eens voor wat informatie.

Acties:
  • 0 Henk 'm!

  • Gamebuster
  • Registratie: Juli 2007
  • Laatst online: 01-08 10:05
Domdo schreef op donderdag 25 maart 2010 @ 12:35:
Voor zover ik kan zien gebruik je TCP en UDP doorelkaar.

ServerSocket en Socket wordt gebruikt voor TCP-verkeer.
DatagramSocket wordt gebruikt voor UDP-verkeer waar je alleen state-less verbindingen mee kunt opzetten (dus verbinden, datagrampacket versturen en verbinding is weer weg).

Check http://java.sun.com/docs/...tagrams/clientServer.html eens voor wat informatie.
DatagramSocket wordt gebruikt voor UDP-verkeer waar je alleen state-less verbindingen mee kunt opzetten (dus verbinden, datagrampacket versturen en verbinding is weer weg).
Oew... dat lijkt me niet zo'n strak plan voor een multiplayer spel :P

ServerSocket en Socket wordt gebruikt voor TCP-verkeer.
I know. Er wordt via TCP verbonden met de server, welke de 2 clients met elkaar in verbinding brengt via UDP.

Let op: Mijn post bevat meningen, aannames of onwaarheden


Acties:
  • 0 Henk 'm!

  • Gamebuster
  • Registratie: Juli 2007
  • Laatst online: 01-08 10:05
Hydra schreef op donderdag 25 maart 2010 @ 11:40:
Probeer je output eens te flushen, wil nog wel 'es helpen.
Er wordt gewerkt met packet's, niet met i/o-streams. Er valt niks te flushen.

Let op: Mijn post bevat meningen, aannames of onwaarheden


Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
Gamebuster schreef op donderdag 25 maart 2010 @ 13:25:
Er wordt gewerkt met packet's, niet met i/o-streams. Er valt niks te flushen.
Je hebt gelijk. Heb ooit zelf met DG sockets gewerkt en toen een issue gehad waarvan ik dacht dat het te maken had met 't flushen, maar aangezien ze geen flush methode hebben zal ik me vergist hebben.
Oew... dat lijkt me niet zo'n strak plan voor een multiplayer spel :P
Da's echt geen probleem. Er is uberhaupt geen sprake van een 'verbinding'. UDP is gewoon en dunne wrapper om het onderliggende IP. Je stuurt gewoon een pakketje naar een bepaald IP adres en een bepaalde poort, en als daar niet op geluisterd wordt komt het gewoon niet aan (weet ff niet uit m'n hoofd of je daar wat op terug hoort, volgens mij niet nl.). Veel shooters waar latency belangrijker is dan af en toe packetloss gebruiken juist UDP omdat het minder overhead heeft dan TCP/IP en bovendien als dataverlies geen issue is minder last hebben van packetloss.

https://niels.nu


Acties:
  • 0 Henk 'm!

  • Gamebuster
  • Registratie: Juli 2007
  • Laatst online: 01-08 10:05
Hydra schreef op donderdag 25 maart 2010 @ 13:36:
[...]


Je hebt gelijk. Heb ooit zelf met DG sockets gewerkt en toen een issue gehad waarvan ik dacht dat het te maken had met 't flushen, maar aangezien ze geen flush methode hebben zal ik me vergist hebben.


[...]


Da's echt geen probleem. Er is uberhaupt geen sprake van een 'verbinding'. UDP is gewoon en dunne wrapper om het onderliggende IP. Je stuurt gewoon een pakketje naar een bepaald IP adres en een bepaalde poort, en als daar niet op geluisterd wordt komt het gewoon niet aan (weet ff niet uit m'n hoofd of je daar wat op terug hoort, volgens mij niet nl.). Veel shooters waar latency belangrijker is dan af en toe packetloss gebruiken juist UDP omdat het minder overhead heeft dan TCP/IP en bovendien als dataverlies geen issue is minder last hebben van packetloss.
Owke :)

Maar ik weet nog steeds niet wat ik fout doe :'(

Geen java programmeurs hier met vergelijkbare ervaringen?

Let op: Mijn post bevat meningen, aannames of onwaarheden


Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
Gamebuster schreef op donderdag 25 maart 2010 @ 13:42:
Maar ik weet nog steeds niet wat ik fout doe :'(

Geen java programmeurs hier met vergelijkbare ervaringen?
Ik zie nergens de code die je nodig hebt om de sockets te binden op een bepaald IP en poortnummer bij het luisteren. Het is eeuwen geleden dat ik hier iets mee gedaan heb (ook voor een spelletje) maar volgens mij zul je echt ergens een ontvangende socket aan een adres en poort moeten binden.

https://niels.nu


Acties:
  • 0 Henk 'm!

  • Gamebuster
  • Registratie: Juli 2007
  • Laatst online: 01-08 10:05
Hydra schreef op donderdag 25 maart 2010 @ 13:48:
[...]


Ik zie nergens de code die je nodig hebt om de sockets te binden op een bepaald IP en poortnummer bij het luisteren. Het is eeuwen geleden dat ik hier iets mee gedaan heb (ook voor een spelletje) maar volgens mij zul je echt ergens een ontvangende socket aan een adres en poort moeten binden.
Dat is ook erg onduidelijk bij de documentatie van java bij de Datagram classes. Zover ik heb begrepen moet je poort en adres toewijzen aan je DatagramPacket en niet aan je DatagramSocket, en dat heb ik gedaan. (zie 2e Thread#run():void)

Het adres en poort wordt opgehaald via de Socket welke via TCP verbinding maakt met de server.

> Server wordt gestart en wacht op clients
> Client A maakt verbinding met server en wacht op reactie van de server
> Client B maakt verbinding met server
> Server stuurt gegevens over B naar A
> Server stuurt gegevens over A naar B
> A en B sturen nu een dummy-packet weg over de poort waarmee de andere client uitzendt, zodat de firewall open gaat.
> Een thread wordt gestart in zowel client A als B waarbij geluisterd wordt naar de poort die toegewezen is door de andere client en doorgestuurd is via de server.
> Een 2e thread worden gestart in zowel client A als B welke iedere 500ms een packet wegstuurt op een zelf-gegeneerde poort waar de andere client naar luistert.

De berichten die verzonden worden door de 2e thread worden nooit ontvangen door de andere client; de 1e thread blijft eeuwig wachten op een packet; nooit komt er een packet binnen ondanks dat deze iedere 500ms verzonden wordt.

[ Voor 37% gewijzigd door Gamebuster op 25-03-2010 13:58 ]

Let op: Mijn post bevat meningen, aannames of onwaarheden


Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
Gamebuster schreef op donderdag 25 maart 2010 @ 13:53:
Dat is ook erg onduidelijk bij de documentatie van java bij de Datagram classes. Zover ik heb begrepen moet je poort en adres toewijzen aan je DatagramPacket en niet aan je DatagramSocket, en dat heb ik gedaan. (zie 2e Thread#run():void)

Het adres en poort wordt opgehaald via de Socket welke via TCP verbinding maakt met de server.
Daar is niks onduidelijks aan. Java docs:
Example: DatagramSocket s = new DatagramSocket(null); s.bind(new InetSocketAddress(8888)); Which is equivalent to: DatagramSocket s = new DatagramSocket(8888); Both cases will create a DatagramSocket able to receive broadcasts on UDP port 8888.
Daar staat toch echt dat als je wilt luisteren op een adres je 'em moet binden op een bepaalde poort door OF bind() aan te roepen OF het via de constructor mee te geven. Jij doet geen van beiden. Het luisteren en versturen van packets zijn gewoon 2 verschillende dingen. Een socket moet je eigenlijk altijd laten luisteren op een poort.

Edit n.a.v. je edit: de rest van je verhaal is leuk en aardig maar ga eerst die poort maar eens binden.

[ Voor 4% gewijzigd door Hydra op 25-03-2010 14:00 ]

https://niels.nu


Acties:
  • 0 Henk 'm!

  • Gamebuster
  • Registratie: Juli 2007
  • Laatst online: 01-08 10:05
Hydra schreef op donderdag 25 maart 2010 @ 13:59:
[...]


Daar is niks onduidelijks aan. Java docs:

[...]


Daar staat toch echt dat als je wilt luisteren op een adres je 'em moet binden op een bepaalde poort door OF bind() aan te roepen OF het via de constructor mee te geven. Jij doet geen van beiden. Het luisteren en versturen van packets zijn gewoon 2 verschillende dingen. Een socket moet je eigenlijk altijd laten luisteren op een poort.

Edit n.a.v. je edit: de rest van je verhaal is leuk en aardig maar ga eerst die poort maar eens binden.
code:
1
2
3
4
5
6
7
8
9
10
11
12
Waiting for input...
Sending output...
Input received: [B@17dfafd1
Waiting for input...
Sending output...
Input received: [B@17dfafd1
Waiting for input...
Sending output...
Input received: [B@5e8fce95
Waiting for input...
Sending output...
Input received: [B@3343c8b3


Het werkt... 8)7
Ik ben weer lekker wakker, maarja, nachten overslaan wordt je ook niet helderder van :P

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import java.net.Socket;
import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.ServerSocket;

import java.io.IOException;
import java.io.DataInputStream;
import java.io.DataOutputStream;

/**
 * A sample Client
 * @author tobyhinloopen
 */
public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        try {
            String host;
            if(args.length > 0) {
                host = args[0];
            } else {
                throw new IOException("Usage: java Main <hostname>");
            }
            
            int port = -1;
            while(true) {
                try {
                    port = (int)(Math.random()*65535);
                    ServerSocket socket = new ServerSocket(port);
                    socket.close();
                    break;
                } catch(Exception e){}
            }
            final int portOut = port;

            System.out.println("Port "+portOut+" is available!");

            //Connect to the server and wait for a partner
            System.out.println("Connecting to "+host);
            Socket server = new Socket(host,22345);

            //Get the OutputStream and write the listening port...
            DataOutputStream out = new DataOutputStream(server.getOutputStream());
            out.writeInt(portOut);

            //Assign a DataInputStream to read the serverdata.
            DataInputStream in = new DataInputStream(server.getInputStream());

            System.out.println("Waiting for a partner...");

            //Read the raw IP bytes.
            byte rawAddressLength = in.readByte();
            byte[] rawAddress = new byte[rawAddressLength];
            for(int readBytes = 0;readBytes<rawAddress.length;) {
                readBytes+=in.read(rawAddress);
            }

            //Create the InetAddress instance based on the IP bytes.
            final InetAddress partnerAddress = InetAddress.getByAddress(rawAddress);

            //Read the port.
            final int portIn = in.readInt();

            System.out.println("Partner found: "+partnerAddress.getHostAddress()+":"+portOut);

            //Close the connection with the server. We don't need it anymore.
            server.close();

            System.out.println("Send data to "+partnerAddress+":"+portOut);
            System.out.println("Listen data from "+partnerAddress+":"+portIn);

            DatagramSocket pincher = new DatagramSocket();
            pincher.send(new DatagramPacket(new byte[256],256,partnerAddress,portIn));
            pincher.close();

            final DatagramSocket socketIn = new DatagramSocket(portIn);
            final DatagramSocket socketOut = new DatagramSocket();

            new Thread(new Runnable() {
                public void run() {
                    while(true) {
                        System.out.println("Waiting for input...");
                        try {
                            byte[] buffer = new byte[256];
                            DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
                            socketIn.receive(packet);
                            System.out.println("Input received: "+packet.getData());
                        } catch(IOException e) {
                            e.printStackTrace();
                        }
                        //try{Thread.sleep(500);}catch(InterruptedException e){}
                    }
                }
            }).start();

            new Thread(new Runnable() {
                public void run() {
                    while(true) {
                        System.out.println("Sending output...");
                        try {
                            byte[] buffer = "Hi there :D".getBytes();
                            DatagramPacket packet = new DatagramPacket(buffer, buffer.length, partnerAddress, portOut);
                            socketOut.send(packet);
                        } catch(IOException e) {
                            e.printStackTrace();
                        }
                        try{Thread.sleep(500);}catch(InterruptedException e){}
                    }
                }
            }).start();

        } catch(IOException e) {
            e.printStackTrace();
        }
    }
}


Enkel de poort toegevoegd op regel 80 8)7

Maarja, bedankt dat je volhield :P
Ik blij :D

Let op: Mijn post bevat meningen, aannames of onwaarheden


Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
Dit werkt:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
    public static void main(String[] args) {
        try
        {
            final DatagramSocket socketIn = new DatagramSocket(8888);
            final DatagramSocket socketOut = new DatagramSocket();
    
            new Thread(new Runnable() {
                public void run() {
                    while(true) {
                        System.out.println("Waiting for input...");
                        try {
                            byte[] buffer = new byte[256];
                            DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
                            socketIn.receive(packet);
                            System.out.println("Input received: "+ new String(packet.getData()));
                        } catch(IOException e) {
                            e.printStackTrace();
                        }
                        //try{Thread.sleep(500);}catch(InterruptedException e){}
                    }
                }
            }).start();
    
            new Thread(new Runnable() {
                public void run() {
                    while(true) {
                        System.out.println("Sending output...");
                        try {
                            byte[] buffer = "Hi there :D".getBytes();
                            DatagramPacket packet = new DatagramPacket(buffer, buffer.length, InetAddress.getLocalHost(), 8888);
                            socketOut.send(packet);
                        } catch(IOException e) {
                            e.printStackTrace();
                        }
                        try{Thread.sleep(2000);}catch(InterruptedException e){}
                    }
                }
            }).start(); 
            
            }
        catch(Exception e)
        {
            e.printStackTrace();
        }

    }


Edit: Damn, goeie timing :)

https://niels.nu


Acties:
  • 0 Henk 'm!

  • Gamebuster
  • Registratie: Juli 2007
  • Laatst online: 01-08 10:05
lol, jij had er ook al new String(inputbytjes) van gemaakt :P

edit: ah, dat heb ik nog niet in mijn versie gedaan die ik hier gepost heb :P

precies dezelfde fixes :P

Ik vergeet echt altijd bytes terug te zetten in een String.

[ Voor 74% gewijzigd door Gamebuster op 25-03-2010 14:19 ]

Let op: Mijn post bevat meningen, aannames of onwaarheden


Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
Gamebuster schreef op donderdag 25 maart 2010 @ 14:16:
lol, jij had er ook al new String(inputbytjes) van gemaakt :P

precies dezelfde fixes :P
Ja ach, dat is het eerste dat opvalt, dat je objectreferenties naar een bytearray aan 't afdrukken bent :P

Wat betreft die messages: dit soort games die via UPD werken sturen meestal eerst een messagenummer mee (32-bit uint) en daarna een payload (een string als die je meegeeft laat je bijvoorbeeld voorafgaan door de lengte van de string). Aan de hand van de messagenummers kun je zien of je geen 'oud' pakketje binnengekregen hebt. Zo werkt quake ook bijvoorbeeld, als een pakket met een objectupdate (raket of speler) binnenkomt dat ouder is dan een vorige update wordt deze gewoon weggegooid. Sommige games maken ook gebruik van zowel TCP als UDP. TCP wordt dan gebruikt voor info die moet aankomen, en UDP voor objectupdates waar er heel veel van zijn en het niet uitmaakt dat er af en toe een wegvalt.

https://niels.nu


Acties:
  • 0 Henk 'm!

  • Gamebuster
  • Registratie: Juli 2007
  • Laatst online: 01-08 10:05
Hydra schreef op donderdag 25 maart 2010 @ 14:21:
[...]


Ja ach, dat is het eerste dat opvalt, dat je objectreferenties naar een bytearray aan 't afdrukken bent :P

Wat betreft die messages: dit soort games die via UPD werken sturen meestal eerst een messagenummer mee (32-bit uint) en daarna een payload (een string als die je meegeeft laat je bijvoorbeeld voorafgaan door de lengte van de string). Aan de hand van de messagenummers kun je zien of je geen 'oud' pakketje binnengekregen hebt. Zo werkt quake ook bijvoorbeeld, als een pakket met een objectupdate (raket of speler) binnenkomt dat ouder is dan een vorige update wordt deze gewoon weggegooid. Sommige games maken ook gebruik van zowel TCP als UDP. TCP wordt dan gebruikt voor info die moet aankomen, en UDP voor objectupdates waar er heel veel van zijn en het niet uitmaakt dat er af en toe een wegvalt.
Maar als ik het goed begrijp komen pakketten wel altijd correct aan, ALS ze aankomen. Ze kunnen in verkeerde volgorde aankomen, ze kunnen totaal niet aankomen, maar het gebeurt niet dat er halve pakketten aankomen waarbij bytes ontbreken of zelfs corrupt zijn o.i.d.?

Zo niet, dan is dat nog "makkelijker" dan de TCP manier met een I/O stream. Pakketten laten beginnen met een byte represent voor het pakket-type (positie speler, positie entities, scores etc), een (40bit (1 byte extra)) unieke index en daarna de data erin gooien.

Maar dan heb ik wel voor niks mijn mooie channelled-outputstream geschreven welke meerdere outputstreams over 1 stream kan schrijven, waarna de data weer uit elkaar gehaald kan worden met een channelled-inputstream. :P

[ Voor 17% gewijzigd door Gamebuster op 25-03-2010 14:40 ]

Let op: Mijn post bevat meningen, aannames of onwaarheden


Acties:
  • 0 Henk 'm!

  • Herko_ter_Horst
  • Registratie: November 2002
  • Niet online
Waar je altijd mee uit moet kijken als je zelf een protocol bovenop UDP aan het bouwen bent, is dat het geen "foute" versie van TCP wordt. Dan kun je namelijk beter gewoon TCP gebruiken...

Dus pakketjes out of sequence is zo'n beetje het enige dat je aan foutcorrectie "mag" doen.

PS: als je code weg gaat gooien, is dat meestal een goed teken :)

"Any sufficiently advanced technology is indistinguishable from magic."


Acties:
  • 0 Henk 'm!

  • Gamebuster
  • Registratie: Juli 2007
  • Laatst online: 01-08 10:05
Herko_ter_Horst schreef op donderdag 25 maart 2010 @ 14:50:
Waar je altijd mee uit moet kijken als je zelf een protocol bovenop UDP aan het bouwen bent, is dat het geen "foute" versie van TCP wordt. Dan kun je namelijk beter gewoon TCP gebruiken...

Dus pakketjes out of sequence is zo'n beetje het enige dat je aan foutcorrectie "mag" doen.

PS: als je code weg gaat gooien, is dat meestal een goed teken :)
Owke :)

Maarre... nu heb ik dus een UDP connectie werkende, maar dat zogeheten hole pinching werkt niet. Zodra ik mijn servertje (62.212.129.157:22345) draait nu, maar 2 externe clients kunnen geen verbinding met elkaar maken door een tussenliggende firewall. Dat moest toch juist voorkomen worden met een firewall?

Als ik zelf 2 clients via extern IP met elkaar laat communiceren komt er geen data aan. Als ik het doe over lokaal netwerk wel. Het hole-pinching princiepe werkt hier dus niet:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import java.net.Socket;
import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.ServerSocket;

import java.io.IOException;
import java.io.DataInputStream;
import java.io.DataOutputStream;

/**
 * A sample Client
 * @author tobyhinloopen
 */
public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        try {
            String host;
            if(args.length > 0) {
                host = args[0];
            } else {
                throw new IOException("Usage: java Main <hostname>");
            }
            
            int port = -1;
            while(true) {
                try {
                    port = (int)(Math.random()*65535);
                    ServerSocket socket = new ServerSocket(port);
                    socket.close();
                    break;
                } catch(Exception e){}
            }
            final int portOut = port;

            System.out.println("Port "+portOut+" is available!");

            //Connect to the server and wait for a partner
            System.out.println("Connecting to "+host);
            Socket server = new Socket(host,22345);

            //Get the OutputStream and write the listening port...
            DataOutputStream out = new DataOutputStream(server.getOutputStream());
            out.writeInt(portOut);

            //Assign a DataInputStream to read the serverdata.
            DataInputStream in = new DataInputStream(server.getInputStream());

            System.out.println("Waiting for a partner...");

            //Read the raw IP bytes.
            byte rawAddressLength = in.readByte();
            byte[] rawAddress = new byte[rawAddressLength];
            for(int readBytes = 0;readBytes<rawAddress.length;) {
                readBytes+=in.read(rawAddress);
            }

            //Create the InetAddress instance based on the IP bytes.
            final InetAddress partnerAddress = InetAddress.getByAddress(rawAddress);

            //Read the port.
            final int portIn = in.readInt();

            System.out.println("Partner found: "+partnerAddress.getHostAddress()+":"+portOut);

            //Close the connection with the server. We don't need it anymore.
            server.close();

            System.out.println("Send data to "+partnerAddress+":"+portOut);
            System.out.println("Listen data from "+partnerAddress+":"+portIn);

            final DatagramSocket socketIn = new DatagramSocket(portIn);
            //Send a dummy-package to pinch a hole in the firewall...
            socketIn.send(new DatagramPacket(new byte[256],256,partnerAddress,portIn));
            final DatagramSocket socketOut = new DatagramSocket();

            new Thread(new Runnable() {
                public void run() {
                    while(true) {
                        System.out.println("Waiting for input...");
                        try {
                            byte[] buffer = new byte[256];
                            DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
                            socketIn.receive(packet);
                            System.out.println("Input received: "+new String(packet.getData()));
                        } catch(IOException e) {
                            e.printStackTrace();
                        }
                        //try{Thread.sleep(500);}catch(InterruptedException e){}
                    }
                }
            }).start();

            new Thread(new Runnable() {
                public void run() {
                    while(true) {
                        System.out.println("Sending output...");
                        try {
                            byte[] buffer = ("Hi there, "+partnerAddress+" :D").getBytes();
                            DatagramPacket packet = new DatagramPacket(buffer, buffer.length, partnerAddress, portOut);
                            socketOut.send(packet);
                        } catch(IOException e) {
                            e.printStackTrace();
                        }
                        try{Thread.sleep(500);}catch(InterruptedException e){}
                    }
                }
            }).start();

        } catch(IOException e) {
            e.printStackTrace();
        }
    }
}

uitvoeren: javac Main.java;java Main 62.212.129.157
mijn "gameserver" op dat IP draait op dit moment en is open. :)

Holepinching, wat het oorspronkelijke onderwerp van dit topic was, werkt niet in mijn script...

Let op: Mijn post bevat meningen, aannames of onwaarheden


Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
Gamebuster schreef op donderdag 25 maart 2010 @ 14:36:
Maar als ik het goed begrijp komen pakketten wel altijd correct aan, ALS ze aankomen.
AFAIK doet IP niet zelf aan errorcorrectie, dat laat het aan de onderliggende protocollen over. UPD is eigenlijk gewoon IP met een dun laagje erop. UDP berichten kunnen verdwijnen en ook de volgorde is niet gegarandeerd. Als je dus bijvoorbeeld bericht 1, 2, 3 en 4 verstuurd kunnen ze binnenkomen als 2,4,3 (volgorde verkeerd en 1 is gewoon weg).

Als je in moet gaan bouwen dat bepaalde informatie perse aan moet komen (zoals game start/stop commenda's e.d.) dan kun je beter TCP/IP gebruiken daarvoor. UDP is weer erg geschikt voor informatie die vooral asap beschikbaar moet zijn maar waarvan het niet belangrijk is of het in de juiste volgorde aankomt (of uberhaupt wegvalt).

https://niels.nu


Acties:
  • 0 Henk 'm!

  • Gamebuster
  • Registratie: Juli 2007
  • Laatst online: 01-08 10:05
Hydra schreef op donderdag 25 maart 2010 @ 15:04:
[...]


AFAIK doet IP niet zelf aan errorcorrectie, dat laat het aan de onderliggende protocollen over. UPD is eigenlijk gewoon IP met een dun laagje erop. UDP berichten kunnen verdwijnen en ook de volgorde is niet gegarandeerd. Als je dus bijvoorbeeld bericht 1, 2, 3 en 4 verstuurd kunnen ze binnenkomen als 2,4,3 (volgorde verkeerd en 1 is gewoon weg).

Als je in moet gaan bouwen dat bepaalde informatie perse aan moet komen (zoals game start/stop commenda's e.d.) dan kun je beter TCP/IP gebruiken daarvoor. UDP is weer erg geschikt voor informatie die vooral asap beschikbaar moet zijn maar waarvan het niet belangrijk is of het in de juiste volgorde aankomt (of uberhaupt wegvalt).
Owke; heb ik dat dus goed begrepen :)

Let op: Mijn post bevat meningen, aannames of onwaarheden

Pagina: 1