处理隧道时,Socket read()函数有时永远不会返回0

时间:2018-05-31 13:56:38

标签: c++ sockets

if(port == 443){ // https
    strcpy(buffer, "HTTP/1.0 200 Connection established\r\n\r\n");
    write(client, buffer, strlen(buffer));
    tunnel(server, client, buffer);
    return;
}

tunnel()如下所示

void tunnel(int server, int client, char* buffer){
    int servercon, clientcon;
    int x;
    x = fcntl(server, F_GETFL, 0);
    fcntl(server, F_SETFL, x|O_NONBLOCK);
    x = fcntl(client, F_GETFL, 0);
    fcntl(client, F_SETFL, x|O_NONBLOCK);
    while(true){
        cout << "test" << endl;
        clientcon = read(client, buffer, BUFSIZE);
        write(server, buffer, clientcon);
        servercon = read(server, buffer, BUFSIZE);
        write(client, buffer, servercon);
        if(???) // This condition
            break;
    }

    close(client);
    close(server);
}

当代理服务器收到CONNECT方法时,我试图实现隧道。 此代码运行良好(HTTPS网站已成功加载) 但是,问题是无限的while()循环无法转义,因为read()函数有时永远不会返回0。

作为休息条件,我尝试了几种选择。

1)servercon == 0(有时read()函数永远不会返回0)

2)servercon == -1(发生安全连接失败。我不知道为什么)

3)添加一个变量i作为计时器,i&lt; 100000(它工作,但效率太低)

我应该如何设置休息条件?

(使套接字无阻塞是必要的。没有它,程序将永远停在read()函数。我不知道原因,但使套接字无阻塞解决了问题。)

1 个答案:

答案 0 :(得分:1)

您的tunnel()循环完全忽略read()write()的返回值。尝试更像这样的东西:

bool writeAll(int sckt, void *buffer, size_t buflen)
{
    char *pbuffer = (char*) buffer;
    while (buflen > 0)
    {
        ssize_t numSent = write(sckt, pbuffer, buflen);
        if (numSent < 0) {
            if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
                continue;
            return false;
        }
        pbuffer += numSent;
        buflen -= numSent;
    }
    return true;
}

...

strcpy(buffer, "HTTP/1.0 200 Connection established\r\n\r\n");
if (writeAll(client, buffer, strlen(buffer)))
    tunnel(server, client, buffer);
close(client);
close(server);
return;

...

void tunnel(int server, int client, char *buffer)
{
    int maxFD = max(server, client) + 1;
    ssize_t numRead;
    FD_SET fd;

    int x = fcntl(server, F_GETFL, 0);
    fcntl(server, F_SETFL, x | O_NONBLOCK);

    x = fcntl(client, F_GETFL, 0);
    fcntl(client, F_SETFL, x | O_NONBLOCK);

    do
    {
        cout << "test" << endl;

        FD_ZERO(&fd);
        FD_SET(&fd, server);
        FD_SET(&fd, client);

        x = select(maxFD, &fd, NULL, NULL, NULL);
        if (x < 0) break;

        if (FD_ISSET(&fd, client))
        {
            numRead = read(client, buffer, BUFSIZE);
            if (numRead <= 0) break;

            if (!writeAll(server, buffer, numRead))
                break;
        }

        if (FD_ISSET(&fd, server))
        {
            numRead = read(server, buffer, BUFSIZE);
            if (numRead <= 0) break;

            if (!writeAll(client, buffer, numRead))
                break;
        }
    }
    while (true);
}

至于为什么你的读取并不总是在你期望的时候返回0,这很可能是由于客户端和服务器使用HTTP keep-alive来在服务器发送响应后保持隧道连接打开,所以客户端可以使用相同的TCP连接将后续请求发送到同一服务器。在每个HTTP / S请求上建立新的TCP连接,甚至是新的HTTPS会话都非常耗时并且浪费带宽,因为它涉及TCP和TLS握手的多次往返。因此,HTTP 1.1及更高版本的默认行为是保持连接处于打开状态,除非任何一方通过Connection: close标头明确声明需要关闭。