Local IP address 구하기

60 views
Skip to first unread message

김승범

unread,
Oct 25, 2002, 3:53:33 AM10/25/02
to
Local IP address를 구하는 C 코드입니다.
윈도우의 Visual C++ 6.0와 리눅스의 GCC 3.0으로 테스트해 보았습니다.

누가 물어본 김에 답해 주면서 만들었는데, 여기저기서 자주 문의되는 것
같아서 올려봅니다. 의견이 있으시면 말씀해 주시면 감사하겠습니다.

[여러 그룹에 교차투고합니다만, 딱 맞는 하나의 그룹을 찾기가 어려워서
Followup-To는 지정하지 않았습니다.]


#ifdef _WIN32
#include <winsock.h>
#else
#include <string.h>
#include <netinet/in.h>
#include <unistd.h>
#include <netdb.h>
#endif

struct in_addr get_local_ip_address(void)
{
struct in_addr local_ip_address;
char slocal[256];
struct hostent *hos;

if (
gethostname(slocal, sizeof slocal) != -1/*SOCKET_ERROR*/
&& (hos = gethostbyname(slocal)) != NULL
)
memcpy(&local_ip_address, hos->h_addr, 4);
else
local_ip_address.s_addr = INADDR_NONE; /* error */

return local_ip_address;
}

#include <stdlib.h>
#include <stdio.h>
#ifndef _WIN32
#include <arpa/inet.h>
#endif

int main()
{
#ifdef _WIN32
WSAData wsaData;
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
return EXIT_FAILURE;
#endif

puts(inet_ntoa(get_local_ip_address()));

#ifdef _WIN32
WSACleanup();
#endif
return EXIT_SUCCESS;
}

--
김승범

parkkh

unread,
Oct 27, 2002, 11:09:06 PM10/27/02
to

VC++ MFC 환경에서 제가 쓰는 함수입니다..
쫌 무식합니다만...
ip 가 여러개인 경우를 처리하도록 하였습니다.

stdafx.h 에
#include <afxtempl.h>
#include <winsock.h>

가 들어가야 합니다.

개선할사항 있으면 꼬리달아 주세요..


int GetIPList(CStringArray& ipList, bool bHexType, bool bUseDot)
{
char szHostName[1024];
CString strHostName;
CString strIPAddr = "";
int i;

if (! ::gethostname(szHostName, 1024))
{
strHostName = szHostName;
struct hostent* pHost;

pHost = gethostbyname(szHostName);
if (pHost != NULL)
{
for(i=0;i<255;i++)
{
if (pHost->h_addr_list[i])
{
BYTE bAddr[4];
CString strIP;
bAddr[0] = (BYTE) pHost->h_addr_list[i][0];
bAddr[1] = (BYTE) pHost->h_addr_list[i][1];
bAddr[2] = (BYTE) pHost->h_addr_list[i][2];
bAddr[3] = (BYTE) pHost->h_addr_list[i][3];

if(bHexType)
{
if(bUseDot)
strIPAddr.Format("%02x.%02x.%02x.%02x", bAddr[0], bAddr[1], bAddr[2],
bAddr[3]);
else
strIPAddr.Format("%02x%02x%02x%02x", bAddr[0], bAddr[1], bAddr[2],
bAddr[3]);
}
else
strIPAddr.Format("%d.%d.%d.%d", bAddr[0], bAddr[1], bAddr[2],
bAddr[3]);
ipList.Add(strIPAddr);
}
else
break;
}
}
}
return ipList.GetSize();
}

"김승범" <musi...@bawi.org> wrote in message
news:3DB8F87D...@bawi.org...

김승범

unread,
Oct 28, 2002, 1:23:05 AM10/28/02
to
parkkh wrote:
>
> VC++ MFC 환경에서 제가 쓰는 함수입니다..
> 쫌 무식합니다만...
> ip 가 여러개인 경우를 처리하도록 하였습니다.

예, 원칙적으로는 IP 주소가 호스트마다가 아니라 네트워크 인터페이스마다
할당되기 때문에 IP 주소가 여럿 있는 경우가 있을 수 있고, 따라서 이에
대한 처리를 해 주는 것이 옳습니다.

>
> stdafx.h 에
> #include <afxtempl.h>
> #include <winsock.h>
>
> 가 들어가야 합니다.
>
> 개선할사항 있으면 꼬리달아 주세요..
>
> int GetIPList(CStringArray& ipList, bool bHexType, bool bUseDot)

1. 문자열로 표현 여부

IP 주소의 문자열 표현의 CStringArray를 결과값으로 주게 하셨는데
이보다는 struct in_addr의 모음을 돌려주는 것이 더 나은 설계라고
생각합니다. 문자열로 표현하는 것은 꼭 필요하지 않을 때도 많은데
문자열 표현을 받아서 이를 다시 struct in_addr로 변환해야 하는 경우를
생각해 보면 문자열 표현은 낭비라고 느껴집니다. 한 가지 함수는
한 가지 일만 처리하도록 해야 한다는 원칙도 있습니다만.

IP 주소는 struct in_addr로 표현하는 것이 정석이고, 이를 문자열로
변환하는 것은 또다른 함수가 해야 할 일이겠지요. 이미 inet_ntoa와
같은 함수도 준비되어 있고, 필요한 경우 bHexType, bUseDot 등의 인자를
받아서 구미에 맞게 변환하는 다른 함수를 만들어 쓸 수도 있겠습니다.

2. 결과값 반환 방식

GetIPList 함수가 CStringArray&를 인자로 받아서 여기에 추가를 한다면
GetIPList 함수를 호출할 때 비어 있지 않은 CStringArray를 넘겨주면
어떻게 되는가 하는 점을 생각해보아야 하겠습니다.

만약 이전 내용을 무시한다면 GetIPList 시작 부분에 ipList.RemoveAll()을
호출하여 내용을 비우고 시작하는 것이 좋겠고, 이 경우 반환값은 별 의미가
없으므로 void로 처리하는 것이 좋아 보입니다.
그렇지 않고 만약 이전 내용에 덧붙이는 것으로 설계하셨다면, 반환값으로는
GetSize(), 즉 이전에 있던 내용에 새로 더해진 것까지 합친 개수가 아니라
이 함수를 통해서 더해진 개수만 돌려주는 것이 더 의미있으리라 생각합니다.
(사실 이것도 그리 큰 의미가 있을 것 같지는 않습니다만.)
이 경우 GetIPList 내부에 별도로 카운터 변수를 하나 쓰셔야겠지요.

아니면 함수의 반환값 자체를 CStringArray로 만드시면 깔끔해집니다.
다만 VC++가 반환값 최적화(RVO: return value optimization)을 제대로
수행하는지 알아볼 필요가 있겠습니다. 하긴 RVO가 안 된다고 하더라도
어차피 반환될 값이 그리 덩치 큰 건 아니니 별 상관 없을 수도 있습니다만.

3. MFC vs STL

MFC를 사용하느냐 STL을 사용하느냐는 취향으로 볼 수도 있겠지만,
이 코드에서는 전반적으로 MFC가 그리 중요한 비중을 차지하고 있지
않으므로 아예 MFC를 쓰지 않고 만드는 것도 괜찮을 것 같습니다.
이 경우 STL의 vector, deque, list 등을 사용할 수 있습니다.
(사실은 의미상 set을 쓰고 싶지만, struct in_addr 형의 대상체 사이의
대소 관계가 정의되어 있지 않고 이를 따로 정의하려면 귀찮기 때문에
고려 대상에서 제외했습니다.)

4. 결론

저라면 함수의 선언부를 다음과 같이 만들 것 같군요.

std::list<struct in_addr> GetIPList();

(C++에서는 struct 키워드를 생략할 수 있습니다만, struct in_addr은
C에서부터 워낙 익숙하기 때문에 그냥 그대로 썼습니다. ;-))

> {
> char szHostName[1024];
> CString strHostName;
> CString strIPAddr = "";
> int i;

아래 세 변수는 좀더 안쪽 scope로 옮길 수 있으므로 그렇게 하는 것이
좋겠습니다. scope는 최대한 안쪽에 작게 잡는 것이 바람직하지요.

- strHostName은 (사용되지 않으므로 필요없습니다만) if (gethostname..)
블럭 안에 넣을 수 있습니다.
- strIPAddr는 그 안의 if (gethostbyname..) 블럭 안에 넣을 수 있습니다.
그리고 굳이 = "" 로 초기화를 할 필요가 없습니다. (더구나 나중에
CString::Format 함수에 의해서 내용이 다시 바뀌기 때문에 더욱
그렇지요.)
- for 루프 안에 쓰이는 i는 for 안으로 넣는 것이 바람직합니다.
for (int i = 0; i < 255; i++)

>
> if (! ::gethostname(szHostName, 1024))

멤버 함수 안도 아닌데 왜 굳이 ::를 쓰셨는지 모르겠습니다.
API 함수라면 무조건 ::를 붙이는 경향도 있는 것 같기는 하던데, 글쎄요,
취향 문제라 볼 수 있겠지만 저라면 괜히 ::를 붙이지는 않을 것 같네요.

그리고 1024와 같은 구체적인 숫자를 사용하면 나중에 szHostName의 크기를
바꿀 때 1024도 함께 바꾸는 귀찮음을 겪든지, 아니면 부정확한 값을 계속
사용하는 위험을 안게 됩니다. 대신 sizeof(szHostName)을 쓰세요.

또한 gethostname의 반환값은 참/거짓의 의미가 아니므로 논리 연산자 ! 를
사용하는 것은 보는 사람으로 하여금 오해를 하게 할 소지가 있습니다.
(딱 보면 마치 gethostname이 실패한 조건을 검사하는 것처럼 보이거든요.)
그보다는 == 0 을 사용하시는 것이 더 이해하기에 좋겠습니다.

if (gethostname(szHostName, sizeof szHostName) == 0)

> {
> strHostName = szHostName;

strHostName은 여기서 대입만 하고 더 이상 사용되는 곳이 없습니다.
따라서 없애셔도 되겠습니다.

> struct hostent* pHost;
>
> pHost = gethostbyname(szHostName);
> if (pHost != NULL)

참고삼아 말씀드리자면, 위 세 줄을 묶어서 다음과 같이 쓸 수 있습니다.

if (struct hostent* pHost = gethostbyname(szHostName))

> {
> for(i=0;i<255;i++)

굳이 i를 0부터 254까지 한정할 필요가 없을 것 같습니다.
hostent::h_addr_list는 zero-terminated array라고 되어 있으므로
(게다가 저 아래쪽에는 break 문까지 이미 들어있군요.)
for (int i=0; pHost->h_addr_list[i]; i++) 처럼 하는 것이 좋겠네요.

역시 취향 문제입니다만, 여기서는 i값 자체가 중요한 것이 아니므로
저라면 포인터를 사용하겠습니다.

for (const char* const* p = pHost->h_addr_list; *p; ++p)

> {
> if (pHost->h_addr_list[i])
> {
> BYTE bAddr[4];
> CString strIP;
> bAddr[0] = (BYTE) pHost->h_addr_list[i][0];
> bAddr[1] = (BYTE) pHost->h_addr_list[i][1];
> bAddr[2] = (BYTE) pHost->h_addr_list[i][2];
> bAddr[3] = (BYTE) pHost->h_addr_list[i][3];
>
> if(bHexType)
> {
> if(bUseDot)
> strIPAddr.Format("%02x.%02x.%02x.%02x", bAddr[0], bAddr[1], bAddr[2], bAddr[3]);
> else
> strIPAddr.Format("%02x%02x%02x%02x", bAddr[0], bAddr[1], bAddr[2], bAddr[3]);
> }
> else
> strIPAddr.Format("%d.%d.%d.%d", bAddr[0], bAddr[1], bAddr[2], bAddr[3]);

앞서 말씀드렸듯이 문자열 변환은 이 함수에서 할 일은 아닌 것 같고요.

> ipList.Add(strIPAddr);
> }
> else
> break;
> }
> }
> }
> return ipList.GetSize();
> }

앞서 말씀드린 내용과 관련됩니다만, 함수를 호출하는 쪽에서 얼마든지
GetSize()를 호출할 수 있기 때문에 GetSize()를 반환하는 것은 별 의미가
없어 보입니다.

--
김승범

김승범

unread,
Oct 28, 2002, 1:29:29 AM10/28/02
to
김승범 wrote:
>
> Local IP address를 구하는 C 코드입니다.
> 윈도우의 Visual C++ 6.0와 리눅스의 GCC 3.0으로 테스트해 보았습니다.
>
> 누가 물어본 김에 답해 주면서 만들었는데, 여기저기서 자주 문의되는 것
> 같아서 올려봅니다. 의견이 있으시면 말씀해 주시면 감사하겠습니다.
>
> [여러 그룹에 교차투고합니다만, 딱 맞는 하나의 그룹을 찾기가 어려워서
> Followup-To는 지정하지 않았습니다.]

IP 주소는 호스트별로가 아니라 네트워크 인터페이스별로 할당되기 때문에
하나의 호스트가 여러 개의 IP 주소를 갖는 경우가 있을 수 있습니다.
이에 대비하여 여러 개의 IP 주소를 반환하는 함수를 만들어 보았습니다.

이전에는 C 코드였는데 이제는 C++ 코드가 되었습니다. :)


#ifdef _WIN32
#include <winsock.h>
#else
#include <string.h>
#include <netinet/in.h>
#include <unistd.h>
#include <netdb.h>
#endif

#ifdef _MSC_VER
#pragma warning(disable: 4786)
#endif
#include <list>

typedef std::list<in_addr> in_addrs;

in_addrs get_local_ip_addresses(void)
{
in_addrs local_ip_addresses;
char slocal[256];

if (gethostname(slocal, sizeof slocal) == 0) {
if (hostent* hos = gethostbyname(slocal)) {
for (const char* const* p = hos->h_addr_list; *p; ++p) {
in_addr local_ip_address;
memcpy(&local_ip_address, *p, 4);
local_ip_addresses.push_back(local_ip_address);
}
}
}

return local_ip_addresses;
}

#include <stdlib.h>
#include <stdio.h>
#ifndef _WIN32
#include <arpa/inet.h>
#endif

int main()
{
#ifdef _WIN32
WSAData wsaData;
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
return EXIT_FAILURE;
#endif

in_addrs s = get_local_ip_addresses();
for (in_addrs::const_iterator i = s.begin(), i_ = s.end(); i != i_; ++i)
puts(inet_ntoa(*i));

김승범

unread,
Oct 28, 2002, 1:43:36 AM10/28/02
to
Win32에서는 local IP address를 구하는 전혀 다른 방법이 존재합니다.
WSAIoctl을 이용하여 SIO_GET_INTERFACE_LIST 요청을 하는 방법입니다.
이 방법을 사용하면 각 인터페이스별로 상태와 특성, interface 주소,
broadcast 주소, netmask 등을 알 수 있습니다.


[EXAMPLE CODE]

#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>

std::ostream& operator<<(std::ostream& os, const INTERFACE_INFO& ii)
{
os << ((ii.iiFlags & IFF_UP) ? 'U' : '-');
os << ((ii.iiFlags & IFF_BROADCAST) ? 'B' : '-');
os << ((ii.iiFlags & IFF_LOOPBACK) ? 'L' : '-');
os << ((ii.iiFlags & IFF_POINTTOPOINT) ? 'P' : '-');
os << ((ii.iiFlags & IFF_MULTICAST) ? 'M' : '-');
os << '\t' << inet_ntoa(ii.iiAddress.AddressIn.sin_addr);
os << '\t' << inet_ntoa(ii.iiBroadcastAddress.AddressIn.sin_addr);
os << '\t' << inet_ntoa(ii.iiNetmask.AddressIn.sin_addr);
return os;
}

#include <algorithm>
#include <cstdlib>

int main()
{
WSAData wsaData;
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
std::cerr << "WSAStartup error\n";
return EXIT_FAILURE;
}

SOCKET sd = WSASocket(AF_INET, SOCK_DGRAM, 0, 0, 0, 0);

INTERFACE_INFO InterfaceList[16];
DWORD nBytesReturned;
if (WSAIoctl(sd, SIO_GET_INTERFACE_LIST, 0, 0, &InterfaceList, sizeof(InterfaceList), &nBytesReturned, 0, 0) != 0) {
std::cerr << "ioctl error\n";
return EXIT_FAILURE;
}

std::copy(InterfaceList, InterfaceList + nBytesReturned / sizeof(INTERFACE_INFO), std::ostream_iterator<INTERFACE_INFO>(std::cout, "\n"));

WSACleanup();
return EXIT_SUCCESS;
}


[실행 예]

UB--M 192.168.0.1 255.255.255.255 255.255.255.0
UBL-M 127.0.0.1 255.255.255.255 255.0.0.0
UB-PM 211.201.125.143 255.255.255.255 255.255.255.255
||||| ~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
||||| interface 주소 broadcast 주소 netmask
|||||
`++++-- IFF_UP The interface is running.
`+++-- IFF_BROADCAST The broadcast feature is supported.
`++-- IFF_LOOPBACK The loopback interface is running.
`+-- IFF_POINTTOPOINT The interface is using point-to-point link.
`-- IFF_MULTICAST The multicast feature is supported.

--
김승범

신창호

unread,
Oct 30, 2002, 3:37:59 AM10/30/02
to
로컬에 대한 ip address를 나타내려면..
아래 코드로 해봤을때..루프백 주소만 나옵니다..

인터페이스에 해당 되는 주소를 알고 싶다면..
ioctl을 사용하셔야 하지 않는지...?

싶습니다..

"김승범" <musi...@bawi.org> wrote in message
news:3DB8F87D...@bawi.org...

김승범

unread,
Oct 30, 2002, 4:41:17 AM10/30/02
to
"신창호" wrote:
>
> 로컬에 대한 ip address를 나타내려면..
> 아래 코드로 해봤을때..루프백 주소만 나옵니다..
>
> 인터페이스에 해당 되는 주소를 알고 싶다면..
> ioctl을 사용하셔야 하지 않는지...?
>
> 싶습니다..

이미 그러한 내용을 반영한 코드를 다시 올렸습니다.
아래 기사를 읽어보시기 바랍니다.

news:3DBCD949...@bawi.org
news:3DBCDC98...@bawi.org

[구글 뉴스그룹:
http://groups.google.co.kr/groups?selm=3DBCD949.29E71997%40bawi.org
http://groups.google.co.kr/groups?selm=3DBCDC98.AF453CB1%40bawi.org
--]

--
김승범

Reply all
Reply to author
Forward
0 new messages