11.12 Standard-E/A-Funktionen verwenden
Auch wenn es vielleicht den Anschein macht, man könne Sockets nur in Verbindung mit elementaren E/A-Funktionen verwenden, so täuscht dies. Selbstverständlich lassen sich auch Standard-E/A-Funktionen wie fscanf() oder fprintf() bei den Sockets verwenden. Hierbei genügt es, sich einen FILE-Zeiger mit der Funktion fdopen() zu einem Socket-Deskriptor zur Verfügung stellen zu lassen.
Wollen Sie einen FILE-Zeiger zum Lesen und Schreiben öffnen, sei dazu geraten, zwei getrennte FILE-Zeiger zu verwenden. Der Grund ist einfach, wenn Sie nur einen FILE-Zeiger verwenden (z. B. mit "r+" geöffnet), muss nach jeder Ausgabeoperation eine Funktion wie fflush(), fseek(), fsetpos() oder rewind() aufgerufen werden, bevor eine Eingabeoperation ausgeführt wird. Umgekehrt ist dies dasselbe. Das Problem an diesen Funktionen ist, dass diese wiederum auf die elementare Funktion lseek() zurückgreifen. Aber es wurde bereits erwähnt, dass es nicht gestattet ist, lseek() auf Socket-Deskriptoren zu verwenden.
Daher sollten Sie für das Lesen und für das Schreiben jeweils einen eigenen FILE-Zeiger verwenden. Hierzu ist es allerdings notwendig, den Socket-Deskriptor mit dup() zu duplizieren, da man mit unterschiedlichen Streams auch unterschiedliche Deskriptoren verwenden sollte. Ein weiterer Vorteil vom Duplizieren ist, dass Sie somit jederzeit nur einen der FILE-Zeiger schließen können, was ohne zu duplizieren nicht möglich ist.
int sockfd;
FILE *lese_fz;
FILE *schreib_fz;
sockfd = socket(PF_INET, SOCK_STREAM, 0);
/* FILE-Zeiger für das Schreiben */
schreib_fz = fdopen(sockfd, "w");
if( schreib_fz == NULL ) {
/* Fehler .... */
}
/* FILE-Zeiger für das Lesen */
lese_fz =fdopen(dup(sockfd), "r");
if( lese_fz == NULL ) {
/* Fehler ... */
}
Wollen Sie jetzt z. B. den FILE-Stream der Leseseite schließen, können Sie dies wie folgt machen:
fclose(lese_fz);
Ähnlich verläuft dies auch, wenn Sie die Schreibseite schließen wollen, nur sollten Sie hierbei zuvor noch fflush() aufrufen, womit alle noch im Puffer befindlichen Daten geschrieben werden:
fflush(schreib_fz);
fclose(schreib_fz);
Sofern Sie hierbei mit fork() einen Kindprozess erzeugt haben und sich nicht sicher sind, ob ein anderer Prozess hierbei noch auf einen offenen Socket-Deskriptor zugreift, sollten Sie außerdem noch zusätzlich beim Schließen die Funktion shutdown() verwenden. Beim Schließen der Leseseite wäre dies:
shutdown( fileno(lese_fz), SHUT_RD );
fclose(lese_fz);
Und beim Schließen der Schreibseite:
fflush(schreib_fz);
shutdown( fileno(schreib_fz), SHUT_RDWR);
fclose(schreib_fz);
11.12.1 Pufferung von Standard-E/A-Funktionen
Natürlich muss man sich bei der Verwendung von Standard-E/A-Funktionen auch wieder Gedanken um die Pufferung der Daten machen. Denn wenn hier die Zeilen- bzw. Vollpufferung eingestellt ist, wird immer erst in das Socket geschrieben, wenn ein Newline-Zeichen vorkommt oder der Puffer voll ist. Besonders wenn Sie etwas in den Puffer schreiben, hat sich ein anschließendes fflush() schon immer bewährt, so dass die Daten im Puffer sofort geschrieben wurden. Natürlich können Sie hier auch die Pufferung des Streams mit den Funktionen setbuf() bzw. setvbuf() verändern.
|