Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

1

05.05.2013, 17:17

SDL_net Broadcast

Hi,
Ich bin gerade dabei ein hauptsächlich technisch als spielerisch interessantes Projekt zu überarbeiten, welches aus Server und Client besteht, die über ein Netzwerk (UDP-Protokoll) kommunizieren. Da ich bis jetzt auf freie cross-platform Bibliotheken aufgebaut habe, benutze ich in diesem Projekt als Netzwerkbibliothek SDL_net. Im Zuge der Überarbeitung möchte ich eine Servererkennung für den Clienten im lokalen Netzwerk einfügen. Also las ich einige Beispiele für UDP Broadcasting und habe mich für folgende Methode entschieden:
  1. Der Client broadcasted ein Gesuch ins Netzwerk, dass sich alle Server bitte bei ihm melden sollen (mithilfe der im Paket beigefügten IP)
  2. Der Server stellt ein Paket zusammen mit seiner eigenen IP, einem Servernamen (z.B.: "Helco's Server") und einer Versionsnummer, um inkompatible Versionen während der Entwicklung voneinander zu trennen und schickt das ganze an den Client
  3. Dieser kann die Serverinformationen dann dem User anzeigen
Mehr ist im Augenblick für eine Testapplikation nicht nötig sodass ich erst einmal dies getrennt vom eigentlich Projekt austesten wollte.
SDLNet wird wunderbar initialisiert, das Paket (vom Client) wird ohne Fehlermeldung gesendet, aber keines der beiden Programme empfängt je irgendein Paket, selbst als ich das Serverprogramm auf einen anderen Computer (im selben Netzwerk) ausgelagert habe.
Erkennt ihr den Fehler?
common.h

C-/C++-Quelltext

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
#include <iostream>
#include <string>
#include <sstream>
#include <SDL/SDL.h>
#include <SDL/SDL_net.h>

using namespace std;

#ifdef _MSC_VER
#  define PACKED_STRUCT(name) __pragma(pack(push, 1)) struct name __pragma(pack(pop))
#elif defined(__GNUC__)
#  define PACKED_STRUCT(name) struct __attribute__((packed)) name
#endif

#define uint unsigned int
#define ushort unsigned short
#define byte unsigned char

string IPtoStr (unsigned int ip)
{
    unsigned char* it=(unsigned char*)(&ip);
    stringstream s;
    s << (short)(*it) << ".";it++;
    s << (short)(*it) << ".";it++;
    s << (short)(*it) << ".";it++;
    s << (short)(*it);
    return s.str();
}

IPaddress GetLocalBroadcastIP (unsigned short port)
{
    IPaddress ip;
    ip.host=0;
    ip.port=port;
    string hostname=SDLNet_ResolveIP(&ip);
    if (hostname=="") {
        ip.host=INADDR_NONE;
        return ip;
    }
    SDLNet_ResolveHost(&ip,hostname.c_str(),25336);
    return ip;
}

#define PACKET_HEADER 0x43424f52 //ASCII for "ROBC"
#define ROBCO_VERSION 0x0201 //Ver.: 1.2

enum PacketType
{
    PACKET_BROADCAST_QUERY=0,
    PACKET_BROADCAST_SIGNAL=1
};

//__pragma(pack(push, 1)) struct name __pragma(pack(pop))

PACKED_STRUCT(PacketHeader
{
    uint magic;
    byte type;
});

PACKED_STRUCT(PacketBroadcastQuery
{
    uint magic;
    byte type;
    uint ipHost;
    ushort ipPort;
});

PACKED_STRUCT(PacketBroadcastSignal
{
    uint magic;
    byte type;
    uint ipHost;
    ushort ipPort;
    char name[16];
    ushort version;
});

server.cpp

C-/C++-Quelltext

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
#include <Windows.h>
#include <conio.h>
#include "../common.h"

int main (int argc,char* argv[])
{
    string name;
    cout << "Server name:";
    cin >> name;
    if (name.length()>15) {
        name=name.substr(0,15);
        cout << "Warning! Name is too long and will be reduced to \"" << name<< "\"!" << endl;
    }
    if (SDLNet_Init ()<0) {
        cout << SDLNet_GetError () << endl;
        system("pause");
        return -1;
    }
    IPaddress myIP=GetLocalBroadcastIP (25336);
    UDPsocket socket=SDLNet_UDP_Open (25336);
    UDPpacket* packet=SDLNet_AllocPacket (1024);
    PacketHeader* header;
    PacketBroadcastQuery* broadcastQuery;
    PacketBroadcastSignal* broadcastSignal;
    cout << "IP:" << IPtoStr(myIP.host) << endl;
    while (!_kbhit()) {
        if (SDLNet_UDP_Recv(socket,packet)) {
            header=(PacketHeader*)packet->data;
            if (packet->len<6||header->magic!=PACKET_HEADER)
                continue;
            if (header->type==PACKET_BROADCAST_QUERY) {
                if (packet->len!=sizeof(PacketBroadcastQuery))
                    cout << "Received invalid broadcast query packet!" << endl;
                else {
                    broadcastQuery=(PacketBroadcastQuery*)header;
                    cout << "Received broadcast query from: " << IPtoStr(broadcastQuery->ipHost) << ":" << broadcastQuery->ipPort << endl;
                    packet->address.host=broadcastQuery->ipHost;
                    packet->address.port=broadcastQuery->ipPort;
                    broadcastSignal=(PacketBroadcastSignal*)header;
                    broadcastSignal->type=PACKET_BROADCAST_SIGNAL;
                    broadcastSignal->ipHost=myIP.host;
                    broadcastSignal->ipPort=myIP.port;
                    memset(broadcastSignal->name,0,16);
                    memcpy(broadcastSignal->name,&(name[0]),15);
                    broadcastSignal->version=ROBCO_VERSION;
                    packet->len=sizeof(PacketBroadcastSignal);
                    SDLNet_UDP_Send(socket,-1,packet);
                }
            }
            else
                cout << "Received invalid packet:" << short(header->type) << endl;
        }
        Sleep(5);
    }
    SDLNet_FreePacket(packet);
    SDLNet_UDP_Close (socket);
    SDLNet_Quit ();
    system("pause");
    return 0;
}

client.cpp

C-/C++-Quelltext

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
#include <Windows.h>
#include <conio.h>
#include "../common.h"

int main (int argc,char* argv[])
{
    if (SDLNet_Init ()<0) {
        cout << SDLNet_GetError () << endl;
        system("pause");
        return -1;
    }
    IPaddress myIP=GetLocalBroadcastIP(25336);
    //SDLNet_ResolveHost(&myIP,"192.168.100.2",25336);
    UDPsocket socket=SDLNet_UDP_Open (25336);
    UDPpacket* packet=SDLNet_AllocPacket(1024);
    PacketBroadcastQuery* query;
    PacketBroadcastSignal* signal;
    PacketHeader* header;
    query=(PacketBroadcastQuery*)packet->data;
    query->magic=PACKET_HEADER;
    query->type=PACKET_BROADCAST_QUERY;
    query->ipHost=myIP.host;
    query->ipPort=myIP.port;
    packet->address.host=INADDR_BROADCAST;
    packet->address.port=25336;
    packet->len=sizeof(PacketBroadcastQuery);
    system("pause");
    if (SDLNet_UDP_Send(socket,-1,packet)<0) {
        cout << SDLNet_GetError () << endl;
    }
    cout << "Server name     |Version" << endl
         << "----------------+-------" << endl;
    int r;
    while (!_kbhit()) {
        r=SDLNet_UDP_Recv(socket,packet);
        if (r<0)
            cout << SDLNet_GetError () << endl;
        else if (r>0) {
            header=(PacketHeader*)packet->data;
            if (packet->len<6||header->magic!=PACKET_HEADER)
                continue;
            if (header->type==PACKET_BROADCAST_SIGNAL) {
                if (packet->len<sizeof(PacketBroadcastSignal))
                    cout << "Received invalid broadcast signal packet!" << endl;
                else {
                    signal=(PacketBroadcastSignal*)header;
                    string name;
                    name.resize(16,' ');
                    memcpy(&(name[0]),signal->name,15);
                    cout << name << "|" << (signal->version&0xff) << "." << (signal->version>>8) << endl;
                }
            }
            else
                cout << "Received invalid packet: " << short(header->type) << endl;
        }
        Sleep(5);
    }
    SDLNet_UDP_Close(socket);
    SDLNet_FreePacket(packet);
    SDLNet_Quit ();
    system("pause");
    return 0;
}

2

06.05.2013, 17:52

so............
auch dies wäre (mehr aus Zufall als Wissen) erledigt.
Broadcasten funktioniert (vom gleichen, wie auch von einem anderen Computer aus) und die Testapplikation funktioniert genauso wie ich das wollte
Da ich im Internet immer ziemlich vergeblich nach einer Antwort gesucht habe, werde ich den vollständigen und richtigen Quelltext hier posten, während der alte falsche Quellcode zum Vergleich im ersten Post stehen bleibt.
Tipps für mich und andere:
  1. Geht den Quellcode durch. Immer und immer wieder!
  2. IP und Port werden in Big-Endian (im Gegensatz zum Windowsstandard LittleEndian) gespeichert. Auch wenn ihr das wisst, habt ihr vielleicht aus Versehen trotzdem ein Fehler deswegen. Bei mir zum Beispiel befindet ein solcher sich in der GetLocalBroadcastIP (falscher Name übrigens) dort steht ip.port=port;, aber das Format in einer IPaddress soll schon Big-Endian sein also wird das schön ersetzt durch SDLNet_Write16(port,&ip.port);
  3. Auf die Ports sowohl am Client als auch am Server achten! Du darfst weder beide Applikationen auf den selben Port achten (zumindestens wenn du am gleichen Computer sitzt, was während der Entwicklung sehr praktisch ist, was passiert wenn du verschiedene Computer nutzt habe ich nicht ausgetestet). Auch darfst du keine der beiden Applikationen auf Port 0 (bei SDLNet gleichbedeutend mit "suche einen freien Port") setzen. In keinen der beiden Fälle bekommst du irgendein Paket das du sendest.Dem Client darfst du einen Port 0 zuweisen, allerdings wenn du wie in diesem Fall den Port des Clients an den Server (über den Broadcast) schicken willst, musst du diesen erst über SDLNet_UDP_GetPeerAddress(socket,-1)->port holen.
  4. (gerade während dem Schreiben noch entdeckt :) ) Achtet darauf dass wirklich ihr eure Programme jedes mal wirklich komplett schließt (notfalls in den Taskmanager schauen), sonst wundert ihr euch nachher warum euer Broadcasten gerade noch funktioniert hat.
  5. Nur Ports über 1024 benutzen. Darunter sind Ports die vom System benutzt werden, auf die habt ihr meistens keinen Zugriff oder es kann euch wesentlich wahrscheinlicher passieren, dass ihr einen Port erwischt der schon benutzt wird (das kann euch natürlich auch bei Ports über 1024 passieren). In dem Fall werdet ihr auch Probleme haben!

Ich hoffe das hilft irgendjemanden irgendwie weiter.
Der richtige Quellcode:
common.h

C-/C++-Quelltext

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
#include <iostream>
#include <string>
#include <sstream>
#include <SDL/SDL.h>
#include <SDL/SDL_net.h>

using namespace std;

#ifdef _MSC_VER
#  define PACKED_STRUCT(name) __pragma(pack(push, 1)) struct name __pragma(pack(pop))
#elif defined(__GNUC__)
#  define PACKED_STRUCT(name) struct __attribute__((packed)) name
#endif

#define uint unsigned int
#define ushort unsigned short
#define byte unsigned char

string IPtoStr (unsigned int ip)
{
    unsigned char* it=(unsigned char*)(&ip);
    stringstream s;
    s << (short)(*it) << ".";it++;
    s << (short)(*it) << ".";it++;
    s << (short)(*it) << ".";it++;
    s << (short)(*it);
    return s.str();
}

string PortToStr (unsigned short port)
{
    unsigned short p=(port<<8)|(port>>8);
    stringstream s;
    s << p;
    return s.str ();
}

string IPtoStr (IPaddress ip)
{
    stringstream s;
    s << IPtoStr(ip.host) << ":" << PortToStr(ip.port);
    return s.str ();
}

string VertoStr (unsigned short version)
{
    stringstream s;
    s << (version&0xff) << "." << (version>>8);
    return s.str();
}

IPaddress GetLocalBroadcastIP (unsigned short port)
{
    IPaddress ip;
    ip.host=0;
    SDLNet_Write16(port,&ip.port);
    string hostname=SDLNet_ResolveIP(&ip);
    if (hostname=="") {
        ip.host=INADDR_NONE;
        return ip;
    }
    SDLNet_ResolveHost(&ip,hostname.c_str(),port);
    return ip;
}

#define PACKET_HEADER 0x43424f52 //ASCII for "ROBC"
#define ROBCO_VERSION 0x0201 //Ver.: 1.2

enum PacketType
{
    PACKET_BROADCAST_QUERY=0,
    PACKET_BROADCAST_SIGNAL=1
};

PACKED_STRUCT(PacketHeader
{
    uint magic;
    byte type;
});

PACKED_STRUCT(PacketBroadcastQuery
{
    uint magic;
    byte type;
    uint ipHost;
    ushort ipPort;
});

PACKED_STRUCT(PacketBroadcastSignal
{
    uint magic;
    byte type;
    uint ipHost;
    ushort ipPort;
    char name[16];
    ushort version;
});

server.cpp

C-/C++-Quelltext

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
#include <Windows.h>
#include <conio.h>
#include "../common.h"

int main (int argc,char* argv[])
{
    string name;
    cout << "Server name:";
    getline(cin,name);
    if (name.length()>15) {
        name=name.substr(0,15);
        cout << "Warning! Name is too long and will be reduced to \"" << name<< "\"!" << endl;
    }
    if (SDLNet_Init ()<0) {
        cout << SDLNet_GetError () << endl;
        system("pause");
        return -1;
    }
    IPaddress myIP=GetLocalBroadcastIP (25336);
    UDPsocket socket=SDLNet_UDP_Open (25336);
    UDPpacket* packet=SDLNet_AllocPacket (1024);
    PacketHeader* header;
    PacketBroadcastQuery* broadcastQuery;
    PacketBroadcastSignal* broadcastSignal;
    cout << "IP:" << IPtoStr(myIP.host) << ":" << myIP.port << endl;
    while (!_kbhit()) {
        if (SDLNet_UDP_Recv(socket,packet)>0) {
            header=(PacketHeader*)packet->data;
            if (packet->len<6||header->magic!=PACKET_HEADER)
                continue;
            if (header->type==PACKET_BROADCAST_QUERY) {
                if (packet->len!=sizeof(PacketBroadcastQuery))
                    cout << "Received invalid broadcast query packet!" << endl;
                else {
                    broadcastQuery=(PacketBroadcastQuery*)header;
                    cout << "Received broadcast query from: " << IPtoStr(broadcastQuery->ipHost) << ":" << PortToStr(broadcastQuery->ipPort) << endl;
                    packet->address.host=broadcastQuery->ipHost;
                    packet->address.port=broadcastQuery->ipPort;
                    broadcastSignal=(PacketBroadcastSignal*)header;
                    broadcastSignal->type=PACKET_BROADCAST_SIGNAL;
                    broadcastSignal->ipHost=myIP.host;
                    broadcastSignal->ipPort=myIP.port;
                    memset(broadcastSignal->name,0,16);
                    memcpy(broadcastSignal->name,&(name[0]),name.length());
                    broadcastSignal->version=ROBCO_VERSION;
                    packet->len=sizeof(PacketBroadcastSignal);
                    SDLNet_UDP_Send(socket,-1,packet);
                }
            }
            else
                cout << "Received invalid packet:" << short(header->type) << endl;
        }
        Sleep(5);
    }
    SDLNet_FreePacket(packet);
    SDLNet_UDP_Close (socket);
    SDLNet_Quit ();
    system("pause");
    return 0;
}

client.cpp

C-/C++-Quelltext

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
#include <Windows.h>
#include <conio.h>
#include "../common.h"

int main (int argc,char* argv[])
{
    if (SDLNet_Init ()<0) {
        cout << SDLNet_GetError () << endl;
        system("pause");
        return -1;
    }
    IPaddress myIP=GetLocalBroadcastIP(25535);
    UDPsocket socket=SDLNet_UDP_Open (0);
    myIP.port=SDLNet_UDP_GetPeerAddress(socket,-1)->port;
    UDPpacket* packet=SDLNet_AllocPacket(1024);
    PacketBroadcastQuery* query;
    PacketBroadcastSignal* signal;
    PacketHeader* header;
    query=(PacketBroadcastQuery*)packet->data;
    query->magic=PACKET_HEADER;
    query->type=PACKET_BROADCAST_QUERY;
    query->ipHost=myIP.host;
    query->ipPort=myIP.port;
    packet->address.host=myIP.host|0xff000000;
    SDLNet_Write16(25336,&packet->address.port);
    cout << "use brodcast IP:" << IPtoStr(packet->address) << endl;
    packet->len=sizeof(PacketBroadcastQuery);
    system("pause");
    if (SDLNet_UDP_Send(socket,-1,packet)<0) {
        cout << SDLNet_GetError () << endl;
    }
    cout << "Server name     |Version|Internet protocol|Port " << endl
         << "----------------+-------+-----------------+-----" << endl;
    int r;
    while (!_kbhit()) {
        r=SDLNet_UDP_Recv(socket,packet);
        if (r<0)
            cout << SDLNet_GetError () << endl;
        else if (r>0) {
            header=(PacketHeader*)packet->data;
            if (packet->len<6||header->magic!=PACKET_HEADER)
                continue;
            if (header->type==PACKET_BROADCAST_SIGNAL) {
                if (packet->len<sizeof(PacketBroadcastSignal))
                    cout << "Received invalid broadcast signal packet!" << endl;
                else {
                    signal=(PacketBroadcastSignal*)header;
                    cout.width (16);cout << signal->name << "|";
                    cout.width (7);cout << VertoStr(signal->version) << "|";
                    cout.width (17);cout << IPtoStr(signal->ipHost) << "|";
                    cout.width (5);cout << PortToStr(signal->ipPort) << endl;
                }
            }
            else
                cout << "Received invalid packet: " << short(header->type) << endl;
        }
        Sleep(5);
    }
    SDLNet_UDP_Close(socket);
    SDLNet_FreePacket(packet);
    SDLNet_Quit ();
    system("pause");
    return 0;
}

Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von »Helco« (06.05.2013, 18:53)