2015-09-23 3 views
0

Я пишу barebones веб-сервера, но я не могу понять, почему мой файл не отправляется через мой сокет, я подключаюсь к нему и всему, что он просто не send() В моем файле ... Что мне не хватает?Отправка файла через сокет TCP/IP (веб-сервер)

// КОД (server.c)

#include<netinet/in.h>  
#include<stdio.h>  
#include<stdlib.h>  
#include<sys/socket.h>  
#include<sys/stat.h>  
#include<sys/types.h>  
#include<unistd.h>  

int main(void) {  
    int create_socket, new_socket;  
    socklen_t addrlen;  
    int bufsize = 1024;  
    char *buffer = malloc(bufsize);  
    struct sockaddr_in address;  

    if ((create_socket = socket(AF_INET, SOCK_STREAM, 0)) > 0){  
     printf("The socket was created\n"); 
    } 

    address.sin_family = AF_INET;  
    address.sin_addr.s_addr = INADDR_ANY;  
    address.sin_port = htons(80);  

    if (bind(create_socket, (struct sockaddr *) &address, sizeof(address)) == 0){  
     printf("Binding Socket\n"); 
    } 

    long fsize; 
    FILE *fp = fopen("index.html", "r"); 
    fseek(fp, 0, SEEK_END); 
    fsize = ftell(fp); 
    rewind(fp); 
    char *msg = malloc(fsize + 1); 
    fread(msg, sizeof(msg), 1, fp); 

    while (1) {  
     if (listen(create_socket, 10) < 0) {  
     perror("server: listen");  
     exit(1);  
     }  

     if ((new_socket = accept(create_socket, (struct sockaddr *) &address, &addrlen)) < 0) {  
     perror("server: accept");  
     exit(1);  
     }  

     if (new_socket > 0){  
     printf("The Client is connected...\n"); 
     } 

     recv(new_socket, buffer, bufsize, 0);  
     printf("%s\n", buffer);  
     write(new_socket, "HTTP/1.1 200 OK\n", 16); 
     write(new_socket, "Content-length: 46\n", 19); 
     write(new_socket, "Content-Type: text/html\n\n", 25); 
/*  write(new_socket, "<html><body><H1>Hello world</H1></body></html>",46); */ 
     if((send(new_socket, msg, fsize+1, 0)) > 0){ 
      printf("success"); 
     }  
     else{ 
      printf("failed"); 
     } 
     close(new_socket);  
    }  
    close(create_socket);  
    return 0;  
} 

// FILE (index.html) * тот же каталог

<html> 
<body> 
    <h1>Hello World</h1> 
</body> 
</html> 
+0

Откуда вы знаете, что его не отправляют? – Amit

+0

его даже не отправляет ответ в браузере ... –

+1

С этим «баребонным» сервером так много чего не так, это не смешно. Вы не очень хорошо справляетесь с ошибками. Вы открываете файл в текстовом режиме вместо двоичного режима. 'sizeof (msg)' is wrong, вместо этого используйте 'fsize' (в любом случае, я бы не советовал полностью читать весь файл в памяти, читать и отправлять его в куски, иначе вы не сможете обрабатывать большие файлы позже). Вы вызываете 'listen()' слишком много раз, назовите его один раз, а не в цикле. Вы не проверяете, если 'recv()' failed ... –

ответ

5

Код полностью сломан, по десяткам разным причинам. Вместо этого попробуйте что-то подобное:

#include <netinet/in.h>  
#include <stdio.h>  
#include <stdlib.h>  
#include <sys/socket.h>  
#include <sys/stat.h>  
#include <sys/types.h>  
#include <unistd.h>  

bool writeDataToClient(int sckt, const void *data, int datalen) 
{ 
    const char *pdata = (const char*) data; 

    while (datalen > 0){ 
     int numSent = send(sckt, pdata, datalen, 0); 
     if (numSent <= 0){ 
      if (numSent == 0){ 
       printf("The client was not written to: disconnected\n"); 
      } else { 
       perror("The client was not written to"); 
      } 
      return false; 
     } 
     pdata += numSent; 
     datalen -= numSent; 
    } 

    return true; 
} 

bool writeStrToClient(int sckt, const char *str) 
{ 
    return writeDataToClient(sckt, str, strlen(str)); 
} 

int main(void){ 
    int create_socket, new_socket;  
    char *buffer; 
    int bufsize = 1024;  
    struct sockaddr_in address;  
    socklen_t addrlen;  

    buffer = (char*) malloc(bufsize);  
    if (!buffer){ 
     printf("The receive buffer was not allocated\n"); 
     exit(1);  
    } 

    create_socket = socket(AF_INET, SOCK_STREAM, 0); 
    if (create_socket == -1){  
     perror("The socket was not created");  
     exit(1);  
    } 

    printf("The socket was created\n"); 

    memset(&address, 0, sizeof(address));  
    address.sin_family = AF_INET;  
    address.sin_addr.s_addr = INADDR_ANY;  
    address.sin_port = htons(80);  

    if (bind(create_socket, (struct sockaddr *) &address, sizeof(address)) == -1){  
     perror("The socket was not bound");  
     exit(1);  
    } 

    printf("The socket is bound\n");  

    long fsize; 
    FILE *fp = fopen("index.html", "rb"); 
    if (!fp){ 
     perror("The file was not opened");  
     exit(1);  
    } 

    printf("The file was opened\n"); 

    if (fseek(fp, 0, SEEK_END) == -1){ 
     perror("The file was not seeked"); 
     exit(1); 
    } 

    fsize = ftell(fp); 
    if (fsize == -1) { 
     perror("The file size was not retrieved"); 
     exit(1); 
    } 
    rewind(fp); 

    char *msg = (char*) malloc(fsize); 
    if (!msg){ 
     perror("The file buffer was not allocated\n"); 
     exit(1); 
    } 

    if (fread(msg, fsize, 1, fp) != 1){ 
     perror("The file was not read\n"); 
     exit(1); 
    } 
    fclose(fp); 

    printf("The file size is %ld\n", fsize); 

    if (listen(create_socket, 10) == -1){ 
     perror("The socket was not opened for listening");  
     exit(1);  
    }  

    printf("The socket is listening\n"); 

    while (1) {  

     addrlen = sizeof(address); 
     new_socket = accept(create_socket, (struct sockaddr *) &address, &addrlen); 

     if (new_socket == -1) {  
      perror("A client was not accepted");  
      exit(1);  
     }  

     printf("A client is connected from %s:%hu...\n", inet_ntoa(address.sin_addr), ntohs(address.sin_port));  

     // I will leave it as an exercise for you to implement 
     // a proper HTTP request parser here... 
     int numRead = recv(new_socket, buffer, bufsize, 0); 
     if (numRead < 1){ 
      if (numRead == 0){ 
       printf("The client was not read from: disconnected\n"); 
      } else { 
       perror("The client was not read from"); 
      } 
      close(new_socket); 
      continue; 
     } 
     printf("%.*s\n", numRead, buffer);  

     if (!writeStrToClient(new_socket, "HTTP/1.1 200 OK\r\n")){ 
      close(new_socket); 
      continue; 
     } 

     char clen[40]; 
     sprintf(clen, "Content-length: %ld\r\n", fsize); 
     if (!writeStrToClient(new_socket, clen)){ 
      close(new_socket); 
      continue; 
     } 

     if (!writeStrToClient(new_socket, "Content-Type: text/html\r\n")){ 
      close(new_socket); 
      continue; 
     } 

     if (!writeStrToClient(new_socket, "Connection: close\r\n\r\n") == -1){ 
      close(new_socket); 
      continue; 
     } 

     //if (!writeStrToClient(new_socket, "<html><body><H1>Hello world</H1></body></html>")){ 
     if (!writeDataToClient(new_socket, msg, fsize)){ 
      close(new_socket); 
      continue; 
     } 

     printf("The file was sent successfully\n"); 
     close(new_socket);  
    }  

    close(create_socket);  
    return 0;  
} 
+0

VG, но вы проигнорировали возвращаемое значение 'fread()', и вы вызываете 'perror()', если 'read()' возвращает ноль, что неверно. – EJP

+0

Я не игнорирую возвращаемое значение 'fread()', но вы правы относительно возвращаемого значения 'recv()' (я не звоню 'read()'). Я исправил это. –

+0

Когда я пытаюсь скомпилировать его, он имеет 10 ошибок –

0

Другое то неправильный размер, используемый в различных местах (как отмечает математик1975), ваша «настоящая» проблема заключается в том, что вы пытаетесь связаться с браузером, ожидающим HTTP-сервер.

Н Yper Т внутр Т ransfer Р rotocol, ну, a protocol. Это сложнее, чем простая связь и свалка контента.

Вы должны разобрать запрос в соответствии с ним и отправить заголовки и контент определенным образом.

+0

Да, это то, что я понял, что это были заголовки .... будет записывать их в сокет сначала, прежде чем отправлять файл (index.html) работать? –

+0

@ JordanDavis прочитал о протоколе. Это слишком долго, чтобы быть выраженным в этом ответе, но есть много информации в Интернете. – Amit

+0

Я знаю о протоколе, я не полный идиот :), и сервер работал, отправляя html как «строку», но теперь, когда я пытаюсь отправить его как файл, он не работает. Думаю, мне нужно отправьте заголовки в сокет, прежде чем я отправлю буфер, содержащий html ... или мне нужно записать заголовки в буфер до отправки буфера? –

0

Проверьте, не сработал ли bind() и сообщите, если это так. Вы привязываетесь к порту 80; в Unix-подобных операционных системах только root может привязываться к зарезервированным портам (менее 1024).

Update 1:

Вы должны инициализировать addrlen к sizeof(address) перед вызовом accept(). От http://linux.die.net/man/2/accept:

addrlen Аргумент представляет собой значение аргумента-результат: вызывающий абонент должен инициализировать его, чтобы содержать размер (в байтах) структуры указывал на по адр; по возврату он будет содержать фактический размер партнера .

+0

Я знаю, что это не так, потому что у меня он работал с «строкой» HTML перед рукой, а компилятор не терпит неудачу в 'bind()' –

+0

См. Обновление параметра addrlen для accept(). – keithmo

1
fsize = ftell(fp); 
rewind(fp); 
char *filebuff = malloc(fsize + 1); 

Почему fsize+1? Вам не нужен +1.

fread(filebuff, sizeof(filebuff), 1, fp); 

Непроверенное возвращаемое значение. Второй аргумент должен быть fsize. В данный момент вы передаете указатель sizeof.

//create/bind socket 
if ((create_socket = socket(AF_INET, SOCK_STREAM, 0)) > 0) 
{  
    printf("The socket was created\n"); 
} 

Если создание сокета терпит неудачу вы должны (а) напечатать соответствующее сообщение об ошибке, как описано ниже, и (б) не просто продолжать выполнение, как если бы не произошла ошибка.

if (bind(create_socket, (struct sockaddr *) &address, sizeof(address)) == 0) 
{  
    printf("Binding Socket\n"); 

}

То же самое.

//listen, create new_sock, write headers, send file 
while (1){ 
    if (listen(create_socket, 10) < 0) {  
     perror("server: listen");  
     exit(1);  
    } 

listen() вызов должен быть впереди цикла, а не внутри нее. Это первый случай, когда вы фактически обработали случай сбоя.

new_sock = accept(sock, (struct sockaddr *) &address, &addrlen);    
    recv(new_socket, buffer, bufsize, 0);  
    printf("%s\n", buffer);  

Недействительный. Непроверенный код возврата. Буфер действителен вообще, если recv() возвращает положительное целое число и только то, что многие байты его действительны. Оно должно быть:

int count = recv(new_socket, buffer, bufsize, 0);  
    printf("%.*s\n", count, buffer);  

Тогда мы начинаем на HTTP:

write(new_sock, "HTTP/1.1 200 OK\n", 16); 
    write(new_sock, "Content-length: 46\n", 19); 
    write(new_sock, "Content-Type: text/html\n\n", 25); 

Линия терминатора в HTTP наследуется от Telnet и определяется как \r\n, не \n.

if(send(new_sock, filebuff, fsize+1, 0) > 0){ 
     printf("success"); 
    }  
    else{ 
     printf("failed"); 
    } 

Неадекватный. Если вы получили сообщение об ошибке при любом системном вызове, вы должны позвонить perror() или использовать errno или strerror() в сообщении об ошибке. «Сбой» не передает никакой полезной информации, и отладка становится простой игрой догадки. Не пишите такой код. Вы должны использовать perror() или все, что вы решите для всех других непроверенных возвращаемых значений выше.

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

int count; 
while ((count = read(in, buffer, sizeof buffer)) > 0) 
{ 
    send(out, buffer, count, 0); 
} 
if (count < 0) 
{ 
    perror("send failed"); 
} 

Я хотел бы избежать stdio для этого, он имеет слишком много проблем, такие, как плохо спроектированные fread() и fwrite() API для функций.

+0

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

+0

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

+0

Я уже это сделал. – EJP

Смежные вопросы