socket send() und recv() dynamisch machen?

B

blacky

Hallo,
ich hantiere ein wenig mit sockets und habe bereits erfolgreich mit TCP und UDP Daten versendet. :)) Leider stört mich, dass ich es bis jetzt beispielsweise bei einer TCP Verbindung nur geschafft habe, das der Client eine anfrage stellt und der Server diese beantwortet oder nur empfängt. Sprich, man hat immer den Ablauf Senden->Empfangen->Senden->Empfangen. Nun soll es aber dynamisch sein, sodass der Client z.B. zwei mal etwas sendet und so der Server auch zweimal etwas empfangen muss bzw. auch umgekehrt. Dies würde ja normalerweise bedeuten, dass es im Client zweimal "send()" und beim Server zwei mal "recv()" hintereinander gibt, aber das wäre ja wieder statisch. Ein Beispiel für das Verhalten welches ich programmieren will wäre ein Chat wo weder Server noch Client weiß wie oft empfangen bzw. gesendet werden muss. Ich habe bereits von "select()" gehört wo mehrere sockets und Clients überwacht werden, aber darauf will ich nicht hinaus. Ich möchte nur, dass der Server oder der Client so oft senden kann wie sie wollen. Ist dies mit "select()" doch möglich und wenn ja, wie? Ein paar Codebeispiele wären super.
Danke im voraus!
 
Hi,

prinzipiell wuerde ich dir empfehlen einfach mal ein Netzwerk tutorial durchzuarbeiten, oder dir mal den source code von einem kleinen Chat Programm oder so anzugucken. Das Thema ist einfach zu umfangreich, da kann man sich hier sonst die Finger zu wund schreiben, es gibt so viele verschiedene Moeglichkeiten und Techniken...

Aber ganz grundlegend, einen Server implementierst du haeufig, indem du eine Endlos-Schleife um select() schreibst, mit der du eingehende Verbindungen abfaengst, die dann in eigenen Threads weiterverarbeit werden. Das ist eine von vielen Moeglichkeiten. Select solltest du dir auf jeden Fall angucken.

Wenn dir blocking und non-blocking calls noch nichts sagen (bzw. synchrones vs. asynchrones Programmieren), solltest du dich auch da ein wenig einarbeiten.

Code-Beispiele gibt es im Netz zuhauf.

mfg,
bytepool
 
Zuletzt bearbeitet:
erster Erfolg :)

Hi, danke nochmal für die Antwort!
Nun habe ich das Verhalten erreicht, dazu habe ich mir einen mini UDP Chat angeschaut, der mit "select()" bestückt war. Ehrlich gesagt habe ich den Code teilweise übernommen, darum bitte ich um Erklärung warum es "FD_SET(0, &fdsets)" gibt? Zudem wären noch Verbesserungsvorschläge super. Außerdem muss ich hierzu noch sagen, obwohl es wie ein Chat ausschaut möchte ich darauf nicht hinaus, sondern ich wollte mich mit dem Server verbinden und diesem Befehle geben und wenn sich ein Status im Server ändert soll er sich beim Client melden.
Hier der komplette funktionierende Code:
Code:
#include <stdio.h>
#include <stdlib.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/fcntl.h>
#include <sys/time.h>

/*
 * 
 */

void handler(int c)
{
    fd_set fdsets;
    char buffer[1024];
    int max, bytes;
    while(1)
    {
       FD_ZERO(&fdsets);
       FD_SET(c, &fdsets);
       FD_SET(0, &fdsets);   //<- dies will ich wissen
       max = c;
       select(max + 1, &fdsets, NULL, NULL, NULL);
       if (FD_ISSET(c, &fdsets))
       {
           bytes = recv(c, buffer, sizeof(buffer)-1,0);
           buffer[bytes] = '\0';
           printf("Received: %s\n", buffer);
       }
       else if (FD_ISSET(0, &fdsets))
       {
           fgets(buffer, 1024, stdin);
           send(c, buffer, strlen(buffer),0);
       }
    }
}

int main(int argc, char** argv) {

    int client, sock, cli_size;
    struct sockaddr_in srv, cli;

    if ( (sock = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
    {
        printf("Error creating socket");
        return -1;
    }

    srv.sin_addr.s_addr = INADDR_ANY;
    srv.sin_family = AF_INET;
    srv.sin_port = htons(9999);

    if ((bind(sock, (struct sockaddr*) &srv, sizeof(srv)))  < 0 )
    {
        printf("Unable to bind the socket");
        return -1;
    }

    if ((listen(sock, 3)) <0)
    {
        printf("Unable to start listening");
        return -1;
    }
    cli_size = sizeof(cli);
    client = accept(sock, (struct sockaddr*) &cli, &cli_size);

    handler(client);

    return (EXIT_SUCCESS);
}

Der Server läuft nun so, dass man sich mit telnet verbinden kann und von telnet sowie von dem Server selbst aus Nachrichten schreibt.
 
Hi,

ein kurzes googlen schickte mich auf diese Seite:
http://linux.die.net/man/3/fd_set

Dort steht u.a. dass fd 0 der file descriptor fuer stdin ist, womit sich der Code doch eigentlich selbst erklaert.

Select lauscht auf stdin (fd 0) und auf dem gegebenen Socket (fd c), und wenn von einem der beiden gelesen werden kann, werden beide Faelle der Reihe nach abgearbeitet. Wenn etwas vom Socket gelesen werden kann, wird es gelesen und auf stdout ausgegeben, und wenn etwas auf stdin geschrieben wurde, wird das gelesen und ueber die Verbindung fd c geschickt.

Das einzige was mir auf Anhieb auffaellt, ist dass ich den file descriptor in handler() etwas aussagekraeftiger als "c" benennen wuerde. Ansonsten finde ich den Code eigentlich angenehm einfach und gut zu lesen. Aber ich bin weder ein C noch ein Netzwerk Wizard. ;)

Edit:
Naja, in deinem Beispiel Code fehlen natuerlich noch eine Menge Kontrollen, z.B. wirst du so Daten verlieren, wenn nach einem Durchlauf von select mehr als 1024 byte auf dem Socket angekommen sein sollten. <edit2> Ach nee, nicht unbedingt, der Rest wird dann mit etwas Glueck im naechsten select Durchlauf gelesen. </edit2> Da muesste dann noch einmal eine Schleife her. Aber wie das genau gehandhabt werden muss, haengt natuerlich auf von den Daten ab die du da rueber schickst, und ob es denn jetzt UDP oder TCP ist.

Du darfst dich z.B. auch nicht darauf verlassen, dass byte chunks die du auf dem socket server verschickst, als einzelne chunks auf der anderen Seite wieder rauskommen. Es ist durchaus moeglich das 2 Pakete in einem select Durchlauf ankommen. Es gibt ein paar von diesen Kleinigkeiten auf die man achten muss, aber das haengt wie gesagt auch vom konkreten Programm ab.

mfg,
bytepool
 
Zuletzt bearbeitet:

Ähnliche Themen

Server und Client für TCP und UDP

"send: Cannot determine peer address" nach Timeout mit UDP Server -Perl Socket eval{}

tcp keepalive (jabber session disconnects)

wirre zeichen :(

Per Sockets ins IRC

Zurück
Oben