[도쿄 여행] 일본에서 볼 수 있는 킷캣 전문 매장 KitKat Chocolatory

 일본의 가장 큰 매력중 하나를 말하자면 우리나라에서 보기 힘든 매장들이 많다는 점을 예를 들고 싶군요. 생각보다 다양한 분야를 전문으로 하는 매장들이 많기 때문에 이러한 가게들 자체가 큰 매력으로 느껴지기도 합니다.

 도쿄 이케부쿠로에 위치한 킷캣 전문매장은 전 세계 최초로 오픈한 킷캣 전문 매장으로 시중에서는 볼 수 없는 이 곳에서만 판매되는 한정판 킷캣을 만나볼 수 있습니다.



 KitKat Chocolatory는 세이부 백화점의 한 코너로서 입점한 매점입니다. 그렇기에 이름만 듣고 꾀 큰 매장일 것이라는 생각을 가지고 오신 분들이라면 조금은 의야해 하실 듯 합니다



 매장 한쪽켠에서는 킷캣의 역사를 한 눈에 볼 수 있도록 잘 꾸며놓은 것을 보실 수 있습니다.



 내부 장식은 킷캣 모형의 악세사리로 꾸며놓은 것이 상당히 독특해 보이는군요.



 킷캣 매장에 도착한 시간은 오픈 시간인 10시에서 1시간을 약간 넘긴 11시 10분 즈음이었습니다만 벌써 매진된 상품이 보입니다. 생각보다 관광객들이 많이 찾아오므로 자신이 사고 싶은 종류의 킷캣이 있다면 매진에 신경쓰셔야 할 듯 보입니다.




 저는 이 곳에서 오랜지맛 킷캣을 구매하였습니다.



 킷캣 매장의 옆쪽을 바라본 사진입니다. 다른 가게들도 생각보다 많은 사람들로 인해 북적이는군요.



 상당히 매력적인 모습을 하고 있는 킷캣 매장의 간판입니다.


 일본의 경우 각 지역별로 다양한 맛의 킷캣들이 판매되고 있습니다. 녹차맛은 국내에서도 이색적인 맛의 킷캣으로 잘 알려져 있지요. 각 지역의 특산물로 만든 킷캣들은 물론이고 심지어는 와사비맛(!) 킷캣도 만들어지고 있습니다. 오직 일본에서만 팔기에 우리나라에서도 선물로서 상당히 큰 인기를 끌 만하다고 생각합니다.


 우리나라에서도 언젠간 다양한 맛의 킷캣을 맛 볼 수 있는 날이 왔으면 좋겠습니다!!


※KitKat Chocolatory에 찾아가는 방법

이케부쿠로역과 연결되어 있으며 지하 1층 SEIBU 백화점 본점으로 찾아가시면 만나보실 수 있습니다.

영업시간 : 10:00 - 21:00 (휴일 20:00)

홈페이지 : http://nestle.jp/brand/kit/chocolatory/

전화번호 : 03-5949-2026




300x250

안드로이드 프레임워크 프로그래밍(2) [안드로이드 커널 다운로드 및 컴파일]

 지난 포스팅에서는 안드로이드 프레임워크 환경 구축에 대해 알아보았습니다. 이번에는 안드로이드 커널을 다운로드한 후 이를 컴파일하는 과정을 진행해 보도록 합니다.


 Ubuntu 12.04 환경에서 Terminal을 통해 안드로이드 커널을 구축해 보도록 하겠습니다. Terminal은 Ctrl+Alt+T 버튼의 조합으로 실행하실 수 있습니다.


 커널을 다운받기에 앞서 아래의 프로그램을 설치합니다. apt-get 명령어를 통해 설치하며 아래의 내용을 그대로 터미널에 적은 후 엔터를 입력하면 되겠습니다.


$ sudo apt-get install git gnupg flex bison gperf build-essential zip curl libc6-dev libncurses5-dev:i386 x11proto-core-dev   libx11-dev:i386 libreadline6-dev:i386 libgl1-mesa-glx:i386 libgl1-mesa-dev g++-multilib mingw32 tofrodos python-markdown libxml2-utils xsltproc zlib1g-dev:i386


$ sudo ln -s /usr/lib/i386-linux-gnu/mesa/libGL.so.1 /usr/lib/i386-linux-gnu/libGL.so


위 패키지를 설치하던 도중 libgl1-mesa-glx:i386을 설치하려 할 때 의존성에 의한 오류가 발생하는 경우가 있습니다. 이 경우 다음과 같은 순서대로 실행을 해 주시면 되겠습니다.


$ sudo apt-get install git gnupg flex bison gperf build-essential zip curl libc6-dev libncurses5-dev:i386 x11proto-core-dev   libx11-dev:i386 libreadline6-dev:i386 libglapi-mesa:i386 libgl1-mesa-dev g++-multilib mingw32 tofrodos python-markdown libxml2-utils xsltproc zlib1g-dev:i386

(libgl1-mesa-glx:i386 의존성을 해결하기 위해 먼저 설치)


$ sudo apt-get install libgl1-mesa-glx:i386


 이제부터 본격적으로 안드로이드 커널을 설치하는 단계에 들어가도록 하겠습니다. 우선 사용자 폴더에 bin이라는 이름의 폴더를 생성합니다.

$ mkdir ~/bin


다음으로 해당 폴더에 설치될 repo를 다른 폴더에서 쉽게 사용할 수 있도록 PATH를 추가합니다.

$ export PATH=$PATH:~/bin


이제 repo를 설치해 보도록 하겠습니다.

$ curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo


다운로드한 repo에 실행권한을 추가합니다.

$ chmod a+x ~/bin/repo


이로서 repo를 설치하였습니다. 안드로이드 커널을 설치할 폴더를 만드신 후 해당 폴더로 이동합니다.

$ mkdir ~/lollipop

$ cd ~/lollipop


커널을 다운받을 폴더로 이동하신 후 repo를 초기화 합니다. 해당 초기화 내용은 폴더 내에 저장됩니다.

$ repo init -u https://android.googlesource.com/platform/manifest -b android-5.0.2_r1


맨 뒤에 굵은 글씨가 자신이 설치하고자 하는 안드로이드의 버전입니다. 자신이 설치하고자 하는 버전을 확인하기 위해서는 다음 사이트를 참조하시면 되겠습니다.

https://android.googlesource.com/platform/manifest



위 목록에서 자신이 설치하고자 하는 버전을 선택하신 후 실행하면 repo가 알아서 해당 버전만 설치해줍니다.


$ repo sync

 해당 명령어를 실행하자마자 바로 안드로이드 커널을 다운로드 하는 과정이 시작됩니다. 컴퓨터의 성능 및 인터넷 환경에 따라 빠르면 1시간내에 다운로드가 완료됩니다. 필자의 경우 약 3시간 30분 정도 소요되었습니다.



 다운로드가 완료된 안드로이드 커널(Lollipop)의 용량은 약 13.7GB를 차지하고 있습니다. 마지막으로 안드로이드 커널을 컴파일해 보도록 하겠습니다.


$ make -j4


 뒷부분의 -j4는 4개의 쓰레드를 수행하여 컴파일을 하겠다는 의미입니다. 쓰레드의 갯수는 자신의 컴퓨터의 코어 갯수로 설정하면 최적하게 컴파일이 수행될 수 있습니다. 


※가상머신 등 메모리의 크기나 Ubuntu 설치시 linux_swap 영역의 크기가 부족할 시 make 컴파일을 진행하는 도중에 실패로 종료되는 경우가 있습니다. 이를 해결하기 위해서는 두 가지의 용량을 더 크게 하거나 make 컴파일시 -j 옵션을 쓰지 않은 단일 thread로 컴파일을 하면 이를 해결할 수 있습니다. 단, 단인 thread로 컴파일 시 컴파일 속도가 현저하게 느려질 수 있습니다.(필자의 경우 약 2일 정도 소요된 것으로 확인되었습니다.)



참고문헌 : 인사이드안드로이드, 송형주 외 3인 저, 위키북스, 2010


300x250

안드로이드 프레임워크 프로그래밍(1) [입문 및 동작환경 구축]

 안드로이드 프로그래밍에 처음 입문하는 경우 거의 대부분의 분들이라면 안드로이드 SDK를 활용하여 애플리케이션을 제작하는 분들이 많으실 것이라 생각합니다. 물론 애플리케이션을 제작하는 것 만으로도 상당히 흥미있는 작업이라고 생각합니다. 물론 임베디드 시스템에 관심있으신 분들이라면 이보다 더 깊이 공부하고 싶어 하시는 분들도 계시리라 생각합니다.

 이번 포스팅은 안드로이드의 애플리케이션 제작에서 만족하지 않고 한 걸음 더 들어가 프레임워크를 다루는 것에 대해 이야기해 볼까 합니다.




 위 그림은 안드로이드 운영체제의 내부 구조입니다. 가장 아래 부분에는 잘 알려져 있는 대로 리눅스 커널로 구성되어 있으며 상단쪽이 바로 흔히 SDK와 Java를 활용한 애플리케이션이 동작하는 부분입니다. 안드로이드의 프레임워크는 애플리케이션 바로 아래쪽에 위치하여 애플리케이션이 요구하는 기능들을 제공해 주는 역할을 한다고 보실 수 있습니다. 이번에 우리들이 다루고자 하는 목표가 바로 위의 붉은 네모로 표시한 프레임워크를 다루는 데에 초점을 맟출 것입니다.


※준비 사항

- 리눅스가 설치된 컴퓨터(Ubuntu 12.04 버전 추천)

→ Gingerbread 이후 버전부터 64bit 환경의 리눅스를 사용해야 합니다. Froyo 이전의 버전은 32bit 사용이 가능합니다.

- Java Developer Kit

→ 안드로이드 애플리케이션을 제작할 때 쓰는 그 JDK입니다. Froyo 이전의 버전은 JDK 6, Gingerbread 이후 버전은 JDK 7 버전을 사용해야 합니다.

- 안드로이드 어플리케이션 개발 IDE(Eclipse)


※시작하기에 앞서

-본 포스팅은 작성 당시 안드로이드 최신 버전인 Lollipop(5.02)를 기준으로 설명합니다. Lollipop의 경우 64bit의 환경에서 진행해야 수월하게 진행하실 수 있습니다.


-자신의 컴퓨터가 Windows 환경이신 분의 경우 리눅스 운영체제를 동작시킬 수 있는 가상머신을 사용해야 합니다. 흔히 사용되는 가상머신으로 VirtualBox와 VMware가 있습니다. 가상머신을 사용하는 방법에 대해서는 아래의 포스팅을 참고해 주시기 바랍니다.


 가상머신으로 프레임워크를 구축하기 위해서는 하드웨어의 여유 공간이 최소 50GB(우분투 설치 공간 포함 약 60GB)정도는 확보하셔야 합니다. 최초 설치시 용량을 확보해 두지 못하면 나중에 다시 용량을 날려야 할 때 굉장히 고생하게 됩니다. 만일을 대비해서 최대 100GB정도 확보하시면 문제 없이 사용하실 수 있습니다.


Ubuntu 12.04(32bit)

http://ftp.daum.net/ubuntu-releases/precise/ubuntu-12.04.5-desktop-i386.iso


Ubuntu 12.04(64bit)

http://ftp.daum.net/ubuntu-releases/precise/ubuntu-12.04.5-desktop-amd64.iso


Virtualbox

https://www.virtualbox.org/wiki/Downloads


VMware

https://my.vmware.com/web/vmware/free#desktop_end_user_computing/vmware_player/6_0|PLAYER-604|product_downloads


VirtualBox에 Ubuntu 설치하기

VMware에 Ubuntu 설치하기



다음으로 JDK를 설치하는 과정이 필요합니다. Lollipop의 경우 JDK 1.7 버전을 설치하셔야 합니다.

Ubuntu에 JDK 설치하기


끝으로 이클립스를 설치합니다.


http://www.eclipse.org/downloads/?


자신의 Ubuntu의 버전에 맞는 프로그램을 다운로드 받습니다.




다운로드 받은 파일 내에는 이클립스 폴더가 있습니다. 입축을 해제하시면 바로 사용하실 수 있습니다.

다음으로 우분투에 리눅스용 SDK 패키지를 설치한다.


http://developer.android.com/sdk/index.html


 다운로드 사이트에 접속하신 후 계속 아래로 스크롤 하시면 SDK Tools Only 메뉴를 보실 수 있습니다. 이 곳에서 Linux 전용을 클릭하도록 합니다.



 압축파일을 확인하시면 다음과 같은 내용을 보실 수 있습니다. 해당 폴더를 적절한 곳에 압축을 해제하시면 되겠습니다.


 다음으로 설치한 Eclipse에 안드로이드 플러그인을 설치하도록 하겠습니다.



메뉴에서 Help ->Eclipse Marketplace...를 선택합니다.




 검색창에 'android development tools'를 검색하면 Google에서 제공하는 ADT for Eclipse 메뉴를 보실 수 있습니다. 오른쪽의 Install을 클릭하여 설치합니다.




위와 같이 나왔을 때 라이센스에 동의한다고 한 후 Finish 버튼을 눌러줍니다.



 다음과 같은 창이 나타나면 설치가 정상적으로 완료된 것입니다. Yes 버튼을 누르면 Eclipse가 다시 시작됩니다.



 이클립스를 다시 실행하면 위에서 보는 바와 같은 경고문이 등장합니다. 앞에서 설치하였던 SDK가 아직 이클립스와 연동이 되지 않은 상황에서 다음과 같이 발생합니다. Open Preferences를 클릭하여 SDK 폴더의 위치를 설정합니다.



 Browse...에서 위에서 압축을 풀었던 SDK 도구의 폴더를 선택하신 후 OK를 눌러주세요. 만약 경고문이 뜰 경우 아래쪽 이미 존재하는 SDK 사용 메뉴를 선택하시면 되겠습니다.




 이클립스 메뉴에서 Window -> Android SDK Manager 를 클릭하면



 우리들이 Windows에서 흔히 사용했던 Android SDK Manager를 보실 수 있습니다. 여기서 자신이 사용하고자 하는 안드로이드의 버전을 설치하면 이로서 프레임워크 구축 환경을 다루기 위한 구성요소의 준비가 끝납니다.


다음 포스팅에서는 안드로이드 커널을 다운로드 하는 방법과 이를 컴파일 하는 방법에 대해 다루어 보도록 하겠습니다.




참고문헌 : 인사이드안드로이드, 송형주 외 3인 저, 위키북스, 2010

300x250

[도쿄 여행] 에비스 생맥주를 맛볼 수 있는 야끼도리집(やき鳥とりちゃま)

 도쿄 에비스 하면 맥주 애호가이신 분들이라면 단번에 눈치를 채실 것입니다. 그렇습니다. 에비스는 일본 삿포로맥주사에서 제조하는 맥주 브랜드 중 하나입니다.


도쿄에는 실제로 에비스라는 이름을 달고 있는 역이 있습니다. 삿포로가 과거에 이 곳에서 에비스 맥주를 생산한 적이 있어 이 지역을 대표하는 이름이 되기도 하였습니다. 그렇다면 에비스역으로 가 보도록 하지요!



이 곳이 에비스 역입니다.

JR야마노테 선을 타시면 쉽게 에비스역에 오실 수 있습니다.




 에비스역에서 하차하시면 위와 같이 에비스 스카이 워크라는 출입구를 보실 수 있습니다. 이 길을 쭈욱 가시면 에비스 가든 플레이스를 보실 수 있습니다.



에비스 스카이 워크를 주욱 걸어 나오면 바로 눈 앞에 비어스테이션이 눈에 들어옵니다.

이 곳에서 에비스 생맥주 이외에 다양한 맥주들을 즐기실 수 있습니다.



에비스 가든 플레이스의 멋진 광경을 보실 수 있습니다. 생각보다 많은 커플들이 눈에 들어오는 군요.



이번에 제가 소개할 곳은 맛있는 에비스 생맥주를 즐길 수 있는 야끼도리집을 소개 해드릴까 합니다.

가게명은 やき鳥とりちゃま(야끼도리 도리챠마)입니다!



입구에 들어오자 마자 주인분께서 웃는 얼굴로 맞이해주십니다.

들어오시면 위와 같은 메뉴들이 준비되어 있습니다.



메뉴판은 영어/일어 모두 구비되어 있습니다.

메뉴에 대해 잘 모르실 경우 주인분께서 좀 더 자세히 알려주십니다.

이야기를 해도 전혀 부담이 없으니 안심하시고 물어보시면 되겠습니다.



드디어 도쿄에서 처음으로 생맥주를 먹어보는군요!

주문하실 때 '나마비루'라고 말하시면 주인분께서 알아들으시고 이렇게 잽싸게 에비스 생맥주를 마련해 주십니다.






마침 제가 가게를 방문했던 때가 12월 23일이다 보이 내부에서도 크리스마스의 기운이 물씬 풍겨옵니다.



주인분께서 멀리서 온 저를 위해 보너스로 마련해주신 야끼도리입니다.

해외에서 느끼는 인심에 두 번 감동하는군요!




가게는 위 두분께서 운영하시는데요

1인 손님이었던 저에게도 상당히 부드럽게 대해주신 점이 상당히 인상깊었습니다.

만약 도쿄에 다시 오게 된다면 친구들과 이 가게에 다시 한 번 들려

에비스 생맥주를 먹어보는 기회를 가져보고 싶군요.



※찾아오시는 길

주소        : 東京都渋谷区恵比寿南1-23-1 ABC亜米利加橋ビル1F

전화번호    : 03-6303-0253


JR에비스역에서 하차하신 후 에비스 스카이 워크쪽 출구를 이용하신 후 나오신 다음

바로 오른쪽으로 꺾어 철로를 건너신 후 바로 오른쪽 골목으로 들어가시면 야끼도리집을 오실 수 있습니다.



300x250

[도쿄 여행] GUEST HOUSE WASABI(게스트 하우스 와사비)

 한 평생 그토록 가고 싶었던 일본의 수도 도쿄를 드디어 갈 수 있는 기회가 생겨 다녀오게 되었습니다. 블로그를 개설하고 난 후 처음으로 가게 되는 여행이다 보니 이것 저것 글감으로 쓸 만한 것을 모으기도 하고 이를 사람들에게 어떻게 전달하면 좋을지에 대한 고민도 많이 해보았습니다. 그리고 그 방향을 잡고 처음으로 블로그에 여행기를 연재 해보고자 합니다.


 여행객에게 있어 가장 중요한 요소라면 바로 하루를 마감하는 숙소를 잡는 일이라고 생각합니다. 저도 지금까지 일본을 여행하면서 여러 종류의 숙소를 사용해본 바 있습니다. 가족 단위로 여행하시는 분이라면 가족들과 함께 시간을 보낼 수 있는 호텔이 가장 좋다고 생각합니다. 물론 친구들 또는 혼자 여행하시는 분들도 호텔을 쓰셔도 좋지만 좀 더 여행에 다양한 경험을 하고 싶으신 분들께는 게스트하우스가 가장 좋은 방법이라 생각합니다. 그래서 이번 포스팅의 첫 여행기는 게스트하우스로 시작해 보고자 합니다.


 숙소명        : Guest House Wasabi

 숙박일        : 2014/12/23

 숙소 유형    : 도미토리

 예약 방법    : Agoda

 Wi-Fi         : 가능

 운영 시간    : 07:00 - 22:00

 주소           : 東京都荒川区東日暮里6-24-16 (6-24-17, Higashi-Nippori, Arakawa-ku, Ueno, Tokyo, Japan)

 전화번호     : 03-5811-7550


 게스트 하우스를 가기 위해서는 닛포리역에서 JR 조반선(常磐線)으로 갈아탄 후 바로 다음 정거장인 미카와시마역(三河島駅)에서 하차하시면 되겠습니다.



미카와시마역에 도착



역 바로 건너편에 게스트하우스의 간판이 보입니다. 그 앞에는 한인 교회가 눈에 들어옵니다.



간판을 확대하여 보았습니다.



미카와시마 역의 승강장 모습입니다.



출구를 나오시면 다음과 같이 육교 아래로 내려오게 됩니다.



위에 보이는 한인 교회 건물 왼편으로 쭈욱 걸어가시면



보시는 것처럼 게스트 하우스가 떡하니 모습을 드러냅니다.



게스트 하우스의 로비 입구 입니다.



도미토리 입구에서 찍은 로비입니다.



로비 휴게실입니다. 인터넷을 즐길 수 있는 자리도 마련되어 있습니다.
이번에는 도미토리를 살펴보도록 하겠습니다.



지하 1층에 위치한 남성용 도미토리 방입니다. 2층으로 구성되어 총 14개의 침실이 있습니다.



각 방 내부의 모습입니다.




세면대는 보시는 것 처럼 3개가 설치되어 있습니다.



공용 목욕탕 또한 준비되어 있습니다.



공용 목욕탕 내부 모습



목욕탕 바깥에서 바라본 모습입니다.





1층의 모습입니다.



게스트 하우스 전면에서 바라본 야경입니다. 미카와시마역이 눈에 들어옵니다.



한 밤중의 게스트 하우스 입구 모습입니다.



도미토리 입구는 비밀번호 방식으로 로비에서 비밀번호를 알려줍니다.



로비 옆 방에 마련되어 있는 휴게실입니다. 다양한 게임들이 마련되어 있습니다.



흥미있는 보드게임들이 준비되어 있군요.



휴게실 전경입니다.



자판기에서 차를 무료로 마실 수 있습니다.




휴게실에 마련된 주방입니다. 사용시 대여료가 필요합니다.





이렇게 해서 게스트 하우스 와사비에서의 하룻밤을 보내고 다시 여행을 떠납니다.

이번 게스트 하우스를 평가하자면


장점 : 다른 게스트하우스와 달리 방 하나에 최대 10명 이상이 사용하기 때문에 좀 더 다양한 사람들과 어울릴 수 있는 자리가 될 수 있습니다.

단점 : 방 내에 사람이 많다보니 귀중품을 분실할 가능성이 있습니다. 또한 자는 데 다른 사람이 이동할 경우 민감하신 분들은 잠을 제대로 주무시지 못하실 수도 있습니다.


이상으로 제 첫 여행기 포스트를 마치기로 하겠습니다.

다음에는 좀 더 알찬 내용으로 준비해 보도록 하겠습니다~!

300x250

Extract contour area using OpenCV in Android(OpcnCV에서 검출된 영역의 넓이 구하기)

※이 프로그램은 OpenCV의 예제파일인 OpenCV Sample - color-blob-detection을 수정한 자료임을 알립니다.


OpenCV의 색상 검출 프로그램을 돌려보면 원하는 영역이 아래와 같이 붉은 테두리로 나타나는 것을 보실 수 있을 것입니다.



위 그림에서 보시는 이 빨간 테두리를 '등고선(Contour)'라 부릅니다. Android OpenCV에서는 이 빨간 테두리가 쳐진 부분의 넓이를 추출하는 기능을 가지고 있습니다. 이를 적용하는 방법은 아래외 같이 하시면 되겠습니다.


ColorBlobDetectionActivity.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ColorBlobDetectionActivity extends Activity implements OnTouchListener, CvCameraViewListener2 {
    private static final String  TAG              = "OCVSample::Activity";
 
    private boolean              mIsColorSelected = false;
    private Mat                  mRgba;
    private Scalar               mBlobColorRgba;
    private Scalar               mBlobColorHsv;
    private ColorBlobDetector    mDetector;
    private Mat                  mSpectrum;
    private Size                 SPECTRUM_SIZE;
    private Scalar               CONTOUR_COLOR;
 
    private CameraBridgeViewBase mOpenCvCameraView;
    List<MatOfPoint>             contours;
    
}

 위의 코드에서 contour 변수는 원래 onCameraFrame() 함수 내에 있는 변수입니다. 이를 다른 곳에서 사용하기 위해서는 위의 코드에서 보시는 것 처럼 전역변수로 선언하는 것이 사용하기에 편합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
        mRgba = inputFrame.rgba();
 
        if (mIsColorSelected) {
            mDetector.process(mRgba);
            contours = mDetector.getContours();
            Log.e(TAG, "Contours count: " + contours.size());
            Imgproc.drawContours(mRgba, contours, -1, CONTOUR_COLOR);
 
            Mat colorLabel = mRgba.submat(4, 68, 4, 68);
            colorLabel.setTo(mBlobColorRgba);
 
            Mat spectrumLabel = mRgba.submat(4, 4 + mSpectrum.rows(), 70, 70 + mSpectrum.cols());
            mSpectrum.copyTo(spectrumLabel);
        }
        
        return mRgba;
    }

 onCameraFrame() 함수 내에 있던 contours 변수를 외부에 전역으로 선언하였으므로 이 함수 안에서는 그냥 변수에 값을 넣는 개념으로 이해하시면 되겠습니다.


 끝으로 Contour의 면적을 구하는 부분을 다음과 같이 설정하시면 Contour의 Area값을 얻으실 수 있습니다.
1
2
3
4
5
6
7
8
if(contours != null){
        double d;
        d=0;
        for(int i=0; i<contours.size();i++){
            d += Imgproc.contourArea(contours.get(i));
        }
            
}

 프로그램을 처음 구동할 때 contours의 값이 null일 경우가 있으므로 if문을 통해 null일 경우 예외처리를 해줍니다.

그 다음으로 contours 내의 모든 등고선의 값을 d에 저장을 하시면 화면에 표시되는 등고선(Contour)의 면적값들을 구하실 수 있습니다.


 위에서 보시는 바와 같이 contours는 List 변수로 내부에는 화면에 표시된 등고선의 수 만큼의 Mat 값들이 있는 것을 확인하실 수 있습니다. 이러한 contours를 반복문을 통해서 각 하나의 contour값을 확인하는 과정을 거칩니다.


 Imageproc.contourArea() 함수는 해당 Contour의 값을 구하는 기능을 가지고 있습니다. 이를 통하여 해당 contour의 넓이를 구해 모든 값을 더하게 되면 화면에 표시된 붉은 영역의 총 면적을 구할 수 있게 됩니다.




300x250

Color detection using Android openCV(안드로이드 OpenCV로 특정 색깔 인식)

※이 프로그램은 OpenCV의 예제파일인 OpenCV Sample - color-blob-detection을 수정한 자료임을 알립니다.


1.먼저 자신이 찾고자 하는 색깔의 Hsv를 알아내야 합니다. 만약 자신이 찾는 색깔의 Hsv를 모르는 경우 해당 색깔의 RGB를 Hsv로 변환하는 사이트를 이용합니다.

http://www.rapidtables.com/convert/color/rgb-to-hsv.htm




위의 RGB 색상 변환으로 나오는 HSV의 값을 아래와 같이 입력하시면 되겠습니다.

new Scalar(235, 75.2, 45.9, 0.0);


위에서 처리한 값을 이제 아래의 onCameraViewStarted() 함수에 입력해주시면 프로그램이 실행하자마자 해당 색상을 검출하는 것을 확인하실 수 있습니다.


ColorBlobDetectionActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void onCameraViewStarted(int width, int height) {
        mRgba = new Mat(height, width, CvType.CV_8UC4);
        mDetector = new ColorBlobDetector();
        mSpectrum = new Mat();
        mBlobColorRgba = new Scalar(255);
        SPECTRUM_SIZE = new Size(200, 64);
        CONTOUR_COLOR = new Scalar(255,0,0,255);
        
        
        mBlobColorHsv = new Scalar(235, 75.2, 45.9, 0.0);
        mDetector.setHsvColor(mBlobColorHsv);
        mIsColorSelected = true;
        
    }


300x250

Use front camera with OpenCV 2.4.9 for android(안드로이드 OpenCV에서 전면카메라 적용 방법)

 굳게 마음을 먹고 OpenCV를 사용하여 프로그래밍을 해보려고 하는데 SurfaceView를 통해 전면 카메라를 사용하는 방법과 다른 방법을 쓰는 것 같아 찾아보니 쉽지가 않더군요.


그래서 직접 OpenCV 라이브러리를 찾아보면서 적용방법을 찾아본 결과 다음과 같은 결과를 얻을 수 있었습니다.

안드로이드용 OpenCV에서 아래의 클래스가 카메라를 담당하는 것으로 보였습니다.


private CameraBridgeViewBase mOpenCvCameraView;

위 클래스를 이것 저것 적용하다보니 사용법을 찾아낼 수 있었습니다.

아래의 코드와 같이 onCreate()에서 굵게 표시한 부분을 코드에 삽입하면 OpenCV가 전면카메라를 사용하는 것을 확인하실 수 있습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
public void onCreate(Bundle savedInstanceState) {
        Log.i(TAG, "called onCreate");
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
 
        setContentView(R.layout.color_blob_detection_surface_view);
 
        mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.color_blob_detection_activity_surface_view);
        mOpenCvCameraView.disableView();
        mOpenCvCameraView.setCameraIndex(CameraBridgeViewBase.CAMERA_ID_FRONT);
        mOpenCvCameraView.setCvCameraViewListener(this);
    }



300x250

블루투스를 통해 이미지를 바이트로 전송하기

 대부분의 안드로이드 폰에는 블루투스가 기본적으로 내장되어 있습니다. 블루투스 기능을 활용하면 굳이 인터넷이나 기지국을 거치지 않고도 안드로이드 기기간에 파일 전송이 가능하지요.

 이번 포스팅에서는 간단하게 다른 안드로이드 폰으로 사진을 전송하는 방법에 대해 알아보겠습니다.


 시작하기에 앞서 아래에 있는 블루투스 채팅 프로그램 프로젝트를 다운로드하여 Import 합니다.

 

BluetoothChat (1).zip





 위의 예제는 안드로이드 폰 상호간에 간단한 문자 채팅을 할 수 있는 프로그램입니다. 이 프로젝트에 이미지를 전송할 수 있는 기능을 넣어보도록 하겠습니다.


먼저 AndroidManifest.xml 파일에 다음과 같은 퍼미션을 추가 해줍니다.

1
2
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.BLUETOOTH" />


다음으로 메인 레이아웃인 main.xml에 그림을 띄울 ImageView와 그림을 전송하기 위한 Button을 다음과 같이 추가합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <ListView android:id="@+id/in"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:stackFromBottom="true"
        android:transcriptMode="alwaysScroll"
        android:layout_weight="1"
    />
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >
 
        <ImageView
            android:id="@+id/getimage"
            android:layout_width="match_parent"
            android:layout_height="150dp"
            android:src="@drawable/app_icon"
            android:scaleType="centerInside"
            />
 
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal" >
 
            <EditText
                android:id="@+id/edit_text_out"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="bottom"
                android:layout_weight="1" >
 
                <requestFocus />
            </EditText>
 
            <Button
                android:id="@+id/button_send"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/send" />
 
            <Button
                android:id="@+id/button_image"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/send_image" />
        </LinearLayout>
 
    </LinearLayout>
 
</LinearLayout>
 



다음으로 BluetoothChat.java를 수정해 보도록 하겠습니다.

먼저 추가된 Button과 ImageView에 대한 함수를 생성합니다.
1
2
    private Button mSendImage;
    private ImageView iv;


다음으로 setupChat() 함수 내에 다음과 같이 추가합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    private void setupChat() {
        
        ............
    
        iv = (ImageView)findViewById(R.id.getimage);
        mSendImage = (Button) findViewById(R.id.button_image);
        mSendImage.setOnClickListener(new OnClickListener(){
 
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(Intent.ACTION_PICK,
                                               android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
                startActivityForResult(intent,10);
            }
            
        });
    }


위에서 보는 것과 같이 이미지 갤러리를 Intent를 통해 불러오는 것을 볼 수 있습니다.

Intent를 통해 선택된 사진은 onActivityResult() 함수를 통해 얻을 수 있습니다.


onActivityResult() 함수에 다음과 같이 값을 추가합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        
        ............
        
        switch (requestCode) {
            
            ............
            
            case 10:
            if(resultCode==RESULT_OK && data != null){
                try {
                    Uri selectedImage = data.getData();
                Bitmap bp = Images.Media.getBitmap(this.getContentResolver(), selectedImage);
                    
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                bp.compress(CompressFormat.JPEG, 80, baos);
                byte[] imageByte = baos.toByteArray();
                
                iv.setImageBitmap(BitmapFactory.decodeByteArray(imageByte, 0, imageByte.length));
 
                if(mChatService.getState() == BluetoothChatService.STATE_CONNECTED){                        
                    int len;
                    final int size = 512;
                    byte[] sendByte = new byte[size];
                    ByteArrayInputStream bais = new ByteArrayInputStream(imageByte);
                    mConversationArrayAdapter.add("이미지 전송을 시작합니다.");
    
                        
                    sendByte[0] = 6;
                    sendByte[1] = 26;
                    sendByte[2] = 18;
                    mChatService.write(sendByte);
                    while( (len=bais.read(sendByte)) != -1){                            
                        if(len<512){
                            byte[] EOF = new byte[len];
                            for(int eof=0 ; eof<EOF.length; eof++){
                                EOF[eof] = sendByte[eof];
                            }
                            mChatService.write(EOF);
                            
                        }else{
                            mChatService.write(sendByte);
                        }
                    }
                    
                    sendByte[0] = 26;
                    sendByte[1] = 6;
                    sendByte[2] = 18;
                    mChatService.write(sendByte);
                        
                    mConversationArrayAdapter.add("이미지 전송이 완료되었습니다!");
                    mConversationArrayAdapter.add("Image Size : " + imageByte.length);
                }
                    
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
           }
           break;
     }
    }



마지막으로 Handler를 통해 전송된 사진의 값을 수정하는 부분을 다음과 같이 수정해 줍니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
....
    boolean imageTransfer = false;
    boolean imageTransferW = false;
    ByteArrayOutputStream ReceiveImage;
 
    // The Handler that gets information back from the BluetoothChatService
    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MESSAGE_STATE_CHANGE:
                if(D) Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1);
                switch (msg.arg1) {
                case BluetoothChatService.STATE_CONNECTED:
                    mTitle.setText(R.string.title_connected_to);
                    mTitle.append(mConnectedDeviceName);
                    mConversationArrayAdapter.clear();
                    break;
                case BluetoothChatService.STATE_CONNECTING:
                    mTitle.setText(R.string.title_connecting);
                    break;
                case BluetoothChatService.STATE_LISTEN:
                case BluetoothChatService.STATE_NONE:
                    mTitle.setText(R.string.title_not_connected);
                    break;
                }
                break;
            case MESSAGE_WRITE:
                byte[] writeBuf = (byte[]) msg.obj;
                String writeMessage = new String(writeBuf);
                mConversationArrayAdapter.add(writeBuf.length+" "+(int)writeBuf[0]
                                                +" "+(int)writeBuf[1]);
                mConversationArrayAdapter.add("나 :  " + writeMessage);
                
                break;
            case MESSAGE_READ:
                byte[] readBuf = (byte[]) msg.obj;
                // construct a string from the valid bytes in the buffer
                if(msg.arg1 > 2 && readBuf[0]==6 && readBuf[1]==26 && readBuf[2== 18){
                    imageTransfer = true;
                    ReceiveImage = new ByteArrayOutputStream();
                    mConversationArrayAdapter.add("Image Transfer Start!");
                    break;
                }
                
                if(msg.arg1 > 2 && readBuf[0]==26 && readBuf[1]==6 && readBuf[2== 18){
                    imageTransfer = false;
                    mConversationArrayAdapter.add("Image Transfer End!");
                    mConversationArrayAdapter.add("Image Size : " + ReceiveImage.size());
                    byte[] getImage = ReceiveImage.toByteArray();
                                        
                    iv.setImageBitmap(BitmapFactory.decodeByteArray(getImage, 0, getImage.length));
                    
                    break;
                }
                
                if(imageTransfer){
                    try {
                        ReceiveImage.write(readBuf);
                        if(readBuf.length==512)
                        mConversationArrayAdapter.add("Size : " + readBuf.length +", "
                                                    +(short)readBuf[0]+", " + (short)readBuf[511]);
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }else{
                    String readMessage = new String(readBuf, 0, msg.arg1);
                    mConversationArrayAdapter.add(mConnectedDeviceName+":  " + readMessage);
                }
                break;
            case MESSAGE_DEVICE_NAME:
                // save the connected device's name
                mConnectedDeviceName = msg.getData().getString(DEVICE_NAME);
                Toast.makeText(getApplicationContext(), "Connected to "
                               + mConnectedDeviceName, Toast.LENGTH_SHORT).show();
                break;
            case MESSAGE_TOAST:
                Toast.makeText(getApplicationContext(), msg.getData().getString(TOAST),
                               Toast.LENGTH_SHORT).show();
                break;
                
            case 6:
                //Toast.makeText(getApplicationContext(), "WTF", Toast.LENGTH_SHORT).show();
                
                break;
            }
        }
    };
....
cs


300x250

안드로이드 Native(NDK) 소스를 간단하게 보는 방법

안드로이드/프레임워크 2014. 11. 17. 01:04

 안드로이드의 카메라 기능에 대해 알아보고 싶어서 직접 안드로이드 라이브러리 내부에 있는 클래스를 열어 이리저리 해집고 다니다가 다음과 같은 코드를 목격하게 되었습니다.




 JNI를 접해본 경험이 없으신 분들께서는 아마 난생 처음 보는 녀석인 native를 보고 당황하실 분들이 있으시리라 생각합니다.

 JNI란 Java Native Interface의 약자로, 아주 간단하게 설명드리자면 안드로이드를 C/C++로 구현할 수 있는 기능입니다! Java 소스코드는 안드로이드 내에 있는 Dalvic머신(Lolipop 이후부터는 ART)에서 구동됩니다만 안드로이드 폰 내의 센서나 커널 등을 다루게 될 때는 JNI를 통해 C/C++로 접근하여야 합니다.

 위의 경우도 Camera 센서를 구동하기 위해 NDK를 통해 해당 함수가 구현된 것을 native로 나타내고 있다고 볼 수 있습니다. 그렇다면 이 Native 코드는 직접 볼 수 있는걸까요?



 가장 확실한 방법은 안드로이드의 OS 커널을 직접 다운받아 해당 소스코드를 직접 찾아보는 방법입니다. 커널은 위 사이트에서 다운로드 하실 수 있습니다.


http://source.android.com/source/downloading.html


 하지만 커널을 직접 구경할 일이 없는 분들께 이 방법은 번거롭기만 합니다. 게다가 자신이 원하는 소스코드를 바로 찾는 것도 상당히 불편합니다. 그렇다면 좀 더 편하게 안드로이드 native 소스코드를 볼 수 잇는 방법은 없는걸까요?


 

 위 사이트는 안드로이드 OS에 있는 Android와 Kernel의 소스코드를 인터넷으로 볼 수 있도록 되어 있습니다. 검색 기능을 제공하고 있어 자신이 찾고자 하는 소스코드를 더 쉽게 찾을 수 있습니다.


http://androidxref.com/


 위 사이트에 접속하신 후 자신이 보고자 하는 안드로이드 버전을 클릭합니다. 여기서는 현 시점에서 최신버전인 Lolipop을 선택하였습니다.




 검색하기 원하는 버전을 선택하신 후 위 사진에서 붉은 네모칸으로 표시한 'select all'을 선택하신 후 왼쪽 'Full Search' 부분에 자신이 찾고자 하는 native 함수명을 입력합니다.



 다음과 같이 해당 함수명을 가지고 있는 소스코드들이 검색에 잡히는 것을 확인하실 수 있습니다. 해당 함수가 존재하는 소스코드는 다음과 같이 찾으실 수 있습니다.


 Java 소스코드에 있는 native 함수가 있는 클래스의 패지명이 소스코드의 명칭이 될 경우가 대부분입니다. 예를들어 Camera.java가 위치한 패키지는 android.hardware.Camera입니다. 즉, jni 내부의 소스코드의 명칭은 android_hardware_camera.cpp가 해당 코드라고 할 수 있습니다.



 Native 소스코드 내의 함수명은 '클래스의 패키지경로_함수명()'으로 구성되어 있습니다. 즉 android.hardware.Camera 클래스 내에 있는 native 함수인 native_setup 함수는 해당 소스코드 내에서는 

'android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, ...)'

와 같은 형식으로 구성되어 있음을 확인하실 수 있습니다.

300x250

[NDK] openCV jni 소스 헤더파일이 include 되지 않을 때 해결방법

 안드로이드로 openCV를 해보기 위해 NDK를 설치한 후 직접 소스를 수정해보기 위해 소스파일을 연 순간 다음과 같은 장면이 펼쳐졌습니다.



 분명 직접 빌드까지 하였던 프로젝트임에도 이렇게 헤더파일이 잡히지 않아 당황하였습니다. 혹시 프로젝트를 빌드에 성공하지 못하셨던 분이라면 아래의 블로그를 참조해 주시길 바랍니다.


 http://adppark.tistory.com/303



 그럼 프로젝트의 헤더파일이 연결될 수 있도록 해보겠습니다.


1. 자신이 원하는 jni 프로젝트 폴더를 오른쪽 클릭 -> Properties -> C/C++ General -> Paths and Symbols를 선택합니다.



 2. 다음과 같은 화면이 나오면 Includes 탭 내의 Languages 내에서 C/C++ 둘중 Include directories가 있는 경우 모두 추가해줍니다.

오른쪽의 Add를 선택합니다.



3. File System...를 선택하신 후



4. "자신의 안드로이드 openCV 버전/sdk/native/jni/include"를 선택하신 후 확인 버튼을 누른 후



5. OK버튼을 눌러 Include directories에 추가합니다.



 6. 추가한 Include 디렉토리를 상단 두번째에 위치시킵니다. 오른쪽의 Move Up를 클릭하여 이동이 가능합니다.



 8. jni 폴더 내에 있는 Android.mk 파일을 연 후 include 부분에 자신의 OpenCV의 절대 경로로 수정합니다.



 9. 시작버튼을 눌러 '고급 시스템 설정'을 검색하여 나오는 항목을 클릭합니다.



10. 다음과 같이 시스템 설정 메뉴가 나오는 것을 보실 수 있습니다. 아래 환경변수(N)을 클릭합니다.



11. 새로 만들기(W)를 선택하신 후



12. 자신의 Android NDK의 환경변수를 설정합니다.

변수 이름은 'NDKROOT'로 설정하시고

변수값에는 자신의 NDK가 있는 위치를 입력하신 후 확인버튼을 누릅니다.



 위와 같은 과정을 거치시면 다음과 같이 헤더파일이 정상적으로 동작하는 것을 확인하실 수 있습니다.




300x250

WF2411 공유기를 통한 외부 기기와 소켓 통신 프로그래밍

공대생의 팁 2014. 11. 2. 00:39

 스마트폰 시대가 열리면서 요즘은 집마다 공유기를 두는 경우가 많습니다. 그 덕에 공유기를 통해 안드로이드폰으로 통신하는 기능을 가진 기기들도 속속 등장하는 것을 볼 수 있지요.


 소켓 프로그래밍에 처음으로 입문한 분들의 경우 IP의 개념이 제대로 정립되지 않았을 경우 열심히 코드를 입력하였는데도 소켓 프로그래밍이 제대로 동작하지 않는 경험으로 멘붕을 경험하신 분들이 꽤 되실 거라 생각합니다.


 같은 공유기에 접속한 기기 끼리는 소켓 통신이 원활이 되는데 왜 공유기로 들어오는 신호는 잡히지 않을까요? 그 이유는 공유기가 접속한 기기에 제공하는 IP주소에서 원인을 찾을 수 있습니다.

 IP주소는 집주소와 같아 해당 IP주소를 입력하면 이론상 그 IP를 가진 컴퓨터에 접속하실 수 있습니다. 실제로 티스토리를 도메인이 아닌 아이피 주소를 입력하면 티스토리로 접속할 수 있는 것을 알 수 있습니다.


 

이미지 출저 : http://www.madtomatoe.com/what-are-private-ip-addresses/


 일반적으로 집에서 여러 대의 컴퓨터를 사용하게 될 때 각 컴퓨터마다 인터넷을 연결하기 위해서는 각 컴퓨터 대수 만큼의 회선이 필요합니다. 만약 각 컴퓨터마다 각각 다른 통신사의 회선을 연결하게 되면 통신요금이 상당히 많이 나올겁니다.

 이렇게 집안에 여러 대의 컴퓨터를 하나의 회선으로 사용할 수 있게 해주는 역할을 하는 것이 바로 공유기 입니다. 외부로부터 하나의 IP를 할당받아 인터넷에 접속할 수 있는 공유기에 각 컴퓨터의 회선을 한 공유기에 연결하면 연결된 PC들이 동시에 인터넷을 할 수 있습니다. 이 때 각 컴퓨터마다 인터넷에 접속하기 위해서는 각자 다른 IP주소를 할당받아야 합니다.

 공유기가 외부로부터 연결된 회선의 IP는 1개 뿐인데 이 여러대에 연결된 컴퓨터에는 주소를 어떻게 배포할까요? 이는 각 기기별로 공유기 자체에서 제공하는 주소를 나누어 주는 것으로 해결할 수 있습니다. 이 때 공유기가 각 컴퓨터에 할당하는 IP를 사설 IP라고 합니다.

 이 상황에서 공유기에 연결된 컴퓨터 끼리는 공유기가 할당한 IP주소를 통해 서로 통신을 할 수 있는 망이 구축된 것을 확인할 수 있습니다. 하지만 이들의 IP주소를 외부망에 있는 컴퓨터에 연결을 하려면 어떻게 해야 할까요?


 1. 실제 컴퓨터가 할당받은 사설 IP에 접속한다.

 사설 IP는 192.168.x.x 대열에서 사용되도록 하고 있습니다. 즉, 해당 주소를 가진 PC는 공유기에 연결되어 있는 모든 PC들을 대상으로 한다고 볼 수 있죠. 이는 해당 컴퓨터의 유일한 IP주소가 아니기 때문에 접속이 불가능합니다.


 2. 공유기가 할당받은 IP에 접속한다.

 분명 공유기는 외부로부터 자신만의 IP를 할당받았습니다. 실제로 외부망에서 공유기의 WAN포트에 걸린 IP주소를 입력하면 공유기에 접속이 가능합니다. 하지만 여기서 문제가 발생합니다. 우리가 목표로 하는 것은 공유기에 연결된 PC에 접속을 하는 것이지만 공유기 위치에서 해당 컴퓨터의 IP주소를 알아야 접속이 가능합니다. 하지만 처음에 접속할 때 우리는 오직 공유기에 도달하기 위핸 1개의 IP밖에 사용할 수 없습니다.


 그렇다면 위 2가지 방법 이외의 접속방법은 없는걸까요?

 이 질문의 답은 각 공유기가 제공하는 포트포워딩(Port forwarding)을 사용하는 방법으로 해결할 수 있습니다.



이미지 출저 : http://documentation.commvault.com/hds/v10/article?p=features/firewall/port_forward_gateway.htm


 위 그림에서 중간에 위치한 기기를 공유기라 하였을 때 오른쪽의 컴퓨터는 외부의 컴퓨터로 공유기의 IP를 통해 접속을 시도하는 모습을 나타내고 있습니다. 왼쪽의 2대의 컴퓨터는 각각 공유기에 연결되어 있으며 440포트가 개방되어 있는 상황을 나타내고 있습니다.

 만약 공유기가 포트포워딩으로 외부에서 공유기의 특정 포트를 연결하게 되면 공유기는 해당 포트로 접속시 설정된 공유기에 연결된 컴퓨터에 연결할 수 있도록 도와줍니다.

 예를들어 외부의 컴퓨터가 공유기의 IP주소와 포트번호 443번을 입력하였을 경우 공유기는 443번 포트 접속시 설정되어있는 사설IP와 포트번호를 통해 내부망의 컴퓨터에 접속을 시도하게 됩니다. 이와 같은 방법으로 공유기 외부의 컴퓨터가 공유기 내부에 연결된 PC와 소켓통신을 할 수 있는 것을 알 수 있습니다.


 여기까지 포트포워딩에 대해 간단하게 설명드렸습니다. 포드 포워딩에 대해 좀 더 자세히 알고 싶으신 분은 아래의 블로그를 참조하시길 바랍니다.

http://luckyyowu.tistory.com/102


 그렇다면 이번에는 외부 컴퓨터가 공유기 내부망에 연결된 컴퓨터와 소켓 프로그래밍을 할 수 있는 환경을 설정해 보도록 하겠습니다.


※본 포스팅은 Windows7 기반의 컴퓨터와 Netis사의 WF2411 공유기를 기준으로 작성되었습니다.


1. 먼저 CMD를 실행하여 'ipconfig'를 입력하면 아래와 같은 결과를 알 수 있습니다. 이 때 IPv4가 해당 컴퓨터가 공유기로부터 할당받은 IP 주소입니다. 이를 기억하도록 합니다.



2.자신의 공유기의 관리자 모드에 접속합니다.

Netis사의 공유기의 초기 설정은  http://192.168.1.1 로 설정되어 있습니다.



3. '방화벽/포트 포워딩 메뉴'로 들어가신 후 '가상 서버' 메뉴를 선택하시면 위와 같은 화면을 보실 수 있습니다.

'규칙 이름'은 자신이 알 수 있는 이름으로 정해줍니다.

'내부 IP 주소(서버 PC)'는 외부 접속을 받고자 하는 공유기 내부망 PC의 사설IP주소를 입력하는 부분입니다. 위 CMD를 통해 확인한 IP주소를 입력하면 되겠습니다.

소켓 프로그래밍 용도로 접속을 하는 것이므로 '프로토콜'은 TCP로 설정합니다.

'포트번호(외부)'는 외부에서 공유기 접속시에 입력하는 Port 번호로 외부 컴퓨터가 공유기에 접속할 때의 포트번호를 입력합니다.

'포트번호(내부)'는 위에서 설정된 포트번호로 접속한 외부 접속을 공유기에 연결된 PC에 전송시 쓰이는 포트번호입니다. 공유기에 연결된 PC가 정해진 포트로 접속해야할 경우 해당 포트번호를 입력합니다.


위의 설정을 모두 완료하시면 외부 접속으로 공유기 내에 있는 기기가 소켓 통신을 하는 것을 확인하실 수 있습니다.



※Windows7 기반 사용자의 경우 위의 설정까지 진행되었음에도 소켓통신이 이루어지지 않는 경우가 있습니다. 이는 해당 Windows7에 설정된 방화벽으 접속을 막는 경우 발생합니다. 이를 해결하기 위해서는 방화벽 설정을 통해 특정 포트를 개방하면 해결할 수 있습니다. 자세한 사항은 다음 포스팅을 참고 하시기 바랍니다.


윈도7 기반 서버 컴퓨터와 소켓 프로그래밍이 안될 때

300x250