[英]Haskell: Unsupported operation (Address family not supported by protocol family)
我有這段代碼連接到本地石墨(實際上只是在localhost上運行的nc -l -p 2023
):
getCarbonAddr :: Config -> IO SockAddr
getCarbonAddr cfg = do
let host = (graphiteHost . graphiteConfig) cfg
let port = (graphitePort . graphiteConfig) cfg
-- addrInfos <- getAddrInfo (Just defaultHints)
addrInfos <- getAddrInfo Nothing
(Just host)
(Just (show port))
putStrLn $ "addrInfos: " ++ show addrInfos
c <- case addrInfos of
(addrInfo : _) -> return (addrAddress addrInfo)
_ -> unsupportedAddressError host
return c
where
unsupportedAddressError h = ioError $ userError $
"unsupported address: " ++ h
主機和端口的配置值分別為“localhost”和2023。 當我在升級到Yosemite后在我的OS X上運行它時 - 我看到以下崩潰:
addrInfos: [AddrInfo {addrFlags = [], addrFamily = AF_INET6, addrSocketType = Datagram, addrProtocol = 17, addrAddress = [::1]:2023, addrCanonName = Nothing},AddrInfo {addrFlags = [], addrFamily = AF_INET6, addrSocketType = Stream, addrProtocol = 6, addrAddress = [::1]:2023, addrCanonName = Nothing},AddrInfo {addrFlags = [], addrFamily = AF_INET, addrSocketType = Datagram, addrProtocol = 17, addrAddress = 127.0.0.1:2023, addrCanonName = Nothing},AddrInfo {addrFlags = [], addrFamily = AF_INET, addrSocketType = Stream, addrProtocol = 6, addrAddress = 127.0.0.1:2023, addrCanonName = Nothing},AddrInfo {addrFlags = [], addrFamily = AF_INET6, addrSocketType = Datagram, addrProtocol = 17, addrAddress = [fe80::1%lo0]:2023, addrCanonName = Nothing},AddrInfo {addrFlags = [], addrFamily = AF_INET6, addrSocketType = Stream, addrProtocol = 6, addrAddress = [fe80::1%lo0]:2023, addrCanonName = Nothing}]
LocalJob: connect: unsupported operation (Address family not supported by protocol family)
這對我來說似乎很奇怪,所以我決定運行這個C程序(用google搜索“getaddrinfo”示例,更改主機名和端口,添加了ai_family的打印):
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#ifndef NI_MAXHOST
#define NI_MAXHOST 1025
#endif
int main(void)
{
struct addrinfo *result;
struct addrinfo *res;
int error;
/* resolve the domain name into a list of addresses */
error = getaddrinfo("localhost", "2023", NULL, &result);
if (error != 0)
{
fprintf(stderr, "error in getaddrinfo: %s\n", gai_strerror(error));
return EXIT_FAILURE;
}
/* loop over all returned results and do inverse lookup */
for (res = result; res != NULL; res = res->ai_next)
{
char hostname[NI_MAXHOST] = "";
error = getnameinfo(res->ai_addr, res->ai_addrlen, hostname, NI_MAXHOST, NULL, 0, 0);
if (error != 0)
{
fprintf(stderr, "error in getnameinfo: %s\n", gai_strerror(error));
continue;
}
if (*hostname != '\0')
printf("hostname: %s. ai_family: %i\n", hostname, res->ai_family);
}
freeaddrinfo(result);
return EXIT_SUCCESS;
}
啟動后,我看到了這個輸出:
➜ getaddrinfotest ./main
hostname: localhost. ai_family: 30
hostname: localhost. ai_family: 30
hostname: localhost. ai_family: 2
hostname: localhost. ai_family: 2
hostname: localhost. ai_family: 30
hostname: localhost. ai_family: 30
所以,ai_family 30似乎是一件奇怪的事情。 據我所知,從socket.h來源 ,它是AF_TIPC協議,這是我以前從未聽說過的一件非常罕見的事情。 我還打開了haskell的packFamily'來源,並且驚訝地看到它沒有處理30的值(不知道AF_TIPC)。
我的問題是:現在最好的事情是什么? 我是否正確地解決了問題? haskell應該更好地處理未知的ai家庭嗎? // 謝謝!
更新:我通過提示使用ipv4解決了一個問題:
addrInfos <- getAddrInfo (Just (defaultHints { addrFamily=AF_INET }))
(Just host)
(Just (show port))
但我仍然想知道如何以“正確的方式”解決這個問題。
默認情況下, getaddrinfo
(及其Haskell綁定getAddrInfo
)返回addrinfo的鏈接列表,該列表可以包含IPv4地址,IPv6地址或兩者的混合。 不幸的是, 套接字函數不允許將IPv4套接字連接到IPv6地址,因此當您遍歷地址列表時,需要創建一個正確類型的套接字:
addrinfos <- getAddrInfo Nothing (Just hostname) (Just (show port))
let first = head addrinfos
sock <- socket (addrFamily first) Stream defaultProtocol
connect sock (addrAddress first)
注意如何(addrFamily first)
傳遞給socket
函數,以便在正確的協議族中創建套接字。
在實際代碼中,您將要遍歷列表addrinfos
並嘗試連接到getAddrInfo
返回的所有地址。 當您這樣做時,不要忘記關閉連接失敗的套接字。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.