通过tcp和浏览器使用HTTP协议发送html文件显示错误

时间:2014-11-21 19:15:58

标签: html c++ http tcp network-programming

我正在编写一个HTTP Web服务器,当我向浏览器发送带有HTML文件等效内容的文本文件时,浏览器会正确显示它,但当我发送HTML文件时,浏览器会显示HTML页面一秒钟然后出现“连接已重置”错误。

我注意到文本文件比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;

2 个答案:

答案 0 :(得分: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之后使用它,您必须先调用它,看看是否可以写入文件描述符。

  3. 您的代码对于它设计的任务来说有点过于复杂,所以我提出了以下解决方案,其中提到的问题已修复,其他一些已经改进

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

答案 1 :(得分: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;
}