我有以下代码用于套接字连接到运行在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地址的端口?
您正在向getaddrinfo()
传递IPv4地址,而不是主机名(不过,您应该在hints.ai_flags
字段中指定AI_NUMERICHOST
)。它将输出一个包含该IP地址的sockaddr_in
的单个addrinfo
,因为它不会尝试验证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
,因为只有在connect()
成功的情况下才调用freeaddrinfo()
。无论如何使用addrinfo
数据,只要getaddrinfo()
成功,都需要调用freeaddrinfo()
。
UDP"连接"不是网络操作。它在本地API中设置一个条件,过滤掉来自其他主机的数据报,并允许您使用send()
而不是sendto()
。它不会失败,但后续发送会失败。