[C++] Socket 'flushen'?

Pagina: 1
Acties:

  • B-Man
  • Registratie: Februari 2000
  • Niet online
Ik ben bezig met een server die 'interactief' commandos afwerkt, die ontvangen worden van clients.
Het werkt interactief als in: op input volgt een reactie, waar de client op wacht.

Nu worden sommige reacties van de server niet verzonden (mogelijk is het TCP send window nog niet vol?), waardoor de client na een tijdje een timeout geeft (volgens spec, kan namelijk ook een echte timeout zijn), terwijl de server de reactie dus wel heeft 'verzonden' naar de socket.

Wat doe ik hieraan? Om met dit protocol te kunnen werken, moet ik er natuurlijk zeker van zijn dat de reactie gewoon verzonden wordt.

De enige manier die ik nu zie om dit op te lossen is het toevoegen van enkele karakters aan de reactie van de server, aangezien het lijkt alsof er dan een of andere drempelwaarde wordt overschreden (mogelijk is dit het TCP send window).

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
void SocketStream::write(const void *pBuffer, int nBytes)
{
    if(fd == -1)    THROW_EXCEPTION(IOException, InvalidFileDescriptor)
    
    const char *buffer = (char *)pBuffer;
    
    int nToSend = nBytes;
    while(nToSend > 0)
    {
        int sent = Socket::write(fd, buffer, nToSend);
        if(sent == -1)
        {
            bCanWrite = false;
            THROW_EXCEPTION(IOException, OSFileWriteError)
        }
        
        nToSend -= sent;
        buffer  += sent;
        
        if(nToSend > 0)
        {
            // We need to wait before we can send again (10 sec by default)
            struct timeval timeout;
            fd_set out;
            FD_ZERO(&out);
            FD_SET(fd, &out);
            
            printf("Cannot send yet, waiting 10 secs...\n");
            
            timeout.tv_sec  = 10000 / 1000;
            timeout.tv_usec = (10000 % 1000) * 1000;

            if(::select(1, 0, &out, 0, &timeout) == -1 && errno != EINTR)
                THROW_EXCEPTION(IOException, OSFileWriteError)
        }
    }
}


bovenstaande functie krijgt alle data als input, en ik krijg geen melding van een pauze, dus de data kan direct weg naar het OS.

  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
Het is niet nodig sockets te flushen, dus er gaat iets anders fout.
Test je op localhost of op twee verschillende hosts?
Bij twee hosts kun je eventueel met een network sniffer controleren wat er precies verzonden wordt.

  • B-Man
  • Registratie: Februari 2000
  • Niet online
Ik test op localhost.
De drempelwaarde lijkt niet constant te zijn, de ene keer worden reacties wel direct verzonden, en soms wordt er niets verzonden.

C++:
1
::send(fd, buffer, nToSend, 0);
(de functie die aangeroepen wordt door Socket::write (klasse ondersteunt POSIX en WIN32, vandaar een aparte klasse)

In beide gevallen geeft bovenstaande functie in ieder geval hetzelfde aantal terug als de invoer die ik erin stop. M.a.w.: het OS accepteert de data, maar verzend deze de ene keer wel, en de andere keer niet.

-- toevoeging:
Misschien handig om te weten dat ik deze code schrijf in Dev-CPP, en compileer op MinGW32 (GNU compiler versie 3.2).

[ Voor 12% gewijzigd door B-Man op 28-08-2004 22:00 ]


  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
Doe je daarna nog iets met de socket?

Voor POSIX en Windows heb je niet per se een aparte klasse nodig, met wat defines in een global header is het ook goed op te lossen.

  • B-Man
  • Registratie: Februari 2000
  • Niet online
Het gaat al mis bij de eerste 'greeting'. De server zend een string naar de client. Deze string (const char *) verzend ik m.b.v. bovenstaande functie naar de client, waarna ik de server laat wachten op een reactie.

Aangezien de client wacht op de greeting, en deze dus niet ontvangt, volgt een timeout.

M.a.w.: na de ::send() volgt een ::recv()

offtopic:
En inderdaad: ik gebruik geen aparte klasse maar een aparte namespace met wat inline functies (binnen wat #ifdefs) om POSIX/WIN32 mogelijk te maken.

[ Voor 22% gewijzigd door B-Man op 28-08-2004 22:26 ]


  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
Is de client code misschien fout dan?
Post die eens.

  • B-Man
  • Registratie: Februari 2000
  • Niet online
Ja, het zit denk ik in de client code. Ik gebruikte tot voorheen de GNU CommonC++ library, en aan de server kant had ik die al vervangen door eigen classes (wilde meer controle), maar nog niet aan de client kant. Aangezien ik de sources van CC++ ook heb, heb ik die goed doorgekeken, en deze doet precies wat ik anders zelf zou doen.

Om toch uit te sluiten dat het daar aan ligt, heb ik nu de klassen die ik zelf gemaakt heb ook in een simpele testclient gebruikt, en nu werkt het direct goed.
Blijkbaar doet de CC++ lib het toch net iets anders. Ik gebruikte de TCPStream klasse om te verbinden met de server, en achter de schermen doet deze ook gewoon een recv(). De CC++ klasse is echter wel een afgeleide van de std::iostream klasse, dus misschien dat de schoen daar wringt (ben niet bekend met de internals van de std:: klassen).

Overigens heb ik de CC++ socket klassen wel naar volle tevredenheid gebruikt, zonder hik of stoot. Tot dusver dan ;)

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Ik denk niet dat sockets je probleem zijn, maar iostream. Het idee van een iostream is dat je formatting meteen doet, maar dat je std::streambuf voorkomt dat elke << meteen I/O operatie is. Dit kun je expliciet controleren door je commando's met << std::endl af te sluiten, dat is een \n gevolgd door een flush.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • B-Man
  • Registratie: Februari 2000
  • Niet online
MSalters schreef op 28 augustus 2004 @ 23:54:
Ik denk niet dat sockets je probleem zijn, maar iostream. Het idee van een iostream is dat je formatting meteen doet, maar dat je std::streambuf voorkomt dat elke << meteen I/O operatie is. Dit kun je expliciet controleren door je commando's met << std::endl af te sluiten, dat is een \n gevolgd door een flush.
Ja, ik denk ook dat iostream mijn probleem veroorzaakt, maar hoe precies weet ik (nog) niet. Aangezien ik aan de zend kant nu direct ::send() aanroep, moet het probleem dus aan de ontvangst kant liggen. M.a.w.: iostream leest blijkbaar op verzoek niet direct de input buffer uit?
Pagina: 1