我有以下代码用于套接字连接到在Ubuntu 15.10上运行的服务器:
void Connect(std::string address, int port)
{
struct addrinfo hints;
struct addrinfo *result = NULL;
struct addrinfo *rp = NULL;
int sfd, s;
std::cout << "Connecting to address " << address << " port " << port << std::endl;
std::memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC; /* Allow IPV4 or IPV6 */
hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
hints.ai_flags = 0;
hints.ai_protocol = 0; /* Any protocol */
std::string portStr;
portStr = std::to_string(port);
s = getaddrinfo(address.c_str(), portStr.c_str(), &hints, &result);
std::cout << "ADDRESS-------------> " << s << std::endl;
if (s != 0)
{
std::stringstream ss;
ss << "Cannot resolve hostname " << address << gai_strerror(s);
throw std::runtime_error(ss.str());
}
/*
* getaddrinfo() returns a list of address structures. We should try each
* address until we successfull bind. If socket() or connect() fails, we close the socket
* and try the next address until the end.
*/
for (rp = result; rp != NULL; rp = rp->ai_next)
{
std::cout << "loop-----------------> " << rp->ai_family << std::endl;
sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
std::cout << "sfd-----------------> " << sfd << std::endl;
if (sfd == -1)
continue;
/*
* If connect succeed, the address was found.
*/
int sts = connect(sfd, rp->ai_addr, rp->ai_addrlen);
std::cout << "sts-----------------> " << sts << std::endl;
if (sts == 0)
break;
close(sfd);
}
/*
* Check for failure
*/
if (rp == NULL)
{
std::stringstream ss;
ss << "Cannot find server address at " << address << " port " << port;
throw std::runtime_error(ss.str());
}
freeaddrinfo(result); /* Object no longer needed */
std::cout << "SOCKET-----------------> " << sfd << std::endl;
currentSocket = sfd;
}
我的问题是,即使IP地址不可用,此代码也会连接套接字。检查结果:
这是运行时输出:
Connecting to address 192.168.0.185 port 9090
ADDRESS-------------> 0
loop-----------------> 2
sfd-----------------> 5
sts-----------------> 0
SOCKET-----------------> 5
$ ping 192.168.0.185
PING 192.168.0.185 (192.168.0.185) 56(84) bytes of data.
From 192.168.0.185 icmp_seq=1 Destination Host Unreachable
From 192.168.0.185 icmp_seq=2 Destination Host Unreachable
From 192.168.0.185 icmp_seq=3 Destination Host Unreachable
我想了解这里发生了什么?为什么它从不存在的Ip地址将套接字连接到端口?
答案 0 :(得分:3)
您将IPv4地址(而非主机名)传递给getaddrinfo()
(您应该在AI_NUMERICHOST
字段中指定hints.ai_flags
)。它将按原样为该IP地址输出包含addrinfo
的单个sockaddr_in
,它不会尝试验证IP的存在。这就是getaddrinfo()
返回0的原因。
您告诉getaddrinfo()
您将使用UDP(SOCK_DGRAM
)套接字,而不是TCP(SOCK_STREAM
)套接字。因此输出addrinfo
包含在调用socket()
时创建UDP套接字的信息。
然后在UDP套接字上调用connect()
。在UDP中,connect()
实际上并不像TCP那样创建物理连接。它只是将指定的对等IP分配给套接字,以便可以使用send()
和recv()
代替sendto()
和recvfrom()
。这就是connect()
返回0而不是失败的原因。这样做允许send()
始终将数据包发送到同一IP,recv()
只接受从同一IP接收的数据包。
您实际上并未发送任何数据,因此未在代码的任何步骤验证对等IP。一旦开始发送数据,传输的数据包将最终从网络接收ICMP主机无法访问错误,从而导致send()
和recv()
开始失败。
因此,如果您希望connect()
因不可达的IP地址而失败,请创建TCP套接字而不是UDP套接字。否则,如果继续使用UDP套接字,则需要将数据发送到IP,以便网络尝试对其进行物理路由。
顺便说一句,如果connect()
确实失败了,那么只有在addrinfo
成功时才会调用freeaddrinfo()
来泄露输出connect()
。无论您如何使用freeaddrinfo()
数据,都需要随时getaddrinfo()
成功致电addrinfo
。
答案 1 :(得分:1)
UDP&#39; connect&#39;不是网络操作。它在本地API中设置条件,从其他主机过滤掉数据报,并允许您使用send()
而不是sendto()
。它不会失败,但随后的发送将会失败。