如何使用客户端套接字绑定
How to Use Client Socket Bind
我正在编写一个套接字库,但在绑定到特定地址和端口的客户端套接字时遇到了一些问题。我目前正在测试我的 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 或本地主机或保留主机名 NULL(如果可能的话,我应该使用 AI_PASSIVE 标志吗?)然后连接,我会得到 10061 ( WSACONNREFUSED
)。
有人知道在这里这样做的正确方法吗?
EDIT3:事实证明,问题是我在调用绑定之前调用套接字,然后在调用连接之前再次调用它。在调用绑定之前,我将其切换为仅呼叫套接字。我不知道是否有其他人对整个概念感到困惑,所以我会把它留在这里,除非有人觉得它应该关闭?
编辑2:我调用套接字两次的问题吗?
编辑:这是一些功能
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
在它这样做之前返回。
您必须绑定到一个有效的本地地址,该地址能够连接到连接的目标地址,即具有指向它的路由。显然在这种情况下:
- 192.168.1.99 不是本地地址,而是远程地址 127.0.0.1
- 是本地地址,但您只能从它访问 127.0.0.1,而不能访问任何远程地址 0.0.0.0 是侦听套接字的有效绑定地址
- ,但不是活动(出站)套接字的有效绑定地址。
这是绑定活动套接字是一个坏主意的几个原因之一:在多宿主主机中,您必须遍历本地地址以选择合适的地址,即复制TCP自动为您执行的路由,如果您根本不执行bind()。
编辑中给出的解释与您收到的错误不一致。
- 无法将套接字绑定到地址
- 无法将套接字绑定到 IPv4 和 IPv6 中的端口
- 谁可以发送到绑定到INADDR_LOOPBACK的套接字?
- 为什么在此 UDP 客户端/服务器示例中没有必要绑定客户端套接字?
- QUdp套接字绑定失败
- 客户端套接字是可绑定的,但不可连接,因为已在使用中
- 在 ZeroMQ 中绑定订阅者套接字并连接发布者套接字会在代码运行时给出错误.为什么
- 使用 Boost.Asio 将 UDP 套接字绑定到特定网络接口
- 用于枚举绑定但未连接的套接字的 API
- udp套接字sendto隐式绑定
- QTcp服务器:使用自定义选项绑定套接字
- 当我尝试将UDP套接字绑定到带有SFML的端口时,为什么会遇到错误
- C 窗口无法绑定套接字
- 网络:当接口打开/关闭时,在所有接口上绑定套接字
- TCP 套接字,无法绑定到端口 - 正在使用中
- Linux 拒绝使用 boost asio 的套接字绑定权限
- Winsock2 无法绑定套接字
- 如何在winsock中不绑定套接字的情况下接收数据
- 确定未绑定套接字的地址族
- ZeroMQ:重新绑定套接字时使用地址错误