从 unix 套接字读取字节并在 c 中打印 ip 地址
read bytes from a unix socket and print ip address in c
我有一个 Java 客户端和一个通过 unix 套接字通信的 C 服务器。
我想从客户端向服务器发送一个 ip 地址。在服务器端,当我尝试解释从客户端收到的字节时,我得到 [ 01 01 01 01 ]
这是正确的,但是当我将 ip_address 打印到视频时,我得到 0.0.0.0
。我究竟做错了什么?我想我以某种错误的方式投射了地址。我需要实现 IPv4 映射的 IPv6 地址,这就是我使用 sockaddr_storage
的原因,这是最常见的情况。
这是我的一些代码摘录:
Java 客户端:
OutputStream out;
byte[] ipAddress = Inet6Address.getByName("1.1.1.1").getAddress();
out.write(ipAddress);
C 服务器做:
struct sockaddr_storage * ip_address;
n = read(rcv_sock, msg, PSIZE);
printf("I recevied n bytes: %d\n", n);// i get 4 bytes
print_bytes(msg, n);
ip_address = (union sockaddr_storage *)msg;
char str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(ip_address->__ss_padding), str, INET_ADDRSTRLEN);
printf("ip_address = %s\n", str); //i get 0.0.0.0
特别是服务器需要包含 28 字节长 header + 套接字地址结构的消息。我已经设法正确解码 header 但不是套接字地址结构。
TCPv4 (TCP/IPv4) 套接字由 32 位地址和 16 位端口号标识。 TCPv6 (TCP/IPv6) 套接字由 128 位地址和 16 位端口号标识。 sockaddr_
结构比 C 库在内部使用(和要求)的内部信息多得多。您不能将这些结构从一个系统转移到另一个系统,并期望它们起作用。 (或者在另一端使 any 有点意义,真的。)
IPv4-mapped IPv6 addresses 是 IPv6 地址,前 80 位为零,后面是 16 位 1,最后是 32 位 IPv4 地址。它们通常以 IPv6 格式编写为 ::ffff:a.b.c.d
,其中 a.b.c.d
是映射到 IPv6 的 IPv4 地址。
与其让客户端发送任何类型的 "sockaddr structure",它绝对应该发送 128 位 IPv6 地址,可能还有 16 位端口号.如果您以二进制格式发送这些,通常的做法是按网络字节顺序传输这些,即最高有效字节在前。
但是,我认为将它们作为字符串发送更简单、更可靠,例如使用格式 IPv6
[=14=]
port
[=14=]
(即在地址后附加一个零字节,在端口后附加一个零字节)。这样你只需要一个指向地址的指针(node
)和一个指向端口的指针(serv
),你就可以使用getaddrinfo()
重构所需的sockaddr_in6
结构。
如果你非要发送二进制的地址和端口(比如保证一个fixed-size初始包),理论上你可以构造sockaddr_in6
手工构造。然而,那是 architecture-specific(byte-order 敏感),而且很脆弱。将 IPv6 地址转换为字符串(如果映射为 IPv4,则为 ::ffff:a.b.c.d
,或者十六进制表示法为 aaaa:bbbb:cccc:dddd:eeee:ffff:gggg:hhhh
),并将端口转换为十进制数,然后再次使用 getaddrinfo()
从中得到合适的套接字结构。
我有一个 Java 客户端和一个通过 unix 套接字通信的 C 服务器。
我想从客户端向服务器发送一个 ip 地址。在服务器端,当我尝试解释从客户端收到的字节时,我得到 [ 01 01 01 01 ]
这是正确的,但是当我将 ip_address 打印到视频时,我得到 0.0.0.0
。我究竟做错了什么?我想我以某种错误的方式投射了地址。我需要实现 IPv4 映射的 IPv6 地址,这就是我使用 sockaddr_storage
的原因,这是最常见的情况。
这是我的一些代码摘录:
Java 客户端:
OutputStream out;
byte[] ipAddress = Inet6Address.getByName("1.1.1.1").getAddress();
out.write(ipAddress);
C 服务器做:
struct sockaddr_storage * ip_address;
n = read(rcv_sock, msg, PSIZE);
printf("I recevied n bytes: %d\n", n);// i get 4 bytes
print_bytes(msg, n);
ip_address = (union sockaddr_storage *)msg;
char str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(ip_address->__ss_padding), str, INET_ADDRSTRLEN);
printf("ip_address = %s\n", str); //i get 0.0.0.0
特别是服务器需要包含 28 字节长 header + 套接字地址结构的消息。我已经设法正确解码 header 但不是套接字地址结构。
TCPv4 (TCP/IPv4) 套接字由 32 位地址和 16 位端口号标识。 TCPv6 (TCP/IPv6) 套接字由 128 位地址和 16 位端口号标识。 sockaddr_
结构比 C 库在内部使用(和要求)的内部信息多得多。您不能将这些结构从一个系统转移到另一个系统,并期望它们起作用。 (或者在另一端使 any 有点意义,真的。)
IPv4-mapped IPv6 addresses 是 IPv6 地址,前 80 位为零,后面是 16 位 1,最后是 32 位 IPv4 地址。它们通常以 IPv6 格式编写为 ::ffff:a.b.c.d
,其中 a.b.c.d
是映射到 IPv6 的 IPv4 地址。
与其让客户端发送任何类型的 "sockaddr structure",它绝对应该发送 128 位 IPv6 地址,可能还有 16 位端口号.如果您以二进制格式发送这些,通常的做法是按网络字节顺序传输这些,即最高有效字节在前。
但是,我认为将它们作为字符串发送更简单、更可靠,例如使用格式 IPv6
[=14=]
port
[=14=]
(即在地址后附加一个零字节,在端口后附加一个零字节)。这样你只需要一个指向地址的指针(node
)和一个指向端口的指针(serv
),你就可以使用getaddrinfo()
重构所需的sockaddr_in6
结构。
如果你非要发送二进制的地址和端口(比如保证一个fixed-size初始包),理论上你可以构造sockaddr_in6
手工构造。然而,那是 architecture-specific(byte-order 敏感),而且很脆弱。将 IPv6 地址转换为字符串(如果映射为 IPv4,则为 ::ffff:a.b.c.d
,或者十六进制表示法为 aaaa:bbbb:cccc:dddd:eeee:ffff:gggg:hhhh
),并将端口转换为十进制数,然后再次使用 getaddrinfo()
从中得到合适的套接字结构。