微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

解除绑定后无法绑定套接字

如何解决解除绑定后无法绑定套接字

我正在尝试编写一个类似服务器的程序,其中客户端连接,您可以下载您想要的任何文件(并有权限)。我制作了一个函数,可以让客户端使用 /dev/TCP/IP/port 设备发送文件(使用 bash,我不想将客户端安装到我的所有 PC 上)。我可以从客户端获取并保存文件,但是如果我想从同一个端口下载第二个文件(每次下载后端口都应该关闭),我会收到一个 EADDRINUSE 错误

功能

void DownloadFile(int fd,cmd_args cmdargs,arguments args) {

    // check filepaths
    if(cmdargs.lpath.size() == 0 || (exists(cmdargs.lpath.c_str()) && is_directory(cmdargs.lpath.c_str()))) {
        log(/*args.OutputFile*/"file","Please specify a path to save the downloaded file");
        return;
    }

    if(cmdargs.rpath.size() == 0) {
        log("file","Please enter a path to a file");
        return;
    }

    // open a second server to bind
    SockInitializer sock = InitializeSocket(cmdargs.ip,cmdargs.port,true);

    // PACKET FORMAT: [f/d][int][\n][data]
    // PACKET EXPLANATION: file/directory size_of_file new_line file_data
    // open connection
    string connect_payload = "exec 3>/dev/tcp/";
    connect_payload += cmdargs.ip;
    connect_payload += '/';
    connect_payload += to_string(cmdargs.port);
    //connect_payload += ';\n';
    // send file/dir type
    string begin_payload = "[ -d \"";
    begin_payload += cmdargs.rpath;
    begin_payload += "\" ] && echo -n d >&3 || echo -n f >&3;";
    // send size number
    begin_payload += "echo -n $(stat --printf='%s' ";
    begin_payload += cmdargs.rpath;
    begin_payload += ") >&3;";
    // new line
    begin_payload += "echo >&3;";
    // send file
    begin_payload += "cat ";
    begin_payload += cmdargs.rpath;
    begin_payload += " >&3\n\n";

    // accept connection
    SendString(fd,connect_payload);
    int cfd = accept(sock.fd,&sock.addr,&sock.addrlen);
    SendString(fd,begin_payload);

    // get type
    string str = ListenSocket(cfd,1,true);
    bool isFile = (str == "f");

    // get file size
    string TodownloadSTRING = "";
    char buf = '\0';
    do {
        if(buf != '\0')
            TodownloadSTRING += buf;
        buf = ListenSocket(cfd,true)[0];
    } while(buf != '\n');

    // parse file size
    int Todownload = atoi(TodownloadSTRING.c_str());

    /* download file */
    if(isFile) {
        int downloaded = 0;
        // open file [write binary]
        fstream file;
        file.open(cmdargs.lpath,ios::out | ios::binary);
        // loop to fetch data
        while(downloaded < Todownload) {
            string tmp = ListenSocket(cfd,DOWNLOAD_BUF_SIZE);
            // write to file
            file.write(tmp.c_str(),tmp.size());
            // flush to file
            file.flush();
            // add size to downloaded
            downloaded += tmp.size();
        }
        // close file
        file.close();
    }

    string disconnect_payload = "exec 3>&-";
    SendString(fd,disconnect_payload);

    // terminate connection
    shutdown(cfd,SHUT_RDWR);
    close(cfd);
    shutdown(sock.fd,SHUT_RDWR);
    close(sock.fd);
}

以上是整个功能,以防您需要整个功能。 下面是相同的功能,但只有(我认为)你需要了解会发生什么

void DownloadFile(int fd,arguments args) {

    //check the filepaths

    // open a second socket to bind
    SockInitializer sock = InitializeSocket(cmdargs.ip,true);

    // PACKET FORMAT: [f/d][int][\n][data]
    // PACKET EXPLAN: file/directory size_of_file new_line file_data

    // Payload to connect here and send the packet with the file
    string connect_payload = "exec 3>/dev/TCP/IP/PORT";
    string begin_payload = "[ -d \"REMOTE_PATH\" ] && echo -n d >&3 || echo -n f >&3; echo -n $(stat --printf='%s' REMOTE_PATH) >&3; echo >&3; cat REMOTE_PATH >&3\n\n";

    // accept connection on the new socket we initialized earlier in the function
    SendString(fd,begin_payload);

    // is REMOTE_PATH referring to a file or a directory?
    string str = ListenSocket(cfd,true)[0];
    } while(buf != '\n');

    // parse file size to integer
    int Todownload = atoi(TodownloadSTRING.c_str());

    /* download file */
    if(isFile) {
        int downloaded = 0;
        // open file [write binary]
        fstream file;
        file.open(LOCAL_PATH,ios::out | ios::binary);
        // loop to fetch data
        while(downloaded < Todownload) {
            // get data
            string tmp = ListenSocket(cfd,tmp.size());
            file.flush();
            // add size to downloaded
            downloaded += tmp.size();
        }
        // close file
        file.close();
    }

    // make the client close the file descriptor referring to the tcp connection
    string disconnect_payload = "exec 3>&-";
    SendString(fd,disconnect_payload);

    // terminate connection from the server
    shutdown(cfd,SHUT_RDWR);
    close(sock.fd);

}

注意事项

问题中遗漏了很多功能,如果您需要任何功能,只需提问,我想您可以从名称中了解它们的作用。

客户端应该使用这个 bash connect 连接到服务器(我知道这实际上并不安全):/bin/bash -i >& /dev/TCP/IP/PORT 0>&1

我正在使用 parrotOS:Linux

函数应该被调用两次而不终止程序,或者断开客户端与主套接字的连接(传递给函数的 fd)

解决方法

正如@Ted Lyngmo 所指出的,我需要在我的套接字上使用 SO_REUSEADDR 和 SO_REUSEPORT 选项。

如果您遇到同样的问题,请按照以下步骤操作:

//get file descriptor
int fd = socket.socket(/*options*/);

// set the options
int _enable = 1;
if(setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&_enable,sizeof(_enable)) < 0) {
    /*Do stuff */
}
if(setsockopt(fd,SO_REUSEPORT,sizeof(_enable)) < 0) {
    /* Do stuff */
}
bind(fd,/*blah blah*/);

注意 SO_REUSEPORT 和 SO_REUSEADDR 选项需要从第一个套接字设置

不要尝试做类似的事情:

int sock = socket.socket(/*blah blah*/);
int bound = bind(sock,/*blah blah*/);
if(bound < 0 && errno == EADDRINUSE) {
    int _enable = 1;
    setsockopt(sock,sizeof(_enable));
    setsockopt(sock,sizeof(_enable));
    bind(sock,/*blah blah*/);
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。