2014-11-21 4 views
3

Я пишу веб-сервер HTTP, когда я отправляю текстовый файл с эквивалентным содержимым файла HTML в браузер, браузер показывает его правильно, но когда я отправляю сам браузер HTML-файла показывает страницу HTML на секунду, а затем появляется сообщение об ошибке «соединение было сброшено».Отправка html-файла с HTTP-протоколом через tcp и браузера показывает ошибку

Я заметил, что текстовый файл больше, чем HTML файл, но я понятия не имею, почему

размер шрифта = 286 байт

HTML размер = 142 байт

и это HTML код:

<!DOCTYPE html> 
<html> 
<body> 

<p>This is a paragraph.</p> 
<p>This is a paragraph.</p> 
<p>This is a paragraph.</p> 

</body> 
</html> 

это мой код:

char sendBuffer[500]; 

FILE *sendFile = fopen("foo.html", "r"); 
fseek(sendFile, 0L, SEEK_END); 
int sz = ftell(sendFile); 
fseek(sendFile, 0L, SEEK_SET); 

string s1; 
s1="HTTP/1.1 200 OK\nContent-length: " + to_string(sz) + "\n"; 
std::vector<char> writable(s1.begin(), s1.end()); 
writable.push_back('\0'); 

strcpy(sendBuffer,(const char *)&writable[0]); 
int c=send(connected,(const char*)&sendBuffer,strlen(&writable[0]),0); 
printf("\nSent : %s\n",sendBuffer); 
strcpy(sendBuffer,"Content-Type: text/html\n\n"); 
c=send(connected,(const char*)&sendBuffer,strlen("Content-Type: text/html\n\n"),0); 
printf("\nSent : %s\n",sendBuffer); 

char send_buffer[300]; 

while(!feof(sendFile)) 
{ 
    int numread = fread(send_buffer, sizeof(unsigned char), 300, sendFile); 
    if(numread < 1) break; // EOF or error 

    char *send_buffer_ptr = send_buffer; 
    do { 
     int numsent = send(connected, send_buffer_ptr, numread, 0); 
     if(numsent < 1) // 0 if disconnected, otherwise error 
     { 
      if(numsent < 0) { 
       if(WSAGetLastError() == WSAEWOULDBLOCK) 
       { 
        fd_set wfd; 
        FD_ZERO(&wfd); 
        FD_SET(connected, &wfd); 

        timeval tm; 
        tm.tv_sec = 10; 
        tm.tv_usec = 0; 

        if(select(0, NULL, &wfd, NULL, &tm) > 0) 
         continue; 
       } 
      } 

     break; // timeout or error 
    } 

    send_buffer_ptr += numsent; 
    numread -= numsent; 
} 
while(numread > 0); 
} 

Вот другая часть кода, который используется непосредственно перед выше код:

int sock, connected, bytes_recieved , _true = 1 , portNumber; 
char send_data [1024] , recv_data[1024];  
struct sockaddr_in server_addr,client_addr; 
int sin_size; 

time_t t = time(NULL); 
struct tm tm = *localtime(&t); 
char date[50]; 

if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) 
{ 
    perror("Unable to create the Socket"); 
    exit(1); 
} 

if (setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(const char*)&_true,sizeof(int)) == -1) { 
    perror("Unable to Setsockopt"); 
    exit(1); 
} 
char *server_address="127.1.1.1"; 
portNumber=8080; 
server_addr.sin_family = AF_INET; 
server_addr.sin_port = htons(portNumber); 
server_addr.sin_addr.s_addr = inet_addr("127.1.1.1");//inet_pton(AF_INET,"127.0.0.1",&server_addr.sin_addr);//INADDR_ANY; 

string host=server_address+':'+to_string(portNumber); 


memset(&(server_addr.sin_zero),0,8);//sockaddr_in zero padding is needed 
if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr))==-1) //bind the socket to a local address 
{ 
    perror("Unable to bind"); 
    exit(1); 
} 

if (listen(sock, 5) == -1) //listen to the socket with the specified waiting queue size 
{ 
    perror(" Listen"); 
    exit(1); 
} 

cout << "MyHTTPServer waiting on port 8080" << endl; 
fflush(stdout); 

sin_size = sizeof(struct sockaddr_in); 
connected = accept(sock, (struct sockaddr *)&client_addr,&sin_size); 

cout<< "I got a connection from (" << inet_ntoa(client_addr.sin_addr) << "," << ntohs(client_addr.sin_port) << ')' << endl; 
+0

Проголосовало против смещения приводом вниз. Также вопрос представляется разумным. –

+2

HTTP требует «\ r \ n» строк, «\ n» недостаточно. Если это не решит вашу проблему, попробуйте придумать минимальный * полный * пример. – Phillip

+0

@Phillip Вы правы, но я видел, что реализации корректно работают только с '\ n'. И поскольку я сказал, что он работает правильно, когда он находится в текстовом формате. Проблема заключается в формате 'HTML'. Вы знаете, почему размер' HTML' и текстовых файлов отличается? –

ответ

1

У вас есть две важные проблемы, которые я могу видеть

  1. Вы передаете send параметры при неправильно, это линия (очень важно)

    int c=send(connected,(const char*)&sendBuffer,strlen(&writable[0]),0); 
    

    должен быть

    int c=send(connected,(const char*) sendBuffer,strlen(&writable[0]),0); 
    /*        ^
    *        No ampersand 
    */ 
    

    поскольку массив sendBuffer распадается на указатель, и вам это не нужно.

  2. Вы передаете первый параметр select тоже неправильно из ручного

    nfds является наибольшим номером дескриптора файла в любом из трех наборов, плюс 1

    так в вашем случае это должно быть

    if (select(connected + 1, NULL, &wfd, NULL, &tm) > 0) 
    

    и вы используете его после того, как вы звоните send вы должны вызвать его перед тем, чтобы увидеть, можно ли записать в дескриптор файла.

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

string  text; 
stringstream stream; 

FILE *sendFile = fopen("foo.html", "r"); 
if (sendFile == NULL) /* check it the file was opened */ 
    return; 

fseek(sendFile, 0L, SEEK_END); 
/* you can use a stringstream, it's cleaner */ 
stream << "HTTP/1.1 200 OK\nContent-length: " << ftell(sendFile) << "\n"; 
fseek(sendFile, 0L, SEEK_SET); 

text = stream.str(); 
/* you don't need a vector and strcpy to a char array, just call the .c_str() member 
* of the string class and the .length() member for it's length 
*/ 
send(connected, text.c_str(), text.length(), 0); 

std::cout << "Sent : " << text << std::endl; 

text = "Content-Type: text/html\n\n"; 
send(connected, text.c_str(), text.length(), 0); 

std::cout << "Sent : %s" << text << std::endl; 
while (feof(sendFile) == 0) 
{ 
    int numread; 
    char sendBuffer[500]; 

    numread = fread(sendBuffer, sizeof(unsigned char), 300, sendFile); 
    if (numread > 0) 
    { 
     char *sendBuffer_ptr; 

     sendBuffer_ptr = sendBuffer; 
     do { 
      fd_set wfd; 
      timeval tm; 

      FD_ZERO(&wfd); 
      FD_SET(connected, &wfd); 

      tm.tv_sec = 10; 
      tm.tv_usec = 0; 
      /* first call select, and if the descriptor is writeable, call send */ 
      if (select(1 + connected, NULL, &wfd, NULL, &tm) > 0) 
      { 
       int numsent; 

       numsent = send(connected, sendBuffer_ptr, numread, 0); 
       if (numsent == -1) 
        return; 
       sendBuffer_ptr += numsent; 
       numread  -= numsent; 
      } 
     } while (numread > 0); 
    } 
} 
/* don't forget to close the file. */ 
fclose(sendFile); 
+0

Я применил упомянутые вами изменения, но проблема с файлом 'HTML' все еще существует. Файлы' text' отправляются без каких-либо проблем. Вы можете протестировать код для себя, чтобы увидеть результат в браузере. –

+0

@AliNfr Я проверил, все еще неясно, что вы имеете в виду, когда говорите _the text file_ ?, вы имеете в виду, что вы отправляете текст напрямую? –

+0

Как я уже говорил в вопросе сохранения файла 'HTML' как' .txt', например 'foo.txt', файл отправляется без каких-либо проблем и отображается правильно в браузере. Я пытаюсь написать' HTTP' на языке 'c', но такие проблемы заставляли меня использовать python, при написании сервера на python у меня не было такой проблемы, и теперь мой проект завершен. –

1

Половину способ ответа. Прежде всего, даже если «Использование \n работ» нарушает стандарт. Нужно использовать CRLF. Используйте CRLF. Период.

Для остальной части кода. Я сомневаюсь, что это изменит многие вещи, но я бы немного переструктурировал код. Это много происходит в функции отправки.

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

Только в качестве грубого начала.

int send_data(int soc, const char *buf, size_t len) 
{ 
    ssize_t sent; 

    do { 

     /* Use iharob code or similar here */ 
     /* Return something <> 0 on error. */ 

     sent = send(soc, buf, len, 0); 

     buf += sent; 
     len -= sent; 
    } while (len > 0); 

    return 0; 
} 

int send_file(int soc, const char *fn) 
{ 
    char buf[500]; 
    FILE *fh; 
    long sz; 
    size_t len; 
    int err = 0; 

    if (!(fh = fopen(fn, "r"))) { 
     perror("fopen"); 
     return 1; 
    } 
    fseek(fh, 0L, SEEK_END); 
    sz = ftell(fh); 
    fseek(fh, 0L, SEEK_SET); 

    /* Consider adding Date + Server here. */ 
    len = sprintf(buf, 
      "HTTP/1.1 200 OK\r\n" 
      "Content-length: %ld\r\n" 
      "Content-Type: text/html\r\n" 
      "Server: FooBar/0.0.1\r\n" 
      "\r\n", sz 
    ); 
    if (len < 0) { 
     err = 3; 
     fprintf(stderr, "Error writing header.\n"); 
     goto fine; 
    } 

    /* Debug print. */ 
    fprintf(stderr, "Header[%d]:\n'%s'\n", len, buf); 

    if ((err = send_data(soc, buf, len)) != 0) { 
     fprintf(stderr, "Error sending header.\n"); 
     goto fine; 
    } 

    while (!feof(fh)) { 
     len = fread(buf, sizeof(char), 500, fh); 
     if (len < 1) 
      break; 
     if ((err = send_data(soc, buf, len))) { 
      fprintf(stderr, "Error sending file.\n"); 
      goto fine; 
     } 
    } 
    if ((err = ferror(fh))) { 
     fprintf(stderr, "Error reading file.\n"); 
     perror("fread"); 
    } 
fine: 
    fclose(fh); 
    return err; 
}