Toon posts:

[C++] Winsock en events

Pagina: 1
Acties:

Verwijderd

Topicstarter
Omdat het vorige topic eigenlijk vooral over "hoe een server te bouwen" ging en ook een beetje dood was gegaan, open ik even een nieuw topic, omdat ik even een flink probleem heb met WSAEventSelect() en WSAWaitForMultipleEvents()

Er zal een lap text komen, want er is veel informatie nodig. Misschien, dus ik zal mijn vraag even helemaal onderaan de post zetten als een soort conclusie ;)
offtopic:
Ik denk dat andere users die hier mee (gaan) werken ook wel wat aan hebben want ik kwam op de MSDN na erg weinig tegen over events op het net en GoT


Goed ik heb een functie die in pseudocode er zo uit ziet:
Pseudo
code:
1
2
3
4
5
6
Maar array met events
Check events
Wacht op een van de events
Kijk wat de laatste was
Voer de event uit
Herhaal


De output zou moeten zijn, als je twee clients toevoegd en daarna met de eerste iets stuurd:
output
code:
1
2
3
4
Starting server
waiting....adding client 1
waiting....adding client 2
waiting...reading from client 1


alleen nu is hij:
output
code:
1
2
3
4
5
6
Starting server
waiting....adding client 1
waiting....not implemented 0
waiting...not implemented 0
waiting...not implemented 0 // adding a client
waiting...not implemented 0


dit is de code die ik er voor had:
functie run (is threaded)
C:
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
DWORD CClientManager::Run(LPVOID arg)
{
    WSAEVENT *clientEvent = (WSAEVENT*)malloc((socketCount+1)*sizeof(WSAEVENT));
    for (int i = 0; i < socketCount+1;i++)
    {
        clientEvent[i] = WSACreateEvent();
    }

    while (1)
    {
        printf ("Event: ");
        for (int i=0; i < socketCount; i++)
        {
            switch (tempEvent[i])
            {
                case FD_ACCEPT:
                    if(WSAEventSelect(hostSocket, clientEvent[socketCount], FD_ACCEPT) !=0)
                        printf("error(accepting)...");
                    break;
                case FD_READ:
                    if(WSAEventSelect(clientSocket[i], clientEvent[i], FD_READ|FD_CLOSE) !=0)
                        printf("error(readin)...");
                    break;
                case FD_CLOSE:
                    if(WSAEventSelect(clientSocket[i], clientEvent[i], FD_CLOSE) !=0)
                        printf("error(closing)");
                    break;
                case FD_WRITE:
                    if(WSAEventSelect(clientSocket[i], clientEvent[i], FD_WRITE) !=0)
                        printf("error(writing)..");
                    break;
                default:
                    if(!WSAResetEvent(clientEvent[i]))
                        printf("error (reseting)...");
            }
        }

        printf ("waiting...");
        DWORD swResult = WSAWaitForMultipleEvents(socketCount+1, clientEvent, false, INFINITE, false);
        int tempEventIndex = swResult - WSA_WAIT_EVENT_0;
        WSANETWORKEVENTS lpNetworkEvents = {0};
        if (tempEventIndex < (socketCount-1))
            WSAEnumNetworkEvents(clientSocket[tempEventIndex-1],clientEvent[tempEventIndex-1],&lpNetworkEvents);    
        else
            WSAEnumNetworkEvents(hostSocket,clientEvent[socketCount],&lpNetworkEvents);
        printf ("ne is: %d...",lpNetworkEvents.lNetworkEvents);
        int j = 0;
        switch (lpNetworkEvents.lNetworkEvents)
        {
            case FD_ACCEPT: 
                {
                    for (; j < socketCount; j++)
                    {
                        if(tempEvent[j] == FD_ACCEPT)
                            break;
                    }
                    struct sockaddr sinTemp = {0};
                    int sinTempLength = sizeof (sinTemp);
                    if ((clientSocket[j] = accept (hostSocket, &sinTemp, &sinTempLength)) == INVALID_SOCKET)
                        printf("Invalid Client. Error: %d\n", WSAGetLastError());
                    else
                        printf("Accepted new user\n");
                    tempEvent[j] = FD_READ;                 
                }
                break;
            case FD_READ: 
                printf("Reading\n");
                break;
            case FD_CLOSE: 
                printf("Closing\n");
                break;
            case FD_WRITE: 
                printf("Writing\n");
                break;
            case 0:
                printf("crapping 0\n");
                Sleep(1000);
                break;
            default:
                printf ("no implemented action\n");
        }
        WSAResetEvent(clientEvent[j]);
    }
    closesocket(hostSocket);
    free (clientEvent);

    return 0;
}


het gaat op de volgende regel fout:
C:
1
if(WSAEventSelect(clientSocket[i], clientEvent[i], FD_READ|FD_CLOSE) !=0)

want als ik :
C:
1
if(WSAEventSelect(clientSocket[i], clientEvent[i], FD_READ) !=0)


Hij zet de "last event code" op 0 (of de event zelf, want dat snap ik dus niet)
hier even een snel lijstje:
code:
1
2
3
4
5
6
7
8
Code    Soort   Betekenis
1   Network_event   FD_READ
32  Network_event   FD_CLOSE
8   Network_event   FD_ACCEPT
16  Network_event   FD_CONNECT
2   Network_event   FD_WRITE
4   Network_event   FD_OOB
-1  Socket error    INVALID_SOCKET


Mijn vraag is dus:
waarom zet hij de event voor WSAWaitForMultipleEvents op 0 en triggered hij hem (dus de dubbele FD_READ|FD_CLOSE)? en hoe kan ik het oplossen?

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 15:32

.oisyn

Moderator Devschuur®

Demotivational Speaker

Wat staat er in tempEvent ()? En waarom select je de events steeds opnieuw?

verder vind ik de afhandeling in je switch nogal vaag. Je zet j op 0, en als het geen FD_ACCEPT is dan reset je dus altijd de event voor client 0

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Verwijderd

Topicstarter
tempEvent is een integer waarin de waarde staat van de event.
Daarna kijk ik of hij er wat mee kan doen (in andere functies wordt deze ook toegewezen).

Ik heb 10 sockets (11 als je de hostsocket ook mee rekend) en je mag maar een WSAEvent per socket hebben, dus 11 events, die moeten worden bijgehouden.
Vandaar het wazige loopje + switch.

Over het WSAResetEvent: mm dat is waar :D

Kan het zijn dat mijn volgorde niet klopt, want de FD_ACCEPT event wordt getriggerd als de client een connect() doet.
Dus als de client send() doet triggert dat op de server toch FD_READ?

Hoe zo jij dat aanpakken?

[ Voor 23% gewijzigd door Verwijderd op 02-12-2003 17:08 . Reden: even een extra vraag er bij gezet ]


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 15:32

.oisyn

Moderator Devschuur®

Demotivational Speaker

Je hebt er iig een knoeiboel van gemaakt ;)

Ik zal even door je code lopen

C++:
1
    WSAEVENT *clientEvent = (WSAEVENT*)malloc((socketCount+1)*sizeof(WSAEVENT)); 

waarom geen
C++:
1
 std::vector<WSAEVENT> (socketCount + 1);
Hoef je het ook niet meer zelf op te ruimen :)
(je moet natuurlijk wel alle event objecten sluiten met WSACloseEvent (), iets dat je nu ook niet doet overigens. Als je daar een wrapper class omheen maakt dan kun je construction en destruction zelf regelen, zodat je niet eens meer zelf WSACreateEvent () en WSACloseEvent () hoeft aan te roepen)
En waarom eigenlijk socketCount + 1? Omdat de host er ook tussen staat neem ik aan?

C++:
1
2
3
                default: 
                    if(!WSAResetEvent(clientEvent[i])) 
                        printf("error (reseting)..."); 

hier snap ik het nut eigenlijk niet zo van

C++:
1
2
3
4
5
6
7
        DWORD swResult = WSAWaitForMultipleEvents(socketCount+1, clientEvent, false, INFINITE, false); 
        int tempEventIndex = swResult - WSA_WAIT_EVENT_0; 
        WSANETWORKEVENTS lpNetworkEvents = {0}; 
        if (tempEventIndex < (socketCount-1)) 
            WSAEnumNetworkEvents(clientSocket[tempEventIndex-1],clientEvent[tempEventIndex-1],&lpNetworkEvents);     
        else 
            WSAEnumNetworkEvents(hostSocket,clientEvent[socketCount],&lpNetworkEvents); 


waarom doe je daar tempEventIndex - 1? Ik vermoed omdat de host er ook bij zit. Maar het for-lusje daarboven doorloop je ook maar socketCount keer, en daar zit de host ook bij. En uit de enumNetworkEvents op de hostSocket blijkt dat die een event heeft die gelijk is aan socketCount. Hij staat blijkbaar achteraan, dus waarom die -1? En als ie achteraan staat is het gelijk duidelijk dat die FD_ACCEPT nooit goed gaat, want zoals je in de opmerking van me hierboven kunt lezen doorloop je je tempEvents 1 keer te weinig

C++:
1
2
3
4
5
                    for (; j < socketCount; j++) 
                    { 
                        if(tempEvent[j] == FD_ACCEPT) 
                            break; 
                    }

waarom doorzoek je de tempEvent array als je toch al weet dat de hostSocket event achteraan staat?

[ Voor 7% gewijzigd door .oisyn op 02-12-2003 17:12 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Verwijderd

Topicstarter
1: socketCount zijn de 10 clientSockets, + 1 voor de host socket.
Ik was nog niet bezig geweest met de code erg netjes te maken, omdat ik namelijk vooral even met die events zat te kloten.
Een vector is is opzich wel handig, alleen nu nog niet, want ik ben nog niet helemaal klaar met de basis.

2: tja je moet een default hebben, maar je hebt gelijk, het is waarschijnelijk netter om daar de tempEvent gewoon aan te passen.

3: mmm de loop wordt wel genoeg door lopen (want het accept gedeelte werkt er ook op + heb hem getest zonder loop)

4: ik wilde graag weten welke van de tempEvents voor de FD_ACCEPT mochten (kunnen er 0 of meer zijn) dus leek mij handig om er zo aan te komen (omdat de index op dat moment namelijk 10 is voor de hostSocket en de client socket[j] moet worden gemaakt door accept.

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 15:32

.oisyn

Moderator Devschuur®

Demotivational Speaker

Ah, dus je zoekt naar een lege client. Kun je dan niet beter zoeken in de clientSocket lijst naar een lege socket? Want ik neem aan dat nu alle tempEvents op FD_ACCEPT gezet worden? Dan is het ook logisch dat FD_ACCEPT wordt geregistreerd bij je hostSocket, aangezien je die als er nog geen clients zijn 10x zet (zie je loopje aan het begin).

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • matthijsln
  • Registratie: Augustus 2002
  • Laatst online: 22-05 13:09
Nog even over het ontwerp van je applicatie (meer je vorige topic).

In eerste instantie zou ik het gewoon zo simpel mogelijk houden: Of single threaded of simpel mulitithreaded zonder optimalisatie. Later is er (als die 500 connecties er aan zitten te komen) natuurlijk nog de mogelijkheid om optimalisatie toe te passen in de socket-code.

Dit zal redelijk simpel te wijzigen zijn als je de code die de service-taken uitvoert goed scheidt met de socket-code.

Dan kan je nu die Winsock code links laten liggen en er eventueel later nog op terug komen, als je wat meer vertrouwd bent met de WinAPI en hoe je je netwerk-code moet structureren (no offense maar dat lijkt me op dit moment de oorzaak van je topic).

  • madwizard
  • Registratie: Juli 2002
  • Laatst online: 26-10-2024

madwizard

Missionary to the word of ska

Ik kan niet alles wat je doet volgen in je code maar ik weet wel dat lNetworkEvents een bitmask is, er kunnen dus meerdere FD_* constantes gecombineerd (ge-or-ed) in voor komen. Je kan ze dus niet met een switch/case checken en vandaar waarschijnlijk ook de 'not implemented' meldingen.

www.madwizard.org


Verwijderd

Om je al vast een boel zoekwerk te besparen straks (als je zo door gaat met de events): Een FD_WRITE event krijg je pas NADAT een WSASend() of send() een keer WSAEWOULDBLOCK gereturned heeft...

code:
1
2
3
4
5
6
while(bytestosend)
  code = send(bytes)
  if code = WSAEWOULDBLOCK
    wacht_op_event()
    send(bytes)
  bytes = volgendebytes()

Maar dan in C++ en wat robuuster dan dit :+

Quote uit MSDN:
The FD_WRITE network event is handled slightly differently. An FD_WRITE network event is recorded when a socket is first connected with connect/WSAConnect or accepted with accept/WSAAccept, and then after a send fails with WSAEWOULDBLOCK and buffer space becomes available. Therefore, an application can assume that sends are possible starting from the first FD_WRITE network event setting and lasting until a send returns WSAEWOULDBLOCK. After such a failure the application will find out that sends are again possible when an FD_WRITE network event is recorded and the associated event object is set.
Voor het lezen uit een socket is het weer net andersom. Je hoeft niet te blijven lezen tot je een WSAEWOULDBLOCK krijgt. Zodra je WSARecv() of recv() aanroept wordt het event opnieuw getriggered als er nog data klaar staat.

[ Voor 58% gewijzigd door Verwijderd op 03-12-2003 09:01 ]


Verwijderd

Topicstarter
@.oisyn, nee dat gedeelte werkt al helemaal. de clientSocket krijgt een tempEvent FD_ACCEPT mee, en dan wordt er 1 event aan de FD_ACCEPT event van hostSocket mee gegeven. Dit stuk werkt. Als het accepten is afgehandeld word de tempEvent voor declientSocket op FD_READ gezet.

@matthijsln: een goede single threaded applicatie had ik al, alleen ik liep nu vast op events.

@madwizard: lNetworkEvents heeft een parameter die dus de laatste event mee geeft voor die socket. Maar hoe haal je hem er dan uit als hij meerdere is. Dus door |?

code:
1
2
3
4
typedef struct _WSANETWORKEVENTS {
  long     lNetworkEvents;
  int      iErrorCode[FD_MAX_EVENTS];
} WSANETWORKEVENTS, *LPWSANETWORKEVENTS;


@Qlone:
dus het is niet
code:
1
2
3
4
5
6
7
8
server: maak socket s
server: maak event FD_READ voor socket s
server: wait
client: send("iets") naar server 
server: event is triggered op WSA_WAIT_EVENT_0
server: kijk wat de laatste event was (FD_READ)
server: ontvang
server: herhaal

?

  • madwizard
  • Registratie: Juli 2002
  • Laatst online: 26-10-2024

madwizard

Missionary to the word of ska

Verwijderd schreef op 03 december 2003 @ 09:16:
@madwizard: lNetworkEvents heeft een parameter die dus de laatste event mee geeft voor die socket. Maar hoe haal je hem er dan uit als hij meerdere is. Dus door |?
Nee met and (&):
C++:
1
2
3
4
5
6
7
8
if (lpNetworkEvents.lNetworkEvents & FD_ACCEPT)
{
   // FD_ACCEPT
}
if (lpNetworkEvents.lNetworkEvents & FD_CLOSE)
{
   // FD_CLOSE
}

Niet else if gebruiken natuurlijk anders blijf je nog maar 1 mogelijkheid afhandelen.

www.madwizard.org


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 15:32

.oisyn

Moderator Devschuur®

Demotivational Speaker

Verwijderd schreef op 03 december 2003 @ 09:16:
@.oisyn, nee dat gedeelte werkt al helemaal. de clientSocket krijgt een tempEvent FD_ACCEPT mee, en dan wordt er 1 event aan de FD_ACCEPT event van hostSocket mee gegeven. Dit stuk werkt. Als het accepten is afgehandeld word de tempEvent voor declientSocket op FD_READ gezet.
ja het werkt ja, maar het werkt socketCount teveel. Zoals ik al zei, je loopt door je tempEvents heen, en bij elke FD_ACCEPT zet je een event op de hostSocket. Als je dus de hele array tempEvents vol hebt staan met FD_ACCEPT, dan selecteer je elke keer een FD_ACCEPT event in hostSocket

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Verwijderd

Topicstarter
.oisyn schreef op 03 december 2003 @ 12:40:
[...]

ja het werkt ja, maar het werkt socketCount teveel. Zoals ik al zei, je loopt door je tempEvents heen, en bij elke FD_ACCEPT zet je een event op de hostSocket. Als je dus de hele array tempEvents vol hebt staan met FD_ACCEPT, dan selecteer je elke keer een FD_ACCEPT event in hostSocket
Dat is waar (nog niet goed in gebouwd).
Alleen het zou niet moeten uit maken omdat er maar eentje wordt geaccept.
En daarna op FD_READ gezet. Maar goed ik zal mijn code op dat vlak verbeteren.

Maar klopt het nou dat als WS2_32.dll een send() krijgt van een socket, de event FD_READ wordt getriggerd? (en dus niet als laatste event 0 terug geeft)

Verwijderd

@Qlone:
dus het is niet
code:
1
2
3
4
5
6
7
8
server: maak socket s
server: maak event FD_READ voor socket s
server: wait
client: send("iets") naar server 
server: event is triggered op WSA_WAIT_EVENT_0
server: kijk wat de laatste event was (FD_READ)
server: ontvang
server: herhaal

?
Voor het ontvangen van data van een client wel, voor het versturen van data naar een client is het zoals ik beschreef.

Verwijderd

Topicstarter
mm, dan ga ik toch even mijn code regel voor regel door zoeken, ik ben iig blij dat ik nu toch nog wel op het goede pad zit.

Verwijderd

Topicstarter
Ja ik ben er eindelijk uit:
het zat hem in een index (iets wat ik al een tijdje dacht)
hoi hoi!

wat voor een waarde geeft
de return waarde van WSAWaitForMulitpleEvents()
de WSA_WAIT_EVENT_0

als de index van de getriggerde event = 0,1,2,4,9?

:D voor de rest: iedereen super veel bedankt voor jullie uitleg.

Verwijderd

Verwijderd schreef op 09 december 2003 @ 16:26:
Ja ik ben er eindelijk uit:
het zat hem in een index (iets wat ik al een tijdje dacht)
hoi hoi!

wat voor een waarde geeft
de return waarde van WSAWaitForMulitpleEvents()
de WSA_WAIT_EVENT_0

als de index van de getriggerde event = 0,1,2,4,9?

:D voor de rest: iedereen super veel bedankt voor jullie uitleg.
WSA_WAIT_EVENT_0
WSA_WAIT_EVENT_0 + 1
WSA_WAIT_EVENT_0 + 2
WSA_WAIT_EVENT_0 + 4
WSA_WAIT_EVENT_0 + 9

Verwijderd

Topicstarter
Jep daar zat toch echt de denk fout (een vrij stomme om het even te zeggen over mijzelf).

Het hele client-manage-systeem werkt echt relax nu (nog een paar kleine dingen er uit). Alleen nu loop ik tegen een ander (wazig) probleem aan (niet ehct een event probleem denk ik:
Als ik met de client informatie zend naar de server, dan ontvangt de server maar een woord (als de woorden gescheiden door spaties zijn)!

Edit: allerlei onbelangrijke code weg gehaald:
het was dus dit:

C:
1
2
3
char temp[1024];
cin >> ws
cin.getline(temp, 1024);

[ Voor 80% gewijzigd door Verwijderd op 10-12-2003 17:12 . Reden: Even wat weg gehaald ]


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 22-05 16:53
Er is ook een versie waarbij je whitespace characters mag opgeven

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Verwijderd

Topicstarter
Jep zie mijn laatste stuk code of bedoel je fgets()?

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 15:32

.oisyn

Moderator Devschuur®

Demotivational Speaker

Gebruik std::getline (), dat voorkomt potentiele bufferoverflows

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.

Pagina: 1