【问题标题】:How to Use Client Socket Bind如何使用客户端套接字绑定
【发布时间】:2023-03-09 21:47:01
【问题描述】:

我正在编写一个 Socket 库,但在绑定到特定地址和端口的客户端套接字方面遇到了一些问题。我目前正在测试我的 winsock TCP 库,尽管我确信我没有理由在 *nix 上不会遇到同样的问题。到目前为止一切正常(如果我 connect() 不先调用 bind() 我可以 send()recv())。

我有一台服务器正在监听 192.168.1.99:XXXX(我的机器地址)。

然后我得到一个套接字并绑定到 192.168.1.99:0。我尝试连接时收到套接字错误 10049 (WSAEADDRNOTAVAIL)。如果我尝试绑定到 0.0.0.0:0,也会发生同样的事情。

如果我尝试绑定到 127.0.0.1:0 或 localhost 或将主机名保留为 NULL(如果这甚至可能,我应该使用 AI_PASSIVE 标志吗?)然后连接我得到一个 10061 (WSACONNREFUSED)。

有人知道这里的正确方法吗?

EDIT3: 原来问题是我在调用 bind 之前调用了 socket,然后在调用 connect 之前再次调用它。在调用绑定之前,我将其切换为仅调用套接字。我不知道其他人是否对这整个概念感到困惑,所以除非有人觉得应该关闭,否则我将把它留在这里?

编辑2:是我调用socket两次的问题吗?

编辑:这是一些功能

int main() {
    WSADATA wsaData;   // if this doesn't work

    if(WSAStartup(MAKEWORD(2,0), &wsaData) != 0) {
        cout << WSAStartup failed << endl;
        return -1;
    }

    ClientSocket* cs = new ClientSocket("192.168.1.99", "XXXX");

    cs->bind("", "0"); //error 10049 (WSAEADDRNOTAVAIL)
    //cs->bind("", "60000"); //error 10049 (WSAEADDRNOTAVAIL)

    //cs->bind("0.0.0.0", "0"); //error 10049 (WSAEADDRNOTAVAIL)
    //cs->bind("0.0.0.0", "60000"); //error 10049 (WSAEADDRNOTAVAIL)

    //cs->bind("192.168.1.99", "0"); //error 10061 (WSACONNREFUSED)
    //cs->bind("192.168.1.99", "60000"); //error 10061 (WSACONNREFUSED)

    //cs->bind("127.0.0.1", "0"); //error 10061 (WSACONNREFUSED)
    //cs->bind("127.0.0.1", "60000"); //error 10061 (WSACONNREFUSED)

    //cs->bind("localhost", "0"); //error 10061 (WSACONNREFUSED)
    //cs->bind("localhost", "60000"); //error 10061 (WSACONNREFUSED)


    cs->connect(); //connects to the info passed in ctor

    Socket* s = cs->getSocket();

    if(s == NULL) {
        cs->close();
        return -1;
    }

    .
    .
    .
    .
}

//these are the methods exposed by my API
int ClientSocket::bind(std::string hostname, std::string port) {
    if(bound) {
        return -1;
    }
    if(connected) {
        return -2;
    }

    //this defines what specialSockOp will do
    ssop = &ClientSocket::BaseDef::bind;

    info->hostname = hostname;
    if(hostname.compare("") == 0) {
        info->AI_FLAG_PASSIVE = true;
    }
    info->port = port;

    //calls SpecialSocket::socket()
    socket();

    bound = true;

    return 0;
}

int ClientSocket::connect() {
    if(connected) {
        return SOCKET_ERROR;
        //throw exception
    }
    if(bound) {
        ssop = &ClientSocket::BaseDef::connect;
    }
    socket();
    return 0;
}

void SpecialSocket::WinImp::socket() {
    struct addrinfo *results = NULL, *p = NULL, hints;
    int rv;

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = (containerClass->info->ipType == IP_DUAL ? AF_UNSPEC :
        containerClass->info->ipType == IPV6 ? AF_INET6 : AF_INET);
    hints.ai_socktype = (containerClass->info->protocol == TCP ? SOCK_STREAM : 
        SOCK_DGRAM);
    hints.ai_protocol = 0;
    if(containerClass->info->AI_FLAG_PASSIVE) {
        hints.ai_flags |= AI_PASSIVE;
    }
    if(containerClass->info->AI_FLAG_CANONNAME) {
        hints.ai_flags |= AI_CANONNAME;
    }

    //cStringHostname returns NULL if an empty string has been passed in
    if((rv = getaddrinfo(containerClass->info->cStringHostname(),
         containerClass->info->cStringPort(), &hints, &results)) != 0) {
            //throw exception
    }

    for(p = results; p != NULL; p = p->ai_next) {
        if((containerClass->info->sock_fd = ::socket(p->ai_family,
                 p->ai_socktype, p->ai_protocol)) == INVALID_SOCKET) {
            closesocket(containerClass->info->sock_fd);
            continue;
        }

        //for a ClientSocket specialSockOp will call either bind or connect
        //corresponding to the function called by ClientSocket
        if(containerClass->specialSockOp(p->ai_addr) == SOCKET_ERROR) {
            closesocket(containerClass->info->sock_fd);
            continue;
        }

        break; //successfully connected
    }

    if(p == NULL) {
        //none of the connections in results were good
        //throw exception - could not connect
    }

    freeaddrinfo(results);
}

int SpecialSocket::WinImp::bind(const void* addr) {
    struct sockaddr* ai_addr = (struct sockaddr*)addr;

    return ::bind(containerClass->getSockFd(), ai_addr, sizeof(*ai_addr));

}

int ClientSocket::WinImp::connect(const void* addr) {
    struct sockaddr* ai_addr = (struct sockaddr*)addr;

    int result = ::connect(containerClass->getSockFd(), ai_addr, sizeof(*ai_addr));

    if(result == SOCKET_ERROR) {
        //these are where my errors are showing up
        cout << "error after connect() is - " << WSAGetLastError() << endl;
        return SOCKET_ERROR;
    }

    containerClass->servInfo->sock_fd = containerClass->getSockFd();

    //I separated the functionality here - Socket can only send/recv
    containerClass->s = new Socket();
    //I use getpeername here to get information about the remote socket

    //Long story but basically sets up the Socket
    containerClass->initRWSocket(containerClass->s, containerClass->servInfo);

    return result;
}

然后用户可以调用ClientSocket.getSocket(),它会返回一个Socket,然后他们就可以使用send/recv。在我的情况下,这将返回 NULL,因为 Socket 从未实例化,因为 SpecialSocket::connect 在它执行之前返回。

【问题讨论】:

  • 你能发布一些示例代码吗?
  • @simonc 这有点复杂,因为很多功能都分布在几个类中,但我会看看我是否能得到一些连贯的东西。 Joachim - 我知道怎么做;我只是不知道为什么它在我的情况下不起作用。
  • socket 已经连接之后再绑定就来不及了,但是为什么还要绑定到本地地址呢?这不习惯。
  • @EJP 我在连接之前调用绑定?而且我现在并没有真正的目的,但我将来可能会这样做,而且我希望有一个能够处理它的框架。

标签: c++ sockets network-programming winsock


【解决方案1】:

您必须绑定到能够连接到 connect 目标地址的有效本地地址,即具有到它的路由。显然在这种情况下:

  • 192.168.1.99 不是本地地址,而是远程地址
  • 127.0.0.1 是本地地址,但您只能通过它访问 127.0.0.1,不能访问任何远程地址
  • 0.0.0.0 是监听套接字的有效绑定地址,但不适用于活动(出站)套接字。

这是绑定活动套接字是一个坏主意的几个原因之一:在多宿主主机中,您必须遍历本地地址以选择合适的地址,即复制 TCP 会自动为您执行的路由,如果你根本没有做 bind()。

您在编辑中给出的解释与您遇到的错误不符。

【讨论】:

  • 谢谢。编辑中的解释是关于一个潜在问题(为什么它根本不起作用)。一旦我解决了这个问题,我就能够真正进行测试。就像你说的,0.0.0.0 不起作用,127.0.0.1 只能到达自己。 192.168.1.99 确实有效。我绑定到它和一个任意端口,然后我可以连接并发送/接收。
猜你喜欢
  • 2011-03-16
  • 1970-01-01
  • 1970-01-01
  • 2011-01-27
  • 1970-01-01
  • 1970-01-01
  • 2014-04-18
  • 2013-10-23
  • 2019-04-02
相关资源
最近更新 更多