Ниже приведена программа простейшего Web-сервера, из которого выброшено всё, что не относится к проблеме.
А проблема состоит в том, что браузер не получает ответа на запрос. Ответ формируется операторами, обозначенными в нижеследующем коде (функция process_request) как "проблемная группа".
Telnet при этом ответ получает.
Если не читать запроса или поставть операторы проблемной группы перед чтением запроса - браузер получает ответ.
В чём дело?
Проблему я решил, прочитав запрос в предке до fork, а не в потомке, как это делается в программе ниже. Но всё равно непонятно.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netdb.h>
#include <time.h>
int make_server_socket()
{
struct sockaddr_in saddr;
int sock_id=sock_id = socket(AF_INET,SOCK_STREAM,0);
bzero((void*)&saddr,sizeof(saddr));
if(INADDR_ANY) // проверка для тех систем, где эта константа не ноль
saddr.sin_addr.s_addr = htonl(INADDR_ANY); // обработка вне зав-ти от номера смаого сервера
saddr.sin_port = htons(12345);
saddr.sin_family = AF_INET;
if(bind(sock_id,(struct sockaddr*)&saddr,sizeof(saddr)) != 0) return -1;
if(listen(sock_id,1) !=0) return -1;
return sock_id;
}
void process_request(int fd)
{
FILE*fp=fdopen(fd,"r+");
char request[BUFSIZ+1];
fgets(request,BUFSIZ,fp);
/*------------ проблемная группа --------------------*/
fprintf(fp, "HTTP/1.0 Error\r\n");
fprintf(fp, "Content-Type: text/html\r\n");
fprintf(fp, "Connection: close\r\n\r\n");
fprintf(fp, "404 ERROR\r\n");
fclose(fp);
}
int main(int argc,char *argv[])
{
int sock,fd;
sock = make_server_socket();
while(1) {
fd = accept(sock,NULL,NULL);
if(fork()==0) {
process_request(fd);
exit(0);
}
close(fd);
}
close(sock);
}
> /*------------ проблемная группа --------------------*/
> fprintf(fp, "HTTP/1.0 Error\r\n");Попробуйте отдавать нормальных HTTP заголовок.
fprintf(fp, "HTTP/1.0 404\r\n");
>> /*------------ проблемная группа --------------------*/
>> fprintf(fp, "HTTP/1.0 Error\r\n");
> Попробуйте отдавать нормальных HTTP заголовок.
> fprintf(fp, "HTTP/1.0 404\r\n");Нет, проблема не в этом. Ведь если передать этот неправильный заголовок и всё остальное ДО fgets - всё будет нормально - браузер выдаст страницу. Похоже, здесь какие то тонкости работы с потоками у потомков. Использование read и write не приводит к проблемам!
а у меня ваш пример (правил только инклюды под систему) и так работает.
Попробуйте так:
fflush(fp);
fclose(fp);
close(fd);
> а у меня ваш пример (правил только инклюды под систему) и так
> работает.
> Попробуйте так:
> fflush(fp);
> fclose(fp);
> close(fd);Попробовал. Увы. И, вроде, последний close не нужен, так как есть fclose. Но всё одинаково. Конечно, можно было бы грешить на браузер, но не работают оба: Firefox и Opera. И опять же telnet-то получает ответ.
Тот же результат на почти другой ОС: Fedora 8 и Fedora 14.
Попробуйте использовать анализатор пакетов wireshark, чтобы посмотреть что реально передается по сети...> Тот же результат на почти другой ОС: Fedora 8 и Fedora 14.
> Попробуйте использовать анализатор пакетов wireshark, чтобы посмотреть что реально передается
> по сети...
>> Тот же результат на почти другой ОС: Fedora 8 и Fedora 14.Попробовал, хотя я в этом не сильно понимаю. Результат такой.
При "нормальной" работе идёт 8 пакетов
1. SYN
2. SYN
3. ACK
4. GET /file HTTP/1.1
5. ACK
6. HTTP/1.0 Error
7. ACK
8. RSTПри втором "нормальном" запуске получается 16 пакетов
1. SYN
2. SYN
3. ACK
4. HTTP/1.0 Error
5. ACK
6. FIN
7. GET /file HTTP/1.1
8. RST
9. SYN
10. SYN
11. ACK
12. HTTP/1.0 Error
13. ACK
14. FIN
15. GET /favicon.ico HTTP/1.1
16. RSTТретий запуск аналогичен первому
При "ненормальной" работе тоже сначала 8 пакетов
1. SYN
2. SYN
3. ACK
4. GET /file HTTP/1.1
5. ACK
6. FIN
7. FIN
8. ACKПотом вываливается сразу 42 пакета, среди которых 5 подряд запросов GET, и также без пакета HTTP/1.0 Error.
Такая картина почти всегда: я всегда видел создание 5 подряд потомков с одним и тем же запросом. Я объяснял это тем, что браузер пытается получить ответ перепосылая запрос.Другая картина - при работе с read-write
Первый запрос дал 16 пакетов1. SYN
2. SYN
3. ACK
4. GET /file HTTP/1.1
5. ACK
6. HTTP/1.0 Error
7. ACK
8. Continuation or on-HTTP Traffic
9. ACK
10. Continuation or on-HTTP Traffic
11. ACK
12. Continuation or on-HTTP Traffic
13. ACK
14. FIN
15. FIN
16. ACKКак это понимать? Буду вспоминать ТСР.
> Попробуйте использовать анализатор пакетов wireshark, чтобы посмотреть что реально передается
> по сети...
>> Тот же результат на почти другой ОС: Fedora 8 и Fedora 14.Проблема, видимо, проста
Надо считывать запрос до конца, а у меня только один fgets торчит.
С telnet-ом запрос считывался за один раз. Wireshark все таки помог.
> Нет, проблема не в этом. Ведь если передать этот неправильный заголовок и
> всё остальное ДО fgets - всё будет нормально - браузер выдаст
> страницу. Похоже, здесь какие то тонкости работы с потоками у потомков.
> Использование read и write не приводит к проблемам!А чем отличается fread от read, помните?
>> Нет, проблема не в этом. Ведь если передать этот неправильный заголовок и
>> всё остальное ДО fgets - всё будет нормально - браузер выдаст
>> страницу. Похоже, здесь какие то тонкости работы с потоками у потомков.
>> Использование read и write не приводит к проблемам!
> А чем отличается fread от read, помните?Буферизованный ввод-вывод. Но fread я не пробовал. Прекрасно работает и с fgets, если его сделать в предке, а в потомке уже писать в стандартный вывод. Проблема возникает если fgets выполнять в потомке. А read тоже работает как то мне непонятно, судя по картине пакетов.