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

$nooc

Alter Hase

  • »$nooc« ist der Autor dieses Themas

Beiträge: 873

Wohnort: Österreich / Kärnten

Beruf: Schüler

  • Private Nachricht senden

1

13.12.2007, 22:03

winsocket -> select();

also folgenes..

ich hab mir ne klasse geschrieben die als server funktionieren soll. wie die überschrift schon sagt, basierend auch winsocket..

ich hab meinen client gestartet, dieser kann auch eine verbindung herstellen, jedoch passiert folgendes:

der server überprüft nur dann ob eine nachricht angekommen ist, wenn auch eine verbindung aufgebaut ist. select() liefert die anzahl der sockets im fd_set die bereit sind..
das problem ist, nachdem sich 1 socket verbunden hat, liefert mir select() auf einmal eine utopische zahl zb 4654987. wie kann das sein?

das 2te problem ist, ich mach da glaub ich irgendwas falsch, denn mein programm geht bis zur accept() methode, die dann halt auf eine neue verbindung wartet.. wie kann ich abfragen OB sich überhaupt ein socket verbinden will? also.. wie kann ich verhindern dass mein programm einfach die accept() aufruft und wartet bis was kommt`?

hier der entsprechende ausschnitt:

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
        // [...]


    timeval time;
            time.tv_sec     = 0;
            time.tv_usec    = 1;

       // select()

    ret = select(0, &m_fdSetRead, NULL, NULL, &time);

    if(ret < 1)
        return 1;

    Client newConnection;
    for(x = 0; x < m_fdSetRead.fd_count; x++)
    {
        int addrsize = sizeof(SOCKADDR_IN);
        int freeClientSlot;

        if(m_fdSetRead.fd_array[x] == m_acceptSocket)
        {
            newConnection.socket    = accept(m_acceptSocket, (SOCKADDR*)&newConnection.addr, &addrsize);
            freeClientSlot          = getFreeClientSlot();

            if(freeClientSlot == -1)
                continue;

            m_Client[freeClientSlot]        = newConnection;
            m_Client[freeClientSlot].used   = true;
        }
    }

        // [...]
Am Anfang der Weisheit steht die eigene Erkenntnis, dass man selbst nichts weiß! - Sokrates

rklaffehn

Treue Seele

Beiträge: 267

Wohnort: Braunschweig

  • Private Nachricht senden

2

14.12.2007, 07:14

Benutzt du auch die FD_XXX Macros, um Socket-Deskriptoren im m_fdSetRead zu setzen/prüfen (bin halt POSIX verseucht :) )? Mit

C-/C++-Quelltext

1
if (FD_ISSET (m_acceptSocket, &m_fdSetRead)) { [...] }
kannst du deine inneres Schleife abkürzen.

Das utopische Verhalten könnte daran liegen, dass du m_fdSetRead vor dem ::select aufruf nicht neu initialisierst. Das ist wichtig, weil ::select in diese Mengen modifiziert.

C-/C++-Quelltext

1
2
3
FD_ZERO (&m_fdSetRead); // Alles muss raus!

FD_SET (m_acceptSocket, &m_fdSetRead); // Auf den will ich hören.

res = select (...); // gibts was?
Mir ist ohnehin nicht klar, warum die Deskriptormenge eine Mitgliedsvariable deiner Klassen ist/sein muss.

Du arbeitest mit einem Timeout. Wenn an keinem Socket was ankommt, ist das ::select Ergebnis 0, was < 1 ist. Für deinen Fall behandelst du das als Fehler (glaub ich). Echte Fehler sind aber < 0.

Wenn dein m_acceptSocket im "listen" Zustand ist (du hast, irgendwann mal ::listen drauf aufgerufen), dann will sich immer jemand verbinden, wenn auf m_acceptSocket Daten zu lesen sind. ::accept erzeugt dir ja einen neuen Socket, auf dem die weitere Kommunikation für die neue Verbindung läuft. Damit stellt sich deine zweite Frage nicht wirklich.
God is real... unless declared integer.
http://www.boincstats.com/signature/user_967277_banner.gif

$nooc

Alter Hase

  • »$nooc« ist der Autor dieses Themas

Beiträge: 873

Wohnort: Österreich / Kärnten

Beruf: Schüler

  • Private Nachricht senden

3

14.12.2007, 11:14

Zitat von »"rklaffehn"«


Benutzt du auch die FD_XXX Macros, um Socket-Deskriptoren im m_fdSetRead zu setzen/prüfen (bin halt POSIX verseucht :) )? Mit

C-/C++-Quelltext

1
if (FD_ISSET (m_acceptSocket, &m_fdSetRead)) { [...] }
kannst du deine inneres Schleife abkürzen.

Das utopische Verhalten könnte daran liegen, dass du m_fdSetRead vor dem ::select aufruf nicht neu initialisierst. Das ist wichtig, weil ::select in diese Mengen modifiziert.

C-/C++-Quelltext

1
2
3
FD_ZERO (&m_fdSetRead); // Alles muss raus!

FD_SET (m_acceptSocket, &m_fdSetRead); // Auf den will ich hören.

res = select (...); // gibts was?



ja, das mache ich weiter oben im code.. ich benutze die makros..

Zitat von »"rklaffehn"«


Mir ist ohnehin nicht klar, warum die Deskriptormenge eine Mitgliedsvariable deiner Klassen ist/sein muss.


das ist noch von 'vorher' .. dachte das sei nötig.. muss das noch ändern..

Zitat von »"rklaffehn"«


Du arbeitest mit einem Timeout. Wenn an keinem Socket was ankommt, ist das ::select Ergebnis 0, was < 1 ist. Für deinen Fall behandelst du das als Fehler (glaub ich). Echte Fehler sind aber < 0.

select liefert ja die anzahl der sockets die bereit sind.. mit der abfrage ob ret < 1 ist geh ich sicher dass min. 1 socket bereit ist, bevor ich daten die evtl. ankommen verarbeite

Zitat von »"rklaffehn"«


Wenn dein m_acceptSocket im "listen" Zustand ist (du hast, irgendwann mal ::listen drauf aufgerufen), dann will sich immer jemand verbinden, wenn auf m_acceptSocket Daten zu lesen sind. ::accept erzeugt dir ja einen neuen Socket, auf dem die weitere Kommunikation für die neue Verbindung läuft. Damit stellt sich deine zweite Frage nicht wirklich.

wie meinst du das? wenn ich accept() aufrufe, dann blockt mir die funktion ja alles, bis sich ein socket verbindet.. ich weiss aber nicht wie ich das verhindern kann..

danke für deine antworten :)
Am Anfang der Weisheit steht die eigene Erkenntnis, dass man selbst nichts weiß! - Sokrates

BlackSnake

Community-Fossil

Beiträge: 1 549

Beruf: Student

  • Private Nachricht senden

4

14.12.2007, 12:21

du könntest diese funktion benutzen....

C-/C++-Quelltext

1
WSAAsyncSelect

rklaffehn

Treue Seele

Beiträge: 267

Wohnort: Braunschweig

  • Private Nachricht senden

5

14.12.2007, 12:39

Wenn du deinen zuhörenden Socket m_acceptSocket z.B. mit

C-/C++-Quelltext

1
::listen (m_acceptSocket, 1); // m_acceptSocket soll max. Verbindungen gleichzeitig akzeptieren
vorbereitest, und select dann meldet, dass dort Daten zum Lesen vorliegen, kann accept nicht mehr blockieren. Dafür blockiert dann eben select (ohne Timeout) oder du fragst in einer Schleife an (mit Timeout) und machst erstmal "was anderes", wenn eben keine Daten anliegen.

Das accept blockiert nur, wenn du vorher nicht mit select auf ankommende Daten wartest und der zuhörende Socket den Zustand "blocking" hat; es gibt auch "nonblocking." Im letzteren Fall kehrt accept auch sofort zurück, und man muss am Ergebnis selbst noch prüfen, ob wirklich eine Verbindung angenommen wurde.

Mal ein paar Codeschnippsel, die jedenfalls mit POSIX Systemen funktionieren und einen Socket erzeugen, der "NONBLOCKING" ist und Verbindungen akzeptiert:

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
bool TcpIpServer::listen ()
{
  if (-1 != _socket)
    return false;

  // erzeugen

  _socket = ::socket (PF_INET, SOCK_STREAM, 0);
  if (_socket < 0)
    {
      dumpError ("create", errno);
      return false;
    }

  // Non-Blocking machen

  int flags = ::fcntl (_socket, F_GETFL, 0);
  if (   flags < 0
      || ::fcntl (_socket, F_SETFL, flags | O_NONBLOCK) < 0)
    {
      dumpError ("F_SETFL", errno);
      close ();
      return false;
    }

  // Adresse definieren, auf der zugehört wird

  struct sockaddr_in sock_addr;
  ::memset (&sock_addr, 0, sizeof (sock_addr));

  sock_addr.sin_family      = AF_INET;
  sock_addr.sin_port        = htons (32410);
  sock_addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);

  if (::bind (_socket, reinterpret_cast<const struct sockaddr*> (&sock_addr),
          sizeof (sock_addr)) < 0)
    {
      dumpError ("bind", errno);
      return close ();
    }

  // Sagen, dass man Verbindungen hören will.

  if (::listen (_socket, 1) < 0)
    {
      dumpError ("listen", errno);
      close ();
      return false;
    }

  return true;
}


Wenn dann durch ein ::select auf dem Socket _socket Daten zum Lesen gemeldet werden, kann ::accept nicht mehr blockieren.

Wenn ich ::accept ohne ein ::select vorher aufrufe, dann kehrt es auch immer sofort zurück, und ich muss am Ergebnis erkennen, ob einfach keine Verbindung vorlag, oder ob ein Fehler aufgetreten ist.

C-/C++-Quelltext

1
2
3
4
5
int incoming_socket = ::accept (_socket);
if (incoming_socket < 0 && errno != EWOULDBOCK && errno != EAGAIN)
{
  return false;
}


Ich hab jetzt nicht geschaut, ob das alles unter Windows mit Winsock auch so funktioniert, für ein POSIX System klappt das aber ganz gut.
God is real... unless declared integer.
http://www.boincstats.com/signature/user_967277_banner.gif

Nox

Supermoderator

Beiträge: 5 272

Beruf: Student

  • Private Nachricht senden

6

14.12.2007, 14:46

Und hier eine Variante, die "blocking" ist und auf Windows funktioniert:

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
    TIMEVAL time = { 3, 0 };

    fd_set readset, errorset;
    while(server->status == READY)
    {

        FD_ZERO(&readset);
        FD_ZERO(&errorset);
        FD_SET(server->Socket, &readset);
        FD_SET(server->Socket, &errorset);

        for(std::list<BasicServerClient*>::iterator temp = server->ClientList.begin(true); temp != server->ClientList.end(); temp++)
        {
            if((*temp)->status == READY)
                FD_SET((*temp)->Socket, &readset);
        }
        server->ClientList.unlock();

        if(SOCKET_ERROR == select(NULL, &readset, NULL, &errorset, &time))
        {
            server->geterror();//EDIT: muss mitgelogt werden!

        }

        for(std::list<BasicServerClient*>::iterator temp = server->ClientList.begin(true); temp != server->ClientList.end(); temp++)
        {
            if(((*temp)->status == READY) && FD_ISSET((*temp)->Socket, &readset))
                (*temp)->ClientCallback();
        }
        server->ClientList.unlock();
    }


Hast du dir denn schonmal den Fehlercode zurückgeben lassen bzw schonmal uberprüft was vor dem select und was nach dem select im FD_Set ist.
PRO Lernkurs "Wie benutze ich eine Doku richtig"!
CONTRA lasst mal die anderen machen!
networklibbenc - Netzwerklibs im Vergleich | syncsys - Netzwerk lib (MMO-ready) | Schleichfahrt Remake | Firegalaxy | Sammelsurium rund um FPGA&Co.

$nooc

Alter Hase

  • »$nooc« ist der Autor dieses Themas

Beiträge: 873

Wohnort: Österreich / Kärnten

Beruf: Schüler

  • Private Nachricht senden

7

19.12.2007, 12:23

hallo leute.. also es tut mir leid, aber ich verstehe das ganze nicht.. ^^

nur kurz zusammengefasst:

mein server soll nicht blocken..
mein server blockt aber, weil accept() aufgerufen wird und dann wartet und wartet und wartet..

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    Client newConnection;
    for(x = 0; x < m_fdSetRead.fd_count; x++)
    {
        int addrsize = sizeof(SOCKADDR_IN);
        int freeClientSlot;

        if(m_fdSetRead.fd_array[x] == m_acceptSocket)
        {
            newConnection.socket    = accept(m_acceptSocket, (SOCKADDR*)&newConnection.addr, &addrsize);
            freeClientSlot          = getFreeClientSlot();

            if(freeClientSlot == -1)
                continue;

            m_Client[freeClientSlot]        = newConnection;
            m_Client[freeClientSlot].used   = true;
        }
    }


also.. WIE kann ich das jetzt so umschreiben dass accept() nur dann ausgeführt wird, wenn sich außerhalb auch ein socket verbinden will.

ich hab dieses "winsocket system" wohl irgendwie nicht ganz durchschaut..

mein code basiert auf einem tutorial.. ich versteh zb. auch nicht was mit dieser if() bedingung erreicht wird O_o

C-/C++-Quelltext

1
2
     // siehe oben..

if(m_fdSetRead.fd_array[x] == m_acceptSocket)
Am Anfang der Weisheit steht die eigene Erkenntnis, dass man selbst nichts weiß! - Sokrates

Nox

Supermoderator

Beiträge: 5 272

Beruf: Student

  • Private Nachricht senden

8

19.12.2007, 15:51

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
    TIMEVAL time = { 3, 0 };

    fd_set readset, errorset;
    while(!quit)
    {
        FD_ZERO(&readset);
        FD_ZERO(&errorset);
        FD_SET(m_acceptSocket, &readset);
        FD_SET(m_acceptSocket, &errorset);

        for(std::list<Client*>::iterator temp = ClientList.begin(); temp != ClientList.end(); ++temp)
            FD_SET((*temp)->Socket, &readset);

        if(SOCKET_ERROR == select(NULL, &readset, NULL, &errorset, &time))
        {
            //EDIT: muss mitgelogt werden!

        }

        if(FD_ISSET(m_acceptSocket, &readset))
        { 
            newConnection.socket    = accept(m_acceptSocket, (SOCKADDR*)&newConnection.addr, &addrsize); 
            freeClientSlot          = getFreeClientSlot(); 

            if(freeClientSlot != -1) 
            {
                m_Client[freeClientSlot]        = newConnection; 
                m_Client[freeClientSlot].used    = true; 
            }
        }

        for(std::list<Client*>::iterator temp = ClientList.begin(); temp != ClientList.end(); ++temp)
        {
            if(FD_ISSET((*temp)->Socket, &readset))
                ;//mach was intelligentes

        }
    }


So ähnlich sollte es eigentlich gehen. Ich weiß aber nicht was genau diese m_fdSetRead Instanz alles macht bzw. nicht macht.
PRO Lernkurs "Wie benutze ich eine Doku richtig"!
CONTRA lasst mal die anderen machen!
networklibbenc - Netzwerklibs im Vergleich | syncsys - Netzwerk lib (MMO-ready) | Schleichfahrt Remake | Firegalaxy | Sammelsurium rund um FPGA&Co.

rklaffehn

Treue Seele

Beiträge: 267

Wohnort: Braunschweig

  • Private Nachricht senden

9

20.12.2007, 07:19

Zitat von »"$nooc"«

mein code basiert auf einem tutorial.. ich versteh zb. auch nicht was mit dieser if() bedingung erreicht wird O_o

C-/C++-Quelltext

1
2
     // siehe oben..

if(m_fdSetRead.fd_array[x] == m_acceptSocket)


Irgendwie muss select ja melden, auf welchen Kanälen Daten ankommen. Als Eingabeparameter gibts du select einen Satz von Deskriptoren, die dich interessieren.

Als Ergebnis liefert select die Anzahl der Deskriptoren, an denen man was machen kann, und modifiziert die übergebenen fd_set Strukturen, so dass hinterher nur noch die Deskriptoren übrig sind, an denen was passiert ist.

Das ist auch der Grund, warum man dieses Set immer neu aufsetzen muss, sonst sind evtl. hinterher nicht mehr alle Desktiptoren enthalten. Diese if-Abfrage ist das selbe wie

C-/C++-Quelltext

1
if (FD_ISSET (m_acceptSocket, &m_fdSetRead)) { ... }


Und jetzt nochmal in natürlicher Sprache dazu, wie man accept dazu bringt, nicht zu blockieren:

Es gibt zwei Fälle. Erstens, der Socket ist "blocking."

Dann nutzt man vorher ein select, um auf dem Socket zu fragen, ob Daten zu lesen anliegen. Wenn select meldet, dass dort für einen Lesezugriff etwas passiert ist, kann man auch accept aufrufen, und das darf nicht blockieren. Die Antwort von select besteht aus dem Ergebnis der Funktion -- an mindestens einem Socket ist was passiert -- und aus der Menge der Sockets, die in den fd_sets zurückgegeben werden. Die müssen selbst nochmal überprüft werden.

Wenn man mit select "nonblocking" simulieren will, darf man kein Timeout benutzen (Parameter NULL), damit select nicht blockiert. Dann kehrt auch select sofort zurück und liefert einfach eine 0 als Ergebnis und man kann es ggf. nochmal versuchen. Wenn man immer ein wenig warten will, gibt man ein kurzes Timeout an. Mit einem Timeout von { 0, 0 } wartet select in alle Ewigkeit, ist also wieder "blocking."

Zweiter Fall, der Socket ist "nonblocking."

Hier ruft man einfach "accept" auf, und prüft das Ergebnis. In diesem Fall liefert ist das accept Ergebnis ein Fehler (SOCKET_ERROR), aber WSAGetLastError liefert WSAEWOULDBLOCK. Das bedeutet dann, dass eben keine Verbindung aufgebaut werden soll. In diesem Fall muss man es einfach später wieder versuchen.

Wie mache ich einen Socket "nonblocking?"

C-/C++-Quelltext

1
2
u_long set_nonblocking = 1;
ioctlsocket (m_acceptSocket, FIONBIO, &set_nonblocking);


Wenn das alles nicht hilft, könnte es auch noch sein, dass dein Client Dummheiten macht und den Server damit aus dem Tritt bringt.
God is real... unless declared integer.
http://www.boincstats.com/signature/user_967277_banner.gif

$nooc

Alter Hase

  • »$nooc« ist der Autor dieses Themas

Beiträge: 873

Wohnort: Österreich / Kärnten

Beruf: Schüler

  • Private Nachricht senden

10

22.12.2007, 12:01

Zitat von »"rklaffehn"«


Wenn man mit select "nonblocking" simulieren will, darf man kein Timeout benutzen (Parameter NULL), damit select nicht blockiert.


ich hab den parameter auf NULL gesetzt, und dann hat select blockiert..
Am Anfang der Weisheit steht die eigene Erkenntnis, dass man selbst nichts weiß! - Sokrates

Werbeanzeige