"Illegal seek" Fehler beim arbeiten mit socket-streams mit nicht-leerem Puffer Lesen
Schreibe ich momentan eine server-Anwendung auf Linux x86_64
mit <sys/socket.h>
.
Nach der Annahme einer Verbindung über accept()
verwende ich fdopen()
wickeln abgerufen Buchse in eine FILE*
stream.
Schreiben in und Lesen aus, dass FILE*
stream in der Regel ganz gut funktioniert, aber die Buchse wird unsusable sobald ich schreiben, es hat einen nicht-leeren Puffer Lesen.
Zur demonstration, die ich geschrieben habe, einige code, der wartet auf eine Verbindung, dann liest die Eingabe zeilenweise in einen Puffer Lesen mit fgetc()
. Wenn die Zeile zu lang und passt nicht in den Puffer, ist es nicht ganz Lesen, sondern Lesen Sie während der nächsten iteration.
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
FILE* listen_on_port(unsigned short port) {
int sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in name;
name.sin_family = AF_INET;
name.sin_port = htons(port);
name.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sock, (struct sockaddr*) &name, sizeof(name)) < 0)
perror("bind failed");
listen(sock, 5);
int newsock = accept(sock, 0, 0);
return fdopen(newsock, "r+");
}
int main(int argc, char** argv) {
int bufsize = 8;
char buf[9];
buf[8] = 0; //ensure null termination
int data;
int size;
//listen on the port specified in argv[1]
FILE* sock = listen_on_port(atoi(argv[1]));
puts("New connection incoming");
while(1) {
//read a single line
for(size = 0; size < bufsize; size++) {
data = fgetc(sock);
if(data == EOF)
break;
if(data == '\n') {
buf[size] = 0;
break;
}
buf[size] = (char) data;
}
//check if the read failed due to an EOF
if(data == EOF) {
perror("EOF: Connection reset by peer");
break;
} else {
printf("Input line: '%s'\n", buf);
}
//try to write ack
if(fputs("ack\n", sock) == EOF)
perror("sending 'ack' failed");
//try to flush
if(fflush(sock) == EOF)
perror("fflush failed");
}
puts("Connection closed");
}
Sollte der code kompilieren des gcc ohne Besondere Parameter. Führen Sie es mit der port-Nummer als argument und netcat verwenden, um eine Verbindung herzustellen lokal.
Nun, wenn Sie versuchen, senden von strings, die kürzer sind als 8 Zeichen, diese laufen einwandfrei.
Aber wenn Sie senden Sie eine Zeichenfolge mit mehr als 10 Zeichen, wird das Programm scheitern.
Dieses Beispiel für die Eingabe:
ab
cd
abcdefghij
Erstellen dieser Ausgabe:
New connection incoming
Input line: 'ab'
Input line: 'cd'
Input line: 'abcdefgh'
fflush failed: Illegal seek
EOF: Connection reset by peer: Illegal seek
Connection closed
Wie Sie sehen, (zu Recht) nur die ersten 8 Zeichen abcdefgh gelesen, aber wenn das Programm versucht zu senden, die 'ack' - saite (die der client nie receves), und dann Spülen Sie die Ausgabe-Puffer, so erhalten wir eine Illegal seek
Fehler, und beim nächsten Aufruf fgetc()
wird EOF zurückgegeben.
Wenn die fflush()
Teil auskommentiert ist, immer der selbe Fehler Auftritt, aber der
fflush failed: Illegal seek
Linie fehlt von der server-Ausgabe.
Wenn die fputs(ack)
Teil auskommentiert ist, alles scheint zu funktionieren wie beabsichtigt, aber eine perror() manuell aufgerufen gdb noch Berichte eine "Illegal seek" Fehler.
Wenn beide fputs(ack)
und fflush()
sind auskommentiert, alles hat so funktionieren wie Sie sollen.
Leider habe ich nicht in der Lage zu finden, eine gute Dokumentation, noch irgendwelchen Internet-Diskussionen über dieses problem, so Ihre Hilfe wäre sehr geschätzt werden.
Bearbeiten
Die Lösung, die ich schließlich entschied sich für ist zu nicht Verwendung fdopen()
und FILE*
, da scheint es keine "saubere" Möglichkeit der Umwandlung von a-Buchse fd in einen FILE*
zuverlässig eingesetzt r+
- Modus.
Stattdessen habe ich direkt gearbeitet, die auf der socket fd, Schreibe meine eigenen Ersatz-code für fputs
und fprintf
.
Wenn jemand braucht es, hier ist der code.
InformationsquelleAutor mic_e | 2012-04-21
Du musst angemeldet sein, um einen Kommentar abzugeben.
Deutlich, "r+" (Lesen/schreiben) - Modus funktioniert nicht auf Steckdosen in dieser Implementierung keine Zweifel, da der zugrunde liegende code wird davon ausgegangen, dass es muss ein suchen zum Umschalten zwischen Lesen und schreiben. Dies ist der Allgemeine Fall mit stdio-streams (d.h., Sie müssen tun, irgendeine Art von Synchronisation-Betrieb), weil wieder in der Trüben Zeit, die tatsächliche stdio-Implementierungen hatte nur einen einzigen Zähler pro stream, und es war entweder ein Zähler für die "Anzahl der Zeichen nach Links zu Lesen aus stream buffer über
getc
makro" (Lesen-Modus) oder "Anzahl der Zeichen, kann sicher geschrieben werden, um stream-Puffer überputc
makro (im schreib-Modus). Dazu, dass einzelne Zähler re-set, Sie hatte zu tun eine suchen-Typ Betrieb.Sucht, sind nicht zugelassen pipes und sockets (seit "Datei-offset" ist nicht aussagekräftig).
Einer Lösung ist nicht zu wrap-a-Buchse mit stdio überhaupt. Ein weiterer, wahrscheinlich einfacher /besser für Ihre Zwecke, ist, wickeln Sie es mit, nicht eine, sondern zwei stdio streams:
Gibt es ein weiteres Manko hier, obwohl, weil, wenn Sie gehen, um
fclose
einem stream, das schließt die anderen Datei-Deskriptor. Zu umgehen, müssen Siedup
den socket-Deskriptor einmal (in einem der beiden Aufrufe vor, dann ist es egal, welche).Wenn Sie beabsichtigen, Sie zu verwenden
select
oderpoll
oder ähnliches auf die Buchse an einem gewissen Punkt, sollten Sie in der Regel gehen für die "nicht-wrap mit stdio" - Lösung, da es keine schöne, saubere, tragbare Weg, um track stdio buffering. (Es gibt von der Implementierung spezifischer Weise).FILE* streams
vor allem ein singleFILE* stream
? Vor dem wickeln der socket-Deskriptor in einemFILE* stream
hatte ich eigene Wrapper fürprintf()
undputs()
, aber ich fand es hässlich. Nehme an, es ist die 'saubere' Lösung, nachdem alle.Ja. Möglicherweise ironisch, Ihr "im Grunde
asprintf
" code ist genau das, was ich im Sinn hatte, als ich definiert den Rückgabewert dersnprintf
wie ich es Tat. 🙂InformationsquelleAutor torek
Nicht verwenden
fflush()
auf Netzwerk-sockets. Sie sind unbuffered-streams.Auch dieser code:
nicht Lesen einer einzigen Zeile. Es liest nur bis zu der Puffer-Größe, die Sie definiert als 8.
sock
noch Daten für Sie zu erhalten, die Sie erhalten sollten, vor schreiben in den stream mitfputs
. BTW kann man ersetzen, einen ganzen block mitfgets()
aufhören zu Lesen am Zeilenumbruch?Ja,
fgets()
liest nicht mehr alsn-1
Zeichen (weil dien
th, oderbufsize
th in diesem Fall ist reserviert für das abschließende null-byte), sondern Stoppt an der ersten newline, wenn ein Auftritt vor dem ausführen von out of space.Einfach Entleerung der lese-Puffer vor schreiben würde, natürlich, dieses problem zu lösen. Aber was ist, wenn die Verarbeitung der Benutzereingabe nimmt erhebliche Zeit, während der die neuen Nachrichten kommen und der Puffer wird wieder gefüllt, und nach der Verarbeitung fertig ist, schicke ich eine Antwort, unwissend der Tatsache, dass in der Zwischenzeit neue Daten da sind? Ich bekommen würde eine andere
Illegal seek
Fehler.Eigentlich zur socket-I/O könnten Sie besser dran, mit dem POSIX -
read()
undwrite()
Funktionen eher alsfget*
undfput*
. Sie sind besser geeignet, um Ungepufferte streams und ich denke, dass wird Ihnen erlauben, zu Antworten, während es eingehende Daten auf den Netzwerk-stack.InformationsquelleAutor smocking
Ja, Sie können verwenden Sie eine Datei-stream, um Ihre socket, zumindest auf Linux.
Sie sollten aber vorsichtig sein: Sie müssen nur verwenden, ferror() um auf Fehler zu testen. Ich habe einige code, verwenden Sie diese und laufen einwandfrei in der Produktion auf eine große französische Website.
Wenn Sie errno oder perror() Sie fangen irgendwelche internen Fehler, dass der stream begegnen, auch wenn Sie es verbergen will, Sie. Und "Illegal seek" ist einer von Ihnen.
Auch, um zu testen, real EOF Bedingungen, die Sie verwenden sollten, feof(), da bei der Rückgabe true, es ist gegenseitig mit ferror() gibt einen nicht-null-Wert. Es ist, weil, wenn mit fgetc() Sie haben keine bedeuten zu differenzieren Fehler von real EOF Bedingungen. So sollten Sie wahrscheinlich besser die Verwendung der Funktion fgets() als ein anderer Benutzer darauf hingewiesen.
So, Ihr test:
Sollte wie folgt geschrieben werden:
InformationsquelleAutor Bunch
Versuchen Sie dies :
InformationsquelleAutor bill