programing

sin_addr을 수행합니다.s_addr = INADDR_ANY; httonl이 필요합니까?

newsource 2023. 9. 2. 08:31

sin_addr을 수행합니다.s_addr = INADDR_ANY; httonl이 필요합니까?

두 가지 실마리를 발견했습니다.

recv-timeout이 있는 소켓:이 코드에 무슨 문제가 있습니까?

c에서 FILE 스트림을 사용하여 소켓 읽기/쓰기

를 쓰다htonl다른 하나는 그렇지 않습니다.

어느 것이 옳은가요?

왜하면 다상른수은들냐와 같은 다른 이 있기 INADDR_LOOPBACK호스트 바이트 순서입니다. 이 계열의 모든 상수는 다음과 같아야 합니다.htonl다음을 포함하여 그들에게 적용됩니다.INADDR_ANY.

(참고: 저는 @Mat이 편집하는 동안 이 답변을 썼습니다. 그의 답변은 또한 일관성 있고 항상 사용하는 것이 더 좋다고 말합니다.htonl.)

이론적 근거

다음과 같이 코드를 작성하면 향후 코드 유지 관리자에게 위험합니다.

if (some_condition)
    sa.s_addr = htonl(INADDR_LOOPBACK);
else
    sa.s_addr = INADDR_ANY;

만약 내가 이 코드를 검토하고 있다면, 나는 즉시 왜 상수들 중 하나가htonl적용되고 다른 하나는 적용되지 않습니다.그리고 나는 그것을 버그로 보고할 것이다, 내가 우연히 그것을 가지고 있든 없든.INADDR_ANY항상 0이므로 변환하지 않습니다.

작성하는 코드는 올바른 런타임 동작을 갖는 것에 관한 것일 뿐만 아니라, 가능한 한 명확하고 정확하다고 믿기 쉬워야 합니다.이러한 이유로 당신은 그것을 벗기지 말아야 합니다.htonl 부근에INADDR_ANY를 사용하지 세 htonl제가 볼 수 있는 것은 다음과 같습니다.

  1. 숙련된 소켓 프로그래머가 사용하는 것은 불쾌할 수 있습니다.htonl왜냐하면 그들은 그것이 아무것도 하지 않는다는 것을 알게 될 것이기 때문입니다(그들은 상수의 값을 암기하고 있기 때문입니다).
  2. 그것을 생략하는 것은 타이핑을 덜 필요합니다.
  3. 가짜 "성능" 최적화(분명히 문제가 되지 않음).

INADDR_ANY는 IPV4의 "임의의 주소"입니다.는 그주는입니다.0.0.0.0 점선으로 선점표으로법, 래서그기.0x000000임의의 엔디엔시티에 16진수로.그것을 통과시키는 것htonl효과가 없습니다.

이제 다른 매크로 상수에 대해 궁금하다면,INADDR_LOOPBACK플랫폼에 정의되어 있는 경우.다음과 같은 매크로가 될 가능성이 있습니다.

#define INADDR_LOOPBACK     0x7f000001  /* 127.0.0.1   */

((으)부터linux/in.h에 있어서의 동등한 정의.winsock.h).

그러니깐INADDR_LOOPBACK하나의htonl필요합니다.

일관성을 위해, 따라서 사용하는 것이 더 나을 수 있습니다.htonl어떤 경우에도

둘 다 옳지 않아요, 둘 다INADDR_ANY그리고.htonl사용되지 않으며 IPv4에서만 작동하는 복잡하고 추한 코드로 이어집니다.사으로환 사용으로 합니다.getaddrinfo모든 소켓 주소 생성 요구사항을 충족합니다.

struct addrinfo *ai, hints = { .ai_flags = AI_PASSIVE|AI_ADDRCONFIG };
getaddrinfo(0, "1234", &hints, &ai);

를 바꿉니다."1234"포트 번호 또는 서비스 이름을 입력합니다.

는 스티스사용을 합니다.htonl(INADDR_ANY)UNIX 네트워크 프로그래밍이라는 책에서 일관되게.

FreeBfreeB SD는 12 SD를 합니다.INADDR_의 수의 상수netinet/in.h12개 중 9개가 필요합니다.htonl()적절한 기능을 위해.(9개는INADDR_LOOPBACK및 다음과 같은 8개의 다른 멀티캐스트 그룹 주소INADDR_ALLHOSTS_GROUP그리고.INADDR_ALLMDNS_GROUP.)

실제로 사용하든 사용하든 아무런 차이가 없습니다.INADDR_ANY또는htonl(INADDR_ANY)가능한 성능 타격 이외에htonl() 그 하지 않을 수 . -- 의 64비트 그리그가성히존도비재않트차을지있수다제. --트 64니고습.gcc 4.2.1 레벨을 것은 타임을 활성화하는 것처럼 .htonl()상수의 변환

이론적으로 일부 구현자가 다시 정의할 수 있습니다.INADDR_ANYhtonl()실제로 무언가를 하지만, 그러한 변화는 수만 개의 기존 코드 조각을 파괴할 것이고 "현실 세계"에서 살아남지 못할 것입니다.으로 또는 합니다.INADDR_ANY일종의 0 값 정수로 정의됩니다.스티븐스는 누군가가 그 일을INADDR_ANY그가 다음과 같이 쓸 때 항상 0입니다.

cli_addr.sin_addr.s_addr = htonl(INADDR_ANY);
cli_addr.sin_port        = htons(0);

를 사용하여 하는 경우bind우리는 인터넷 주소를 다음으로 설정합니다.INADDR_ANY16비트 인터넷 포트를 0으로 설정합니다.

댓글로 덧붙이려고 했는데 좀 장황하게...

저는 여기에 있는 답변과 논평을 통해 분명히 알 수 있다고 생각합니다.htonl().INADDR_ANY그리고.INADDR_NONE작동하지 않는 것과 같습니다.).입니다. - 제가 . 하지만 저는 에서 에 에 대해 을 본 적이 . 누군가 제가 단순히 누락했다면 수정해 주십시오. 하지만 저는 man 페이지나 include 헤더에서 이에 대해 명시적으로 정의하는 것을 본 적이 없습니다.INADDR_*호스트 순서입니다.다시 말하지만, 큰 문제는 아닙니다.INADDR_ANY,INADDR_NONE,그리고.INADDR_BROADCAST하지만 그것은 중요합니다.INADDR_LOOPBACK.

저는 C에서 낮은 수준의 소켓 작업을 꽤 많이 했지만 루프백 주소가 제 코드에서 사용되는 경우는 거의 없습니다.비록 이 주제가 1년이 넘었지만, 바로 이 문제가 오늘 저를 뒤에서 괴롭히기 시작했습니다. 그리고 그것은 포함 헤더에 정의된 주소가 네트워크 순서로 되어 있다는 잘못된 가정을 했기 때문입니다.내가 왜 그런 생각을 했는지 확실하지 않습니다 - 아마도 왜냐하면.in_addr는 네트워크.inet_aton그리고.inet_addr네트워크 순서로 값을 반환하므로 이러한 상수를 그대로 사용할 수 있다는 것이 논리적인 가정이었습니다.그 이론을 테스트하기 위해 빠른 5라인을 함께 사용하는 것은 제게 다른 것을 보여주었습니다. 어떤 가 이것을, 저는 그 호스트 순서로과, 그리고 그 값들을 것을 입니다.htonl()그들에게 적용되어야 합니다.일관성을 위해, 다른 사람들이 여기서 이미 그랬던 것처럼, 저는 또한 제안하고 싶습니다.htonl() 것에사다니용됩든에 됩니다.INADDR_*값에 아무런 영향을 미치지 않더라도 값.

이전 답변 중 최신 답변이 없는 것 같고 제가 이 질문 페이지를 마지막으로 볼 사람이 아닐 수도 있기 때문에 조금만 요약해 보겠습니다.INADDR_ANY 상수 주변에서 htonl을 사용하거나 완전히 피하는 것에 대해 찬반 의견이 있었습니다.

요즘은 시스템 라이브러리가 대부분 IPv6을 지원하므로 IPv6뿐만 아니라 IPv4도 사용합니다.데이터 구조와 상수가 바이트 순서에 영향을 받지 않기 때문에 IPv6의 상황은 훨씬 쉽습니다.하나는 'in6addr_any'와 'in6addr_loopback'(모두 6_addr 유형의 구조)을 사용하며 둘 다 네트워크 바이트 순서의 상수 개체입니다.

IPv6가 동일한 문제를 겪지 않는 이유 확인(IPv4 주소가 4바이트 어레이로 정의된 경우에도 문제가 발생하지 않음):

struct in_addr {
    uint32_t       s_addr;     /* address in network byte order */
};

struct in6_addr {
    unsigned char   s6_addr[16];   /* IPv6 address */
};

IPv4의 경우 'inaddr_any' 및 'inaddr_loopback'을 'structin_addr' 상수로 사용하면 좋습니다(memcmp와 비교하거나 memcpy로 복사할 수도 있음).실제로 glibc 및 기타 라이브러리에서 제공하지 않으므로 프로그램에서 생성하는 것이 좋을 수 있습니다.

const struct in_addr inaddr_loopback = { htonl(INADDR_LOOPBACK) };

로 만들 수 static), 로서htonl매크로가 아니라 일반 함수입니다.

문제는 glibc(다른 답변에서 주장된 것과 대조적으로)가 thtonl을 매크로로 제공하는 것이 아니라 함수로 제공한다는 것입니다.따라서 다음 작업을 수행해야 합니다.

static const struct in_addr inaddr_any = { 0 };
#if BYTE_ORDER == BIG_ENDIAN
static const struct in_addr inaddr_loopback = { 0x7f000001 };
#elif BYTE_ORDER == LITTLE_ENDIAN
static const struct in_addr inaddr_loopback = { 0x0100007f };
#else
    #error Neither big endian nor little endian
#endif

헤더에 추가하면 IPv4 상수를 IPv6에서 최대한 쉽게 사용할 수 있습니다.

하지만 이를 구현하기 위해서는 상수를 사용하여 초기화해야 했습니다.각각의 바이트를 정확하게 알면 상수가 필요 없습니다.몇몇 사람들이 주장하는 것처럼.htonl()0으로 평가되는 상수에 대해 중복되며, 다른 사람도 상수 자체가 중복이라고 주장할 수 있습니다.그리고 그가 옳을 것입니다.

코드에서 저는 암묵적인 것보다 명시적인 것을 선호합니다.따라서 이러한 상수(예: INADDR_ANY, INADDR_ALL, INADDR_LOOPBACK)가 모두 호스트 바이트 순서로 일치하는 경우에는 이렇게 처리해야 합니다.위의 상수를 사용하지 않는 경우 예를 참조하십시오.

struct in_addr address4 = { htonl(use_loopback ? INADDR_LOOPBACK : INADDR_ANY };

물론 당신은 당신이 전화할 필요가 없다고 말할 수 있습니다.htonlINADDR_ANY의 경우 다음을 수행할 수 있습니다.

struct in_addr address4 = { use_loopback ? htonl(INADDR_LOOPBACK) : INADDR_ANY };

하지만 어쨌든 0이기 때문에 상수의 바이트 순서를 무시할 때, 저는 상수를 사용하는 것에 대한 논리를 전혀 이해하지 못합니다.INADDR_ALL도 마찬가지입니다. 0xffffff를 쉽게 입력할 수 있기 때문에

이 문제를 해결하는 또 다른 방법은 이러한 값을 직접 설정하지 않는 것입니다.

struct in_addr address4;

inet_pton(AF_INET, "127.0.0.1", &address4);

이것은 약간의 쓸모없는 처리를 추가하지만 바이트 순서 문제가 없으며 IPv4와 IPv6(주소 문자열만 변경)에 대해서도 사실상 동일합니다.

하지만 문제는 당신이 왜 그런 짓을 하느냐는 것입니다.원하신다면connect()IPv4 localhost(때로는 IPv6 localhost 또는 어떤 호스트 이름으로도 가능)에 대한 getaddrinfo()(답변 중 하나에 언급됨)가 훨씬 낫습니다.

  1. 이름를 일치하는 입니다.struct addrinfo기록.

  2. struct addrinfo에는 에대다포포함에 대한 되어 있습니다.struct sockaddr직접사수있는과 함께 직접 할 수 connect()그러므로 당신은 건설에 대해 신경 쓸 필요가 없습니다.struct sockaddr_in) (으)로 타이핑합니다struct sockaddr 타기.

    struct addrinfo *ai, hints = {.ai_family = AF_INET }; getaddrinfo(0, "1234", "", "ai);

    포인터를 하는 것을 합니다.struct sockaddr신이필요하구조는로당에 .connect()콜.콜.

결론은 다음과 같습니다.

사용할 수 있는 하지 못합니다.struct in_addr상수(호스트 순서에서 다소 쓸모없는 부호 없는 정수 상수를 제공하는 경우).

struct addrinfo *ai, hints = { .ai_family = AF_INET, .ai_protocol = IPPROTO_TCP };
int error;

error = getaddrinfo(NULL, 80, &hints, &ai);
if (error)
    ...

for (item = result; item; item = item->ai_next) {
    sock = socket(item->ai_family, item->ai_socktype, item->ai_protocol);

    if (sock == -1)
        continue;

    if (connect(sock, item->ai_addr, item->ai_addrlen) != -1) {
        fprintf(stderr, "Connected successfully.");
        break;
    }

    close(sock);
}

쿼리가 하나의 결과만 반환할 정도로 선택적인 경우 다음 작업을 수행할 수 있습니다(간단히 설명하기 위해 오류 처리 생략).

struct *result, hints = { .ai_family = AF_INET, .ai_protocol = IPPROTO_TCP };
getaddrinfo(NULL, 80, &hints, &ai);
sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
connect(sock, result->ai_addr, result->ai_addrlen);

렵다면이 ,getaddrinfo()상수를 사용하는 것보다 훨씬 느릴 수 있습니다. 시스템 라이브러리가 이 문제를 해결하는 데 가장 적합합니다.입니다.service 및 null입니다.hints.ai_family설정됩니다.

저는 이미 "상당한" 대답이 있을 때 보통 대답하는 것을 좋아하지 않습니다.이 경우 답변에 추가한 정보가 잘못 전달되고 있으므로 예외로 하겠습니다.

INADDR_ANY는 0비트 0비트 IPv4 주소로 됩니다.0.0.0.0또는0x00000000 중.htonl()이 값은 동일한 값 0이 됩니다. 그므로기, 하전화러를 부릅니다.htonl()이 상수 값은 기술적으로 필요하지 않습니다.

INADDR_ALL전체 1비트 IPv4 주소로 정의됩니다.255.255.255.255또는0xFFFFFFFF 중.htonl()와 함께INADDR_ALL돌아올 것입니다INADDR_ALL 시다, 하기전에 전화하기htonl()기술적으로 필요하지 않습니다.

된 또 는 " 헤파정다상른다같음다습니과수는의"입니다.INADDR_LOOPBACK로 정의된.127.0.0.1또는0x7F000001이 주소는 네트워크 바이트 순서로 제공되며, 다음과 같이 소켓 인터페이스에 전달할 수 없습니다.htonl()사용해야 합니다.htonl()이 상수로

어떤 사람들은 프로그래머들이 사용하는 일관성과 코드 가독성을 요구한다고 제안할 것입니다.htonl()명명된 임의의 상수에 대하여INADDR_*그들 중 일부에게 필요하기 때문입니다.이 포스터들은 틀렸습니다.

이 스레드의 예는 다음과 같습니다.

if (some_condition)
    sa.s_addr = htonl(INADDR_LOOPBACK);
else
    sa.s_addr = INADDR_ANY;

"존 즈윈크"의 인용구:

"만약 제가 이 코드를 검토하고 있다면, 저는 왜 상수 중 하나가 hashtonl을 적용하고 다른 하나는 적용하지 않는지 즉시 의문을 제기할 것입니다.그리고 INADR_ANY가 항상 0이므로 변환이 불가능하다는 "내부 지식"이 있는지 여부에 관계없이 버그로 보고합니다.그리고 저는 다른 많은 유지 관리자들도 그렇게 할 것이라고 생각합니다.

만약 제가 그런 버그 리포트를 받고 있다면, 저는 즉시 그것을 버릴 것입니다.이 과정은 저에게 많은 시간을 절약해 줄 것입니다. "기본적인 최소 지식"이 없는 사람들의 버그 보고서를 제출합니다.INADDR_ANY항상 0입니다. (의 값을 알고 있음을 시사함)INADDR_ANY등. 어떻게든 캡슐화 또는 다른 비캡슐화를 위반합니다. 동일한 번호가 사용됩니다.netcat출력 및 커널 내부.프로그래머는 실제 수치를 알아야 합니다.모르는 사람은 내면의 지식이 부족한 것이 아니라 그 지역에 대한 기본적인 지식이 부족합니다.

정말로, 만약 당신이 소켓 코드를 유지하는 프로그래머가 있고, 그 프로그래머가 INADDR_ANY와 INADDR_ALL의 비트 패턴을 모른다면, 당신은 이미 곤경에 처해 있습니다.0을 반환하는 매크로에서 0을 랩핑하는 것은 의미 없는 일관성에 종속되고 도메인 지식을 존중하지 않는 일종의 심리입니다.

소켓 코드를 유지하는 것은 C를 이해하는 것 이상입니다.사이의 차이를 이해하지 못하는 경우INADDR_LOOPBACK그리고.INADDR_ANY와 양립할 수 있는netstat출력, 그러면 당신은 그 코드에서 위험하고 그것을 변경해서는 안 됩니다.

불필요한 사용과 관련하여 Zwinck가 제안한 스트로맨 논쟁htonl():

  1. 경험이 풍부한 소켓 프로그래머가 htonl을 사용하는 것은 그들이 아무것도 하지 않는다는 것을 알기 때문에 불쾌할 수 있습니다(그들은 상수의 값을 암기하고 있기 때문입니다).

경험이 풍부한 소켓 프로그래머들이 그 가치를 알고 있다는 묘사가 있기 때문에 이것은 단순한 주장입니다.INADDR_ANY외워서이것은 숙련된 C 프로그래머만이 가치를 알고 있다는 글과 같습니다.NULL외워서"마음에 따라"를 쓰는 것은 그 숫자가 외우기에 약간 어렵다는 인상을 줍니다. 아마도 몇 자리, 예를 들면,127.0.0.1하지만 우리는 "모든 0비트"와 "모든 1비트"라는 이름의 패턴을 기억하는 것의 어려움에 대해 과장되게 논의하고 있습니다.

이러한 수치가 다음과 같은 출력에 나타난다는 점을 고려하면,netstat그리고 다른 시스템 유틸리티들, 그리고 또한 이러한 값들의 일부가 IP 헤더에 나타난다는 것을 고려할 때, 이러한 값들을 심장이든 뇌든 모르는 유능한 소켓 프로그래머 같은 것은 없습니다.실제로 이러한 기본 사항을 모르고 소켓 프로그래밍을 시도하면 네트워크 가용성에 위험할 수 있습니다.

  1. 그것을 생략하는 것은 타이핑을 덜 필요합니다.

이 주장은 터무니없고 무시하기 위한 것이므로 많은 반박이 필요하지 않습니다.

  1. 가짜 "성능" 최적화(분명히 문제가 되지 않음).

이 주장이 어디서 나왔는지 알기 어렵습니다.그것은 야당에게 바보같이 보이는 주장을 제공하려는 시도일 수 있습니다.어떤 경우에도, 사용하지 않는 것.htonl()매크로는 상수를 제공하고 일반적인 C 컴파일러를 사용할 때 성능에 아무런 차이가 없습니다. 상수 식은 두 경우 모두 상수로 줄어듭니다.


사용하지 않는 이유htonl()INADDR_ANY를 사용하면 대부분의 경험이 많은 소켓 프로그래머들은 필요하지 않다는 것을 알고 있습니다.게다가, 모르는 프로그래머들은 배울 필요가 있습니다.사용 시 추가 "비용"은 없습니다.htonl()문제는 그러한 결정적으로 중요한 가치에 대한 무지를 조장하는 코딩 표준을 확립하는 비용입니다.

정의에 따르면 캡슐화는 무지를 조장합니다.바로 그 무지함이 캡슐화된 인터페이스를 사용하는 일반적인 이점입니다. 지식은 비싸고 유한하기 때문에 캡슐화는 일반적으로 좋습니다.문제는 캡슐화를 통해 어떤 프로그래밍의 노력이 가장 잘 강화되는가 하는 것입니다.캡슐화에 의해 관찰되는 프로그래밍 작업이 있습니까?

사용하는 것이 기술적으로 올바르지 않습니다.htonl()이 값에 영향을 미치지 않기 때문입니다.그러나 사용해야 한다는 주장은 오해의 소지가 있을 수 있습니다.

개발자가 그것을 알 필요가 없는 더 나은 상황이 될 것이라고 주장하는 사람들이 있습니다.INADDR_ANY모두 0 등입니다.이 무지의 땅은 더 나은 것이 아니라 더 나쁜 것입니다.이러한 "매직 값"은 TCP/IP와의 다양한 인터페이스에서 사용됩니다.예를 들어 Apache를 구성할 때 IPv6이 아닌 IPv4만 수신하려면 다음을 지정해야 합니다.

Listen 0.0.0.0:80

나는 실수로 로컬 IP 주소를 제공한 프로그래머들을 만났습니다.INADDR_ANY(0.0.0.0) 위.이 프로그래머들은 무엇이INADDR_ANY네, 그리고 그들은 아마 그것을 포장할 것입니다.htonl()그들이 그것을 하는 동안.이 땅은 추상적인 사고와 캡슐화의 땅입니다.

"캡슐화"와 "추상"의 아이디어는 널리 받아들여졌고 너무 광범위하게 적용되었지만 항상 적용되는 것은 아닙니다.IPv4 주소 지정 영역에서 이러한 상수 값을 "추상적"으로 취급하는 것은 적절하지 않습니다. 즉, 와이어에서 직접 비트로 변환됩니다.


제 요점은 다음과 같습니다. "올바른" 사용법은 없습니다.INADDR_ANY와 함께htonl()둘 다 동등합니다.나는 그 가치가 특정한 방법으로 사용되어야 한다는 요구사항을 채택하는 것을 추천하지 않을 것입니다, 왜냐하면INADDR_X상수의 가족은 단지 4명의 구성원을 가지고 있고, 그들 중 하나만 가지고 있습니다.INADDR_LOOPBACK바이트 순서에 따라 값이 다릅니다.값의 비트 패턴을 "눈감아주는" 값을 사용하기 위한 표준을 확립하는 것보다 이 사실을 아는 것이 더 좋습니다.

다른 많은 API에서는 API가 사용하는 상수의 숫자 값이나 비트 패턴을 알지 못한 채 프로그래머가 진행하는 것이 중요합니다.소켓 API의 경우 이러한 비트 패턴과 값이 입력으로 사용되어 널리 표시됩니다.이러한 값을 수치적으로 아는 것이 사용에 대해 생각하는 데 시간을 보내는 것보다 낫습니다.htonl()그들 위에.

특히 C에서 프로그래밍할 때 소켓 API의 대부분의 "사용"은 다른 사람의 소스 코드를 잡고 이를 적용하는 것을 포함합니다.이것은 무엇을 아는 것이 매우 중요한 또 다른 이유입니다.INADDR_ANY그것을 사용하는 선을 터치하기 전입니다.

언급URL : https://stackoverflow.com/questions/6081892/does-sin-addr-s-addr-inaddr-any-need-htonl-at-all