【问题标题】:Select() implemented wrong in program, getting timeouts from serverSelect() 在程序中实现错误,从服务器获取超时
【发布时间】:2021-06-22 15:18:15
【问题描述】:

传奇中的第三个问题:How to correctly implement select to correctly get data from stdin and recv()。我建议阅读此问题以及它链接的其他问题以了解情况。

基本上,我自己尝试了实现select() 的运气。我的代码:

#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <stdlib.h>

using namespace std;

int main (int argc, char** argv) {
    if (argv[1] == NULL) {
        cout << "\033[31mTARGET NOT SPECIFIED - TERMINATING...\033[0m\n";
        return -1;
    }
    if (argv[2] == NULL) {
        cout << "\033[31mPORT NOT SPECIFIED - TERMINATING...\033[0m\n";
        return -2;
    }
    
    string target = argv[1];
    int port = atoi(argv[2]);

    cout << "GENERATING SOCKET...\n";
    int chatter = socket(AF_INET, SOCK_STREAM, 0);
    if (chatter == -1) {
        cout << "\033[31mSOCKET GENERATION FAILURE - TERMINATING...\033[0m\n";
        return -3;
    }
    cout << "\033[32mSUCCESSFULLY GENERATED SOCKET\033[0m\n";

    struct sockaddr_in hint;
    hint.sin_family = AF_INET;
    hint.sin_port   = htons(port);
    inet_pton(AF_INET, target.c_str(), &hint.sin_addr);

    struct timeval tv;
    tv.tv_usec = 0.0;
    tv.tv_sec = 5;
    int recval;

    cout << "CONNECTING TO " << target << " AT PORT " << port << "...\n";   
    int connection_status = connect(chatter, (sockaddr*)&hint, sizeof(hint));
    if (connection_status == -1) {
        cout << "\033[31mCONNECTION FAILURE - TERMINATING...\033[0m\n";
        return -4;
    }
    cout << "\033[32mCONNECTED TO HOST\033[0m\n";

    char buf[4096] = {0};
    string msg;
    while (true) {
        fd_set rfds;
        FD_ZERO(&rfds);
        FD_SET(chatter, &rfds);

        getline(cin, msg);
        msg+"\r\n";
        int sendmsg = send(chatter, msg.c_str(), msg.size()+1, 0);
        if (sendmsg == -1) {
            cout << "\033[31mMESSAGE SENDING FAILURE - TERMINATING...\033[0m\n";
            return -5;
        }

        recval = select(chatter + 1, &rfds, NULL, NULL, &tv);
        switch(recval) {
            case(0):
                cout << "\033[31mTIMEOUT\033[0m\n";
                break;
            case(-1):
                cout << "\033[31mERROR\033[0m\n";
                break;
            default:
                if (recv(chatter, buf, 4096, 0) < 0) {
                    cout << "\033[31mFAILURE TO RECEIVE MESSAGE - TERMINATING...\033[0m\n";
                    return -6;
                } else {
                    cout << recv(chatter, buf, 4096, 0) << "\n";
                    cout << buf << "\n";
                }
                break;
        }
    }

    close(chatter);

    return 0;
}

scanme.nmap.org 和我的 HTTP 服务器上尝试程序时,我不断收到 TIMEOUT。我做错了什么?

此时,在修复了用户在第一个问题中指出的问题后,我知道我发送数据的方式没有问题。只是程序处理从getline()/recv()获取数据的方式存在问题。

编辑:感谢回答的新的、改进的、有效的代码

#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <stdlib.h>

using namespace std;

int main (int argc, char** argv) {
    if (argv[1] == NULL) {
        cout << "\033[31mTARGET NOT SPECIFIED - TERMINATING...\033[0m\n";
        return -1;
    }
    if (argv[2] == NULL) {
        cout << "\033[31mPORT NOT SPECIFIED - TERMINATING...\033[0m\n";
        return -2;
    }
    
    string target = argv[1];
    int port = atoi(argv[2]);

    cout << "GENERATING SOCKET...\n";
    int chatter = socket(AF_INET, SOCK_STREAM, 0);
    if (chatter == -1) {
        cout << "\033[31mSOCKET GENERATION FAILURE - TERMINATING...\033[0m\n";
        return -3;
    }
    cout << "\033[32mSUCCESSFULLY GENERATED SOCKET\033[0m\n";

    struct sockaddr_in hint;
    hint.sin_family = AF_INET;
    hint.sin_port   = htons(port);
    inet_pton(AF_INET, target.c_str(), &hint.sin_addr);

    int recval;

    cout << "CONNECTING TO " << target << " AT PORT " << port << "...\n";   
    int connection_status = connect(chatter, (sockaddr*)&hint, sizeof(hint));
    if (connection_status == -1) {
        cout << "\033[31mCONNECTION FAILURE - TERMINATING...\033[0m\n";
        return -4;
    }
    cout << "\033[32mCONNECTED TO HOST\033[0m\n";

    char buf[4096] = {0};
    string msg;
    while (true) {
        struct timeval tv;
        tv.tv_usec = 0.0;
        tv.tv_sec = 5;

        fd_set rfds;
        FD_ZERO(&rfds);
        FD_SET(chatter, &rfds);

        getline(cin, msg);
        msg += "\r\n";
        const char *pMsg = msg.c_str();
        size_t msgSize = msg.size();
        do {
            int numSent = send(chatter, pMsg, msgSize, 0);
            if (numSent == -1) {
                cout << "\033[31mMESSAGE SENDING FAILURE - TERMINATING...\033[0m\n";
                close(chatter);
                return -5;
            }
            pMsg += numSent;
            msgSize -= numSent;
        } while (msgSize > 0);

        recval = select(chatter + 1, &rfds, NULL, NULL, &tv);
        switch(recval) {
            case(0):
                cout << "\033[31mTIMEOUT\033[0m\n";
                break;
            case(-1):
                cout << "\033[31mERROR\033[0m\n";
                break;
            default:
                int numRead = recv(chatter, buf, 4096, 0);
                if (numRead < 0) {
                    cout << "\033[31mFAILURE TO RECEIVE MESSAGE - TERMINATING...\033[0m\n";
                    close(chatter);
                    return -6;
                }
                else if (numRead == 0) {
                    cout << "\033[31mDISCONNECTED - TERMINATING...\033[0m\n";
                    close(chatter);
                    break;
                } else {
                    cout << numRead << "\n";
                    cout.write(buf, numRead);
                    cout << "\n";
                }
                break;
        }
    }

    close(chatter);

    return 0;
}

【问题讨论】:

  • chatter + 1 +1 来自哪里? (不是说这是一个问题,只是要求确保问题清楚)
  • 我没有看到尝试实施 select。您是说使用吗?
  • @Jeffrey 老实说,我不知道我只是在一个例子中看到了这一点。我将尝试对此回答者的建议,如果可行,我将保留它,如果不可行,我将尝试删除它。
  • @Jeffrey 关于+1:“此参数应设置为三个集合中任何一个中编号最高的文件描述符,再加上 1。”(来自 @ 987654332@ 在 Linux 上)。如果我没记错的话,select 的 Windows 版本不在乎。编辑:Yepp,来自 MSDN:“忽略。包含 nfds 参数只是为了与 Berkeley 套接字兼容。

标签: c++ linux sockets select file-descriptor


【解决方案1】:

在某些平台上,select() 会更改传递的timeval 以指示剩余时间。所以这很可能是你的超时错误的原因,因为你只设置了一次timeval,它最终会下降到0。你需要在每次调用select()时重置你的tv变量,所以把它移到里面你的while 循环。

此外,您有 2 次调用 recv(),您应该只使用 1 次调用。您忽略了第一个 recv() 收到的字节,如果服务器恰好发送少于 4096 个字节,那么 next 调用 select() 将没有任何数据可供检测, 除非连接断开。

改变这个:

if (recv(chatter, buf, 4096, 0) < 0) {
    cout << "\033[31mFAILURE TO RECEIVE MESSAGE - TERMINATING...\033[0m\n";
    return -6;
} else {
    cout << recv(chatter, buf, 4096, 0) << "\n";
    cout << buf << "\n";
}

到这里:

int numRead = recv(chatter, buf, 4096, 0);
if (numRead < 0) {
    cout << "\033[31mFAILURE TO RECEIVE MESSAGE - TERMINATING...\033[0m\n";
    return -6;
}
else if (numRead == 0) {
    cout << "\033[32mHOST DISCONNECTED\033[0m\n";
    break;
} else {
    cout << numRead << "\n";
    cout.write(buf, numRead);
    cout << "\n";
}

另外,msg+"\r\n"; 是无操作的,您可能打算改用 msg += "\r\n";

而且,在调用send() 时,您不应包含msg 的空终止符。而且您没有考虑到send() 可能无法一次性发送全部数据的可能性。您需要在循环中调用send(),例如:

const char *pMsg = msg.c_str();
size_t msgSize = msg.size();

do {
    int numSent = send(chatter, pMsg, msgSize, 0);
    if (numSent == -1) {
        cout << "\033[31mMESSAGE SENDING FAILURE - TERMINATING...\033[0m\n";
        return -5;
    }
    pMsg += numSent;
    msgSize -= numSent;
}
while (msgSize > 0);

【讨论】:

  • 您关于发送/接收的建议改进了代码,尽管您在 tv 上的观点使它真正起作用。如果你想检查一下,我有你的建议的新代码。
  • 如果代码有效,并且您现在只想改进它,那么您应该改为在CodeReview 上提问。
猜你喜欢
  • 2016-05-13
  • 1970-01-01
  • 2019-12-10
  • 1970-01-01
  • 2021-01-11
  • 2018-02-10
  • 1970-01-01
  • 1970-01-01
  • 2011-09-10
相关资源
最近更新 更多