Ошибка «Недопустимый поиск» при работе с потоками сокетов с не-пустыми буферами чтения

Сейчас я пишу серверное приложение на Linux x86_64, используя . После принятия соединения через accept()я использую fdopen(), чтобы обернуть полученный сокет в поток FILE*.

Запись и чтение из этого FILE*потока обычно работает довольно хорошо, но сокет становится непригодным для использования, как только я пишу в него, когда он имеет не-пустой буфер чтения.

В демонстрационных целях я написал некоторый код, который прослушивает соединение, а затем считывает ввод, строка за строкой, в буфер чтения, используя fgetc(). Если строка слишком длинная, чтобы поместиться в буфер, она не считывается полностью, а считывается во время следующей итерации.

#include 
#include 
#include 
#include 
#include 

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");
}

Код должен компилироваться в gcc без каких-либо специальных параметров. Запустите его с номером порта в качестве аргумента и используйте netcat для локального подключения к нему.

Теперь, если вы попробуете отправить строку короче 8 символов, это будет работать безупречно. Но если вы отправите строку, содержащую более 10 символов, программа завершится ошибкой. Этот образец ввода:

ab
cd
abcdefghij

создаст этот вывод:

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

Как видите, (правильно)читаются только первые 8 символов abcdefgh, но когда программа пытается отправить строку 'ack' (, которая клиент никогда не получает ), а затем очищает выходной буфер, мы получаем ошибку Illegal seek, а следующий вызов fgetc()возвращает EOF.

Если часть fflush()закомментирована, та же ошибка возникает, но строка

fflush failed: Illegal seek

отсутствует в выводе сервера.

Если часть fputs(ack)закомментирована, кажется, что все работает как задумано, но perror(), вызываемый вручную из gdb, по-прежнему сообщает об ошибке «Недопустимый поиск».

Если оба fputs(ack)и fflush()закомментированы,все работает как задумано.

К сожалению, я не смог найти ни хорошей документации, ни обсуждений этой проблемы в Интернете, поэтому буду очень признателен за вашу помощь.

редактировать

Решение, на котором я окончательно остановился, состоит в том, чтобы не использовать fdopen()и FILE*, поскольку, похоже, не существует чистого способа преобразования сокета fd в FILE*, который может надежно использоваться в режиме r+. Вместо этого я работал непосредственно над сокетом fd, написав собственный код замены для fputsи fprintf.

Если кому надо, вот код .

6
задан mic_e 22 April 2012 в 11:13
поделиться