【问题标题】:Sending html file with HTTP protocol over tcp and browser shows error通过 tcp 和浏览器使用 HTTP 协议发送 html 文件显示错误
【发布时间】:2015-01-20 00:43:19
【问题描述】:

我正在编写一个 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;

【问题讨论】:

  • 投了赞成票,以抵消驾车通过的投反对票。问题似乎也很合理。
  • HTTP 需要 "\r\n" 换行符,"\n" 是不够的。如果这不能解决您的问题,请尝试提出一个最小的完整示例。
  • @Phillip 你是对的,但我已经看到只有\n 的实现工作正常。正如我所说,它在文本格式时可以正常工作。问题在于HTML 格式。你知道为什么HTML 和文本文件的大小不同吗?

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


【解决方案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);

【讨论】:

  • 我已经应用了您提到的更改,但HTML 文件的问题仍然存在。text 文件发送没有任何问题。您可能需要测试代码以供自己查看浏览器中的结果。
  • @AliNfr 我测试过了,还是不清楚你说的文本文件是什么意思?,你的意思是直接发送文本?
  • 正如我在问题中提到的,如果您将 HTML 文件保存为 .txt ,如 foo.txt ,则文件发送没有任何问题,并在浏览器中正确显示。我一直在尝试用c 语言编写HTTP 服务器,但是这样的问题迫使我使用python,在用python 编写服务器时我没有这样的问题,现在我的项目已经完成了。
  • @AliNfr 您使用的是什么编译器、IDE 和操作系统?
  • Windows 7 、Visual Studio 2012 都安装了最新更新。即使使用RESTclientfiddler 客户端,我也一直在尝试测试该程序,并且都显示该程序有错误。
【解决方案2】:

半途而废。首先,即使 “使用 \n 有效” 它也违反了标准。应该使用 CRLF。使用 CRLF。期间。

对于其余的代码。我怀疑这会改变很多事情,但我会稍微重新构造代码。发送函数中发生了很多事情。

分离了数据发送到自己的函数。你甚至可以考虑将 send header 分离到它自己的函数中——如果你找到了一种构造它的好方法。当您扩展以发送文本或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;
}

【讨论】:

    猜你喜欢
    • 2012-05-04
    • 2019-10-27
    • 2014-03-07
    • 2020-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-09
    • 1970-01-01
    相关资源
    最近更新 更多