简体   繁体   English

在子线程中访问套接字描述符时出现“错误的文件描述符”

[英]“Bad file descriptor” when accessing socket descriptor in child thread

When trying to access socket descriptor (with bind() or listen()) in a child thread I get the error: 当尝试在子线程中访问套接字描述符(使用bind()或listen())时,出现错误:

Bad file descriptor 错误的文件描述符

Here is the code (headers ignored): 这是代码(标题被忽略):

class task {
private:
    std::int32_t        socFd;
    std::string         path;
    char                buffer[MAX_SOC_BUFFER];
    struct sockaddr_un  socAddr;

public:
    void init() {
        if ((socFd = socket((std::int32_t)AF_UNIX, (std::int32_t)SOCK_STREAM, 0)) == -1) {
            perror("socket");
            exit(1);
        }

        socAddr.sun_family = AF_UNIX;
        strcpy(socAddr.sun_path, getSocPath().c_str());
    }

    task(const std::string& path) : path{path}
    {
        init();
    }

    auto getSocketFd() ->decltype(socFd) {
        return socFd;
    }

    const std::string& getSocPath() {
        return path;
    }

    auto getSocAddr() -> decltype(socAddr)&
    {
        return socAddr;
    }

    char* getBuff() {
        return buffer;
    }

    virtual ~ task() {
        close(getSocketFd());
    }
};


class server_task : public task{
public:
    void init () {
        ::unlink(getSocPath().c_str());
        size_t len = sizeof(getSocAddr());
        if (bind(getSocketFd(), (struct sockaddr *)&(getSocAddr()), len) == -1) {
            perror("bind");
            exit(1);
        }
    }

    server_task(const std::string& path) : task(path)
    {
        init();
    }

    void operator()() {
        struct sockaddr_un remote;
        if (listen(getSocketFd(), 5) == -1) {
            perror("listen");
            exit(1);
        }
        for(;;) {
            int done, n, socFd2;
            printf("Waiting for a connection...\n");
            socklen_t t = sizeof(remote);
            if ((socFd2 = accept(getSocketFd(), (struct sockaddr *)&remote, &t)) == -1) {
                perror("accept");
                exit(1);
            }
            printf("Connected.\n");
            done = 0;
            do {
                n = recv(socFd2, getBuff(), 100, 0);
                if (n <= 0) {
                    if (n < 0) perror("recv");
                    done = 1;
                }
                if (!done) {
                    std::string buffStr(getBuff());
                    std::transform(buffStr.begin(), buffStr.end(),buffStr.begin(), ::toupper);
                    if (send(socFd2, buffStr.c_str(), n, 0) < 0) {
                        perror("send");
                        done = 1;
                    }
                }
            } while (!done);
            close(socFd2);
        }
    }
};


int main(){
std::vector<std::thread> threads;
server_task server("/tmp/temp_socket");
threads.push_back(std::thread(server));

for(auto& thread : threads)
    thread.join();

return 0;

} }

If I change the main() function to: 如果我将main()函数更改为:

int main(){
    server_task server("/tmp/temp_socket");
    server();
    return 0;
}

no such error occurs. 不会发生此类错误。 As I understand the file descriptor table are shared between parent process and it's children threads so the socket handle should be valid. 据我了解,文件描述符表在父进程及其子线程之间共享,因此套接字句柄应该有效。

What am I doing wrong? 我究竟做错了什么?

threads.push_back(std::thread(server));

This creates at least one copy of the server object, maybe multiple copies, and because you haven't defined a copy constructor every copy shares the same socket and the first one to go out of scope closes the socket. 这将创建server对象的至少一个副本,可能是多个副本,并且由于尚未定义副本构造函数,因此每个副本都共享相同的套接字,而第一个超出范围的副本将关闭套接字。 By the time the new thread starts the temporary copies have been destroyed and closed the socket, so the copy of the server_task that runs in the new thread refers to a closed socket. 到新线程启动时,临时副本已被销毁并关闭了套接字,因此在新线程中运行的server_task的副本指的是已关闭的套接字。

You should either define a move constructor for your task and server_task types so that ownership of the socket is transferred not copied, and move the object into the thread: 您应该为您的taskserver_task类型定义一个移动构造函数,以便不复制套接字的所有权,然后将该对象移动到线程中:

thread.push_back(std::thread(std::move(server)));

or else you should pass the server to the new thread by reference so there are no copies: 否则,您应该通过引用将服务器传递给新线程,以便没有副本:

thread.push_back(std::thread(std::ref(server)));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM