继上一个问题之后,我对此代码感到很好奇-
case AF_INET:
{
struct sockaddr_in * tmp =
reinterpret_cast<struct sockaddr_in *> (&addrStruct);
tmp->sin_family = AF_INET;
tmp->sin_port = htons(port);
inet_pton(AF_INET, addr, tmp->sin_addr);
}
break;
在问这个问题之前,我在SO上搜索了相同的主题,并得到了关于该主题的混合答案。说使用联合来完成这项任务,但是对于接受的答案的评论也再次有所不同。
微软在相同结构的文档中说-
应用程序开发人员通常仅使用SOCKADDR_STORAGE的ss_family成员。其余成员确保SOCKADDR_STORAGE可以包含IPv6或IPv4地址,并对结构进行适当填充以实现64位对齐。这种对齐方式使特定于协议的套接字地址数据结构可以访问SOCKADDR_STORAGE结构中的字段,而不会出现对齐问题。通过填充,SOCKADDR_STORAGE结构的长度为128个字节。
Opengroup的文档状态-
标头应定义sockaddr_storage结构。该结构应为:
足够大以容纳所有受支持的协议特定的地址结构
在适当的边界处对齐,以便可以将指向它的指针转换为指向协议特定地址结构的指针,并用于访问那些结构的字段而不会出现对齐问题
套接字的手册页也说相同-
另外,套接字API提供了数据类型struct
sockaddr_storage。此类型适合容纳所有受支持的特定于域的套接字地址结构;它足够大并且对齐正确。(特别是,它足够容纳IPv6套接字地址。)
我已经使用这两种类型转换看到多种实现C
与C++
语言在野外,现在我不确定的事实,哪一个是正确的,因为有一些职位,矛盾与以上权利-
那么,哪种安全和正确的方法来填充sockaddr_storage
结构?这些指针强制转换安全吗?还是联合方法?我也知道该getaddrinfo()
调用,但是对于上述仅填充结构的任务而言,这似乎有点复杂。memcpy还有另一种推荐的方式,这样安全吗?
在过去的十年中,C和C ++编译器比sockaddr
设计接口甚至编写C99时要复杂得多。作为其一部分,已理解的“未定义行为”的 目的_已经改变。回顾过去,未定义的行为通常旨在涵盖_硬件_实现之间关于操作语义的分歧。但是如今,最终归功于许多组织希望停止编写FORTRAN并有能力支付编译器工程师来实现这一目标,未定义的行为是编译器用来
_推断代码
的事情。左移是一个很好的例子:C99 6.5.7p3,4(为清楚起见重新排列了一下)读取
结果
E1 << E2
是E1
左移位E2
位置;空位用零填充。如果[E2
]的值为负或大于或等于提升的[E1
] 的宽度,则行为不确定。
因此,例如,1u << 33
UB在unsigned int
32位宽的平台上。该委员会之所以没有定义,是因为在这种情况下,不同的CPU架构的左移指令执行不同的操作:有些始终产生零,有些减少了类型宽度(x86)的模数,有些减少了模数的位数。
(ARM),并且至少有一种历史上常见的体系结构会陷入陷阱(我不知道是哪一种,但这就是为什么它是未定义且未指定的原因)。但是现在,如果你写
unsigned int left_shift(unsigned int x, unsigned int y)
{ return x << y; }
在具有32位的平台上unsigned int
,知道上述UB规则的编译器将 在 调用该函数时
推断出y
必须具有介于0到32之间的值。它将把该范围输入到过程间分析中,并使用它来执行诸如在调用方中删除不必要的范围检查之类的操作。如果程序员有理由认为它们
不是 不必要的,那么,现在您开始了解为什么这个主题如此蠕虫。
有关针对未定义行为目的进行的更改的更多信息,请参阅LLVM人们关于该主题的三部分文章(1
2
3)。
现在您已经了解了,我实际上可以回答您的问题。
在消除了一些不相关的并发症之后struct sockaddr
,这些是struct sockaddr_in
,和的定义struct sockaddr_storage
:
struct sockaddr {
uint16_t sa_family;
};
struct sockaddr_in {
uint16_t sin_family;
uint16_t sin_port;
uint32_t sin_addr;
};
struct sockaddr_storage {
uint16_t ss_family;
char __ss_storage[128 - (sizeof(uint16_t) + sizeof(unsigned long))];
unsigned long int __ss_force_alignment;
};
这是穷人的子类。它是C语言中的一个普遍用法。您定义了一组结构,它们都具有相同的初始字段,这是一个代码号,用于告诉您实际上已通过了哪种结构。回想过去,每个人都希望,如果您分配并填写了一个struct sockaddr_in
,向上转换为struct sockaddr
,并将其传递给例如connect
,实现connect
可以struct sockaddr
安全地取消对指针的引用以检索该sa_family
字段,得知它正在查看a
sockaddr_in
并将其抛回,然后继续。C标准一直说取消引用struct sockaddr
指针会触发未定义的行为-自C89以来,这些规则未更改-
但每个人都希望 在这种情况下 这样做是安全的 __因为无论您使用的是哪种结构,它都是相同的“加载16位”指令。
这就是POSIX和Windows文档谈论对齐的原因。早在1990年代编写这些规范的人就认为, 实际上
可能造成麻烦的主要方式是,如果您结束发布未对齐的内存访问。
但是该标准的文本中没有任何关于加载指令或对齐的内容。它是这样说的(C99§6.5p7+脚注):
一个对象只能通过具有以下类型之一的左值表达式访问其存储值:73)
- 与对象的有效类型兼容的类型,
- 与对象的有效类型兼容的类型的限定版本,
- 类型是与对象的有效类型相对应的有符号或无符号类型,
- 一种类型,是与对象的有效类型的限定版本相对应的有符号或无符号类型,
- 在其成员(包括递归地,子集合或包含的联盟的成员)中包括上述类型之一的集合或联合类型,或
- 字符类型。
73)此列表的目的是指定对象可能会别名也可能不会别名的那些情况。
struct
类型仅与自身“兼容”,声明的变量的“有效类型”是其声明的类型。所以您显示的代码…
struct sockaddr_storage addrStruct;
/* ... */
case AF_INET:
{
struct sockaddr_in * tmp = (struct sockaddr_in *)&addrStruct;
tmp->sin_family = AF_INET;
tmp->sin_port = htons(port);
inet_pton(AF_INET, addr, tmp->sin_addr);
}
break;
…具有未定义的行为, 即使 幼稚的代码生成将按预期运行,编译器也可以从中进行推断。现代编译器可能由此推断出,case AF_INET
永远无法执行 。它将整个块删除为死代码,并随之而来。
那么,您如何sockaddr
安全地工作呢?最简单的答案是“随便使用getaddrinfo
和”
getnameinfo
。他们为您解决了这个问题。
但是,也许你需要工作,一个地址族,比如AF_UNIX
,即getaddrinfo
不处理。在大多数情况下,您只需声明地址族类型正确的变量,然后 _仅_在调用带有struct sockaddr *
int connect_to_unix_socket(const char *path, int type)
{
struct sockaddr_un sun;
size_t plen = strlen(path);
if (plen >= sizeof(sun.sun_path)) {
errno = ENAMETOOLONG;
return -1;
}
sun.sun_family = AF_UNIX;
memcpy(sun.sun_path, path, plen+1);
int sock = socket(AF_UNIX, type, 0);
if (sock == -1) return -1;
if (connect(sock, (struct sockaddr *)&sun,
offsetof(struct sockaddr_un, sun_path) + plen)) {
int save_errno = errno;
close(sock);
errno = save_errno;
return -1;
}
return sock;
}
该 实施 的connect
有通过一些跳铁圈,使这个安全的,但是这不是你的问题。
魂斗罗对方的回答,有 是 一个情况下,你可能想要使用sockaddr_storage
;与getpeername
和一起getnameinfo
在需要同时处理IPv4和IPv6地址的服务器中。这是一种知道要分配多少缓冲区的简便方法。
#ifndef NI_IDN
#define NI_IDN 0
#endif
char *get_peer_hostname(int sock)
{
char addrbuf[sizeof(struct sockaddr_storage)];
socklen_t addrlen = sizeof addrbuf;
if (getpeername(sock, (struct sockaddr *)addrbuf, &addrlen))
return 0;
char *peer_hostname = malloc(MAX_HOSTNAME_LEN+1);
if (!peer_hostname) return 0;
if (getnameinfo((struct sockaddr *)addrbuf, addrlen,
peer_hostname, MAX_HOSTNAME_LEN+1,
0, 0, NI_IDN) {
free(peer_hostname);
return 0;
}
return peer_hostname;
}
(我也可能写过struct sockaddr_storage addrbuf
,但是我想强调一点,我实际上不需要addrbuf
直接访问的内容。)
最后要注意的:如果BSD人已经确定sockaddr结构只是一个 小 有点不同......
struct sockaddr {
uint16_t sa_family;
};
struct sockaddr_in {
struct sockaddr sin_base;
uint16_t sin_port;
uint32_t sin_addr;
};
struct sockaddr_storage {
struct sockaddr ss_base;
char __ss_storage[128 - (sizeof(uint16_t) + sizeof(unsigned long))];
unsigned long int __ss_force_alignment;
};
…由于“包含上述类型之一的聚合或联合”规则,上流和下流本来可以很好地定义。如果您想知道如何在新的C代码中处理此问题,请继续。
问题内容: 是否有任何技术可以允许这样的行设置 产生结果 实际上,这是行到范围的操作。我正在Oracle Land上玩,并且希望您提出任何建议。 问题答案: 我觉得可能可以对此进行改进,但是它可以起作用:
问题内容: 的绑定()函数接受一个指针,但在我看到所有的实施例中,结构来代替,并且被转换为: 我无法确定为什么要使用结构。为什么不只是准备并通过? 这只是约定吗? 问题答案: 不,不只是惯例。 是用于任何类型的套接字操作的通用描述符,而是特定于基于IP的通信的结构(IIRC,“ in”代表“ InterNet”)。据我所知,这是一种“多态性”:该函数假装采用,但是实际上,它将假定传入了适当的结构类
我一直在阅读文档并遍历头文件以理解结构,然而,还有最后一件事我无法获得。 头定义sockaddr结构,该结构至少包括以下成员:sa_family_t、sa_family address family char、SA_Data[]套接字地址(可变长度数据) 标头应定义sockaddr_in结构,该结构应至少包括以下成员:sa_family_t sin_family AF_INET。in_port_t
我有一个pem格式的证书和私钥,我想转换成.crt文件,我已经运行了下面的命令。 卡特彼勒csr.pem privatekey.pem>组合.pem openssl-0.9.8k_x64\bin\openssl x509-outform der-in combined.pem-out certificate.crt 但是,获取以下错误:无法加载证书91172:error:0906d06c:pem例
问题内容: 组态 本地: 将在本地计算机上创建ssh连接并发出命令的本地计算机。 代理: 具有SSH权限同时访问本地和远程的EC-2实例。 远程: 位于NAT路由器后面的远程计算机(LOCAL无法访问,但将打开与PROXY的连接并允许LOCAL隧道连接到它)。 端口转发步骤(通过命令行) 创建从REMOTE到PROXY的SSH连接,以将REMOTE计算机上端口22上的SSH流量转发到PROXY服务
我正在阅读关于reinterpret_cast的笔记,它是混淆现象(http://en.cppreference.com/w/cpp/language/reinterpret_cast)。 我写了代码: 我认为这些规则在这里不适用: T2是对象的(可能是cv限定的)动态类型 T2和T1都是指向相同类型T3的指针(可能是多级的,可能在每个级别都是cv限定的)(因为C11) T2是一个聚合类型或并集类