【发布时间】:2016-07-13 19:38:42
【问题描述】:
当我尝试连接到我的 ipv4 服务器时出现错误。目前ios应用用户需要输入其服务器的IP地址、端口和帐户信息。
然后,ios 应用程序调用 SocketSender 类(包含在标头搜索路径中)上的 Connect,该类依次调用 Socket.h 的连接函数,然后检查结果。
连接 - SocketSender.cpp
bool SocketSender::Connect (const char *host, int port, CApiError &err)
{
errno = 0;
struct hostent *hostinfo;
hostinfo = gethostbyname (host);
if (!hostinfo) {
#ifdef PLATFORM_WIN32
m_nLastErrorNo = SOCKET_ERRNO();
err.SetSystemError(m_nLastErrorNo);
#else
/* Linux stores the gethostbyname error in h_errno. */
m_nLastErrorNo = EINVAL; // h_errno value is incompatible with the "normal" error codes
err.SetError(FIX_SN(h_errno, hstrerror(h_errno)), CATEGORY_SYSTEM | ERR_TYPE_ERROR);
#endif
return false;
}
socket_fd = socket (AF_INET, SOCK_STREAM, 0);
if (socket_fd == -1) {
m_nLastErrorNo = SOCKET_ERRNO();
err.SetSystemError(m_nLastErrorNo);
return false;
}
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_port = htons (port);
address.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list;
int result;
SetSocketOptions();
result = connect (socket_fd, (struct sockaddr *) &address, sizeof (address));
if (result == -1) {
if (IS_IN_PROGRESS()) {
fd_set f1,f2,f3;
struct timeval tv;
/* configure the sets */
FD_ZERO(&f1);
FD_ZERO(&f2);
FD_ZERO(&f3);
FD_SET(socket_fd, &f2);
FD_SET(socket_fd, &f3);
/* we will have a timeout period */
tv.tv_sec = 5;
tv.tv_usec = 0;
int selrez = select(socket_fd + 1,&f1,&f2,&f3,&tv);
if (selrez == -1) { // socket error
m_nLastErrorNo = SOCKET_ERRNO();
Disconnect(true);
err.SetSystemError(m_nLastErrorNo);
return false;
}
if (FD_ISSET(socket_fd, &f3)) { // failed to connect ..
int sockerr = 0;
#ifdef PLATFORM_WIN32
int sockerr_len = sizeof(sockerr);
#else
socklen_t sockerr_len = sizeof(sockerr);
#endif
getsockopt(socket_fd, SOL_SOCKET, SO_ERROR, (char *)&sockerr, &sockerr_len);
if (sockerr != 0) {
m_nLastErrorNo = sockerr;
} else {
#ifdef PLATFORM_WIN32
m_nLastErrorNo = ERROR_TIMEOUT; // windows actually does not specify the error .. is this ok?
#else
m_nLastErrorNo = ETIMEDOUT;
#endif
}
Disconnect(true);
err.SetSystemError(m_nLastErrorNo);
return false;
}
if (!FD_ISSET(socket_fd, &f2)) { // cannot read, so some (unknown) error occured (probably time-out)
int sockerr = 0;
#ifdef PLATFORM_WIN32
int sockerr_len = sizeof(sockerr);
#else
socklen_t sockerr_len = sizeof(sockerr);
#endif
getsockopt(socket_fd, SOL_SOCKET, SO_ERROR, (char *)&sockerr, &sockerr_len);
if (sockerr != 0) {
m_nLastErrorNo = sockerr;
} else {
#ifdef PLATFORM_WIN32
m_nLastErrorNo = ERROR_TIMEOUT; // windows actually does not specify the error .. is this ok?
#else
m_nLastErrorNo = ETIMEDOUT;
#endif
}
Disconnect(true);
err.SetSystemError(m_nLastErrorNo);
return false;
}
#ifndef PLATFORM_WIN32 // FIXME: is the same needed for windows ?
// unix always marks socket as "success", however error code has to be double-checked
int error = 0;
socklen_t len = sizeof(error);
if (getsockopt(socket_fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
err.SetSystemError();
return false;
}
if(error != 0) {
m_nLastErrorNo = error;
Disconnect(true);
err.SetSystemError(m_nLastErrorNo);
return false;
}
#endif
} else {
m_nLastErrorNo = SOCKET_ERRNO();
Disconnect(true);
err.SetSystemError(m_nLastErrorNo);
return false;
}
}
m_nIP = ntohl(address.sin_addr.s_addr);
m_bServerSocket = false;
return true;
}
这是可以正常工作的原始版本。当我将上述内容更改为使用 AF_INET6 和 in_addr6->sin6_addr 时,我不断收到错误并且应用程序无法连接。我尝试使用 getaddrinfo 但这仍然没有连接。
struct addrinfo hints, *res, *res0;
int error;
const char *cause = NULL;
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_DEFAULT;
error = getaddrinfo(host, "PORT", &hints, &res0);
if (error) {
errx(1, "%s", gai_strerror(error));
/*NOTREACHED*/
}
socket_fd = -1;
printf("IP addresses for %s:\n\n", host);
int result;
void *addr;
char *ipver;
for (res = res0; res!=NULL; res = res->ai_next) {
socket_fd = socket(res->ai_family, res->ai_socktype,
res->ai_protocol);
if (socket_fd < 0) {
cause = "socket";
continue;
}
if ((result = connect(socket_fd, res->ai_addr, res->ai_addrlen)) < 0) {
cause = "connect";
close(socket_fd);
socket_fd = -1;
continue;
}
// get the pointer to the address itself,
// different fields in IPv4 and IPv6:
if (res->ai_family == AF_INET) { // IPv4
struct sockaddr_in *ipv4 = (struct sockaddr_in *)res->ai_addr;
addr = &(ipv4->sin_addr);
ipver = "IPv4";
} else { // IPv6
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)res->ai_addr;
addr = &(ipv6->sin6_addr);
ipver = "IPv6";
}
SetSocketOptions();
break; /* okay we got one */
}
我需要使其与 ipv6 和 ipv4 向后兼容。任何帮助将不胜感激,因为我在过去一周一直在测试这个。此外,如果有人知道如何在 XCode 上调试 SocketSender.cpp,那将有很大帮助。
【问题讨论】:
-
您到底有什么问题?你说有错误,但你没有说错误实际上是什么,或者哪一行代码报告了错误。无论如何,您的
gethostbyname()代码一开始就被破坏了。您没有检查hostinfo->h_addrtype字段以确保 IP 地址(是的,复数)与您期望的类型相同,并且您没有遍历整个列表。gethostbyname()可以返回 IPv4 或 IPv6 地址,您无法控制报告的类型。getaddrinfo()提供该控制,所以是的,请使用它。 -
@RemyLebeau 当我使用 getaddrinfo() 并遍历列表并使用 socket.h(网络库)中的连接函数进行连接时。连接函数返回 -1 表示它没有连接。为了澄清,我正在尝试对 ipv6 执行与 socketsender.cpp 相同的操作,但它没有连接。
-
你试过检查
errno了吗?它会告诉你为什么connect()失败。您是否尝试记录getaddrinfo()报告的 IP 地址?您的设备是否连接到可以访问这些 IP 地址的网络? -
@3rdeye7:如果您在使用
getaddrinfo返回的地址进行连接时遇到问题,并且您将 IPv4 文字和数字端口传递给getaddrinfo,您可能会遇到this bug ,它返回具有 0 个端口的结构;获得结构后,您必须手动将端口设置为正确的数字,以解决此错误。 -
@user102008 非常感谢,这正是它,终于让它工作了。不敢相信我没有在开发者论坛上遇到过。我添加了一个答案以供任何遇到相同/类似问题的人参考。