[네트워크/C] getaddrinfo 함수

by Blogger 하얀쿠아
2017. 6. 16. 00:56 소프트웨어 Note/C

개요


getaddrinfo는 domain address를 받아서 네트워크 주소 정보(IP address)를 가져오는 함수이다.


예를들면, http://www.google.co.kr라는 domain address가 있는데, 이 주소는 사람이 알아보기 쉬운 주고이긴 하지만, 컴퓨터는 이 주소를 가지고 해당되는 구글의 서버를 찾아가지 못한다. 그래서 이 domain address와 대응되는 IP주소가 무엇인지를 알아 낸 뒤에 그 IP주소로 연결을 해야한다. 


즉, Domain address -> IP address 변환을 하고 싶을 때 사용하는 함수라는 뜻이다.

이걸 전문용어로 DNS (Domain Name System/Service) resolving 이라고 한다.


getaddrinfo 함수는 총 4개의 매개변수를 가진다.

그 중, 1~3번째는 입력 매개변수이고, 4번째 매개변수는 결과를 사용자에게 돌려주는 출력 매개변수이다.

결과는  addrinfo 구조체 (strcut addrinfo) 의 linked list로 돌려준다.


이 결과는 사용을 끝낸 뒤엔 freeaddrinfo 함수로 메모리 해제를 해주어야 한다. 그렇지 않으면 메모리 누수가 발생한다.


관련헤더

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>


함수원형

int getaddrinfo(const char *hostname,
                const char *service,
                const struct addrinfo *hints,
                struct addrinfo **result);

void freeaddrinfo(struct addrinfo *res);


getaddrinfo 함수의 매개변수는 총 4개이다.


hostname [in] : 호스트 이름 혹은 주소 문자열(주소 문자열은 => IPv4의 점으로 구분하는 10진 주소 문자열이거나 IPv6의 16진 문자열)


service [in] : 서비스 이름 혹은 10진수로 표현한 포트 번호 문자열


hints [in] : getaddrinfo 함수에게 말그대로 힌트를 준다. 희망하는 유형을 알려주는 힌트를 제공한다. addrinfo 구조체에 hint로 줄 정보를 채운 뒤, 그것의 주소값을 넘기면 된다. 이 힌트는 반환받을 result를 제한하는 기준을 지정하는 것이다. 예를들면, IPv4주소만 받고 싶거나, IPv6주소만 받고 싶을 수도 있고, 둘다 받고 싶을수도 있다. 이럴땐hints의 ai_family의 값을 조작하면 된다. 별도의 hint를 제공하지 않을 경우, NULL을 넣는다.


result [out] : DNS서버로부터 받은 네트워크 주소 정보(IP 주소)를 돌려주는 output 매개변수이다. addrinfo 구조체를 사용하며, 링크드 리스트이다. 이 addrinfo 구조체에 대해서는 아래 그림을 참고하면 이해하기 쉽다. 이 result의 내용중 필요한것들은 적절히 copy하여 사용자의 변수로 옮겨두어야 하며, result는 사용이 끝나는 즉시 freeaddrinfo 함수로 메모리 해제를 해주어야 한다.


getaddrinfo가 반환한 주소정보(addrinfo)의 예



반환 값

이 함수는 성공하면 0을 반환하다.

실패하면 0이 아닌 값을 반환하는데, 아래 에러 값들중 하나를 반환하게 된다.


EAI_ADDRFAMILY

지정된 네트워크 호스트에 요청했던 address family 주소가 없는 경우이다. 예를들어 IPv4주소만 갖는 네트워크 호스트에게 'IPv6주소를 내놔라' 하고 요청한다면, 이런 에러가 반환된다.


EAI_AGAIN

nameserver가 일시적인 오류 표시를 반환했다. 일정 시간 이후에 다시 시도하라는 의미이다.


EAI_BADFLAGS

hints.ai_flags에 잘못된 플래그가 포함되어 있다. 또는 hints.ai_flags에는 AI_CANONNAME이 포함되었고 이름이 NULL인 경우이다.


EAI_FAIL

nameserver가 지속되는 오류 표시를 반환했다. EAI_AGAIN과는 달리 다시 시도해도 실패할 것이라는 의미다.


EAI_FAMILY

요청한 address family는 지원되지 않는다.


EAI_MEMORY

getaddrinfo를 수행하기에 메모리가 부족한 경우다. 거의 막장인 상황이다. kernel의 OOM killer가 제대로 작동하길 기대하면서 기다렸다가 일정 시간 지난 후 재시도 해보고 그래도 같은 에러를 받는다면 속편하게 재부팅 하라.


EAI_NODATA

지정한 네트워크 호스트가 있긴 한데... 네트워크 주소가 정의되어 있지 않은 경우다.


EAI_NONAME

노드 또는 서비스를 알 수 없다.

또는 노드와 서비스 모두가 NULL이다.

또는 AI_NUMERICSERV가 hints.ai_flags에 지정되었는데, 정작 service는 포트 번호를 나타내는 숫자형태의 문자열이 아니었다.

어떤 경우이건, 입력 매개변수를 잘못 넣었다는 의미다. 자신이 매개변수를 잘 넣었는지? 스스로의 코드를 다시 점검 해볼 것.


EAI_SERVICE

요청한 서비스를 요청한 소켓 유형에 사용할 수 없다. 다른 소켓 유형을 통해 사용할 수 있다. 예를 들어 서비스가 "쉘"(스트림 소켓에서만 사용할 수있는 서비스)이고 hints.ai_protocol이 IPPROTO_UDP이거나 hints.ai_socktype이 SOCK_DGRAM 인 경우 이 오류가 발생할 수 있다.

또는 서비스가 NULL이 아니고 hints.ai_socktype이 SOCK_RAW (서비스 개념을 지원하지 않는 소켓 유형) 인 경우 오류가 발생할 수 있습니다.


EAI_SOCKTYPE

요청한 소켓 유형이 지원되지 않는다. 

예를 들어, hints.ai_socktype 및 hints.ai_protocol이 일치하지 않는 경우다. ai_socktype에는 SOCK_DGRAM 를 넣고, ai_protocol에는 IPPROTO_TCP를 넣은 hint를 입력 매개변수로 넘기면 발생할 수 있는 에러이다.


EAI_SYSTEM

위 모든 경우를 제외한 뭔가 다른 시스템 오류다, 골치아픈 경우다. 자세한 내용은 errno를 확인해야 하는 경우다.



반환값의 해석

const char *gai_strerror(int errcode);


gai_strerror( ) 함수를 이용한다.

이 함수는 getaddrinfo( )가 return한 오류 코드를 사람이 읽을 수 있는 문자열로 변환해준다.

디버깅, 로그 출력 시 도움이 되니 참고할 것.



예제

아래는 www.google.com 80포트의 Network Address 정보를 얻는 예제이다.

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

int main(int argc,char *argv[])
{
	int status;
	struct addrinfo hints;
	struct addrinfo *servinfo;  // 결과를 저장할 변수

	memset(&hints, 0, sizeof(hints)); // hints 구조체의 모든 값을 0으로 초기화
	hints.ai_family = AF_UNSPEC;     // IPv4와 IPv6 상관하지 않고 결과를 모두 받겠다
	hints.ai_socktype = SOCK_STREAM; // TCP stream sockets

	status = getaddrinfo("www.google.com", "80", &hints, &servinfo);
}


공식 근거문서

POSIX.1-2001. getaddrinfo( ) 함수는 RFC 2553에 문서화 되어 있다.



중요 알림사항

getaddrinfo( ) 는 IPv6 scope-ID를 지정하기 위한 address%scope-id 표기법을 지원한다.


AI_ADDRCONFIG, AI_ALL 및 AI_V4MAPPED는 glibc 2.3.3부터 사용할 수 있다.


AI_NUMERICSERV는 glibc 2.3.4부터 사용 가능하다.


POSIX.1-2001에 따르면, hint를 NULL로 지정하면 ai_flags가 0으로 간주된다. 그러나 GNU C 라이브러리는 이 값이 향상을 위한 것으로 간주하기 때문에, 이렇게 hint가 NULL인 경우에는 ai_flags를 0 대신 (AI_V4MAPPED | AI_ADDRCONFIG) 값을 사용합니다.


같이보기

inet_ntop( ) 함수

inet_pton( ) 함수

addrinfo 구조체



이 댓글을 비밀 댓글로

[네트워크/C] sockaddr, sockaddr_in, sockaddr_un 구조체 - 소켓 주소 정보를 나타낸다

by Blogger 하얀쿠아
2017. 6. 14. 01:56 소프트웨어 Note/C

기본 지식

'Socket Address Structures' 에 관해서 설명하는 글이다.

리눅스/유닉스 시스템에서는 소켓(socket)의 통신 대상을 지정하기 위해 '주소(address)'를 사용한다.
이 '주소' 라는 것을 저장하거나 표현하는데 사용하는 구조체가 바로, 본 포스팅에서 설명하고자 하는 'struct sockaddr' 이다.
bind( ), connect( ) 와 같은 함수들이 2번째 매개변수로써 바로 이 'struct sockaddr' 을 받는다.

이 struct sockaddr은 기본 형태이고, 주소체계(Address family)값에 따라서 구조체를 형변환 해서 사용하면 편리하다.
즉, 일반적으로 개발을 하다보면 struct sockaddr_in, struct sockaddr_un, struct sockaddr_in6 등의 구조체와 상호 형변환을 해서 사용하게 된다.

주소체계 유형은 아래와 같은 것들이 있다

AF_INET, AF_INET6, AF_UNIX, AF_LOCAL, AF_LINK, AF_PACKET


관련 헤더

다음 세가지 헤더파일이 관련있다.

#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */



sockaddr 구조체

sockaddr 구조체는 소켓의 주소를 담는 기본 구조체 역할을 한다.

속된말로, '소켓 주소의 와꾸[각주:1]를 잡는 녀석' 쯤으로 이해하면 쉽다.

아래와 같이 정의되어 있다.

struct sockaddr {
	u_short    sa_family;     // address family, 2 bytes
	char    sa_data[14];     // IP address + Port number, 14 bytes
};


구성은 정말 간단하다. 2개의 멤버변수를 가진 구조체이다.

 sa_family : 주소체계를 구분하기 위한 변수이며, 2 bytes 이다. 참고로, u_short는 unsigned short를 말한다.

 sa_data : 실제 주소를 저장하기 위한 변수다. 14 bytes 이다.

즉, 이 구조체는 16 bytes 의 와꾸(틀, 크기)를 잡아주는 녀석이다.


sockaddr_in 구조체 : AF_INET인 경우

위에서 정의된 sockaddr 구조체에서 sa_family가 AF_INET인 경우에 사용하는 구조체이다.

sockaddr을 그대로 사용할 경우, sa_data에 IP주소와 Port번호가 조합되어 있어 쓰거나 읽기 불편하다.

그래서 sockaddr_in을 사용한다. 이 구조체에서 사용하는 IP주소는 IPv4 주소체계를 사용한다.


struct sockaddr_in {
	short    sin_family;          // 주소 체계: AF_INET
	u_short  sin_port;            // 16 비트 포트 번호, network byte order
	struct   in_addr  sin_addr;   // 32 비트 IP 주소
	char     sin_zero[8];         // 전체 크기를 16 비트로 맞추기 위한 dummy
};

struct  in_addr {
	u_long  s_addr;     // 32비트 IP 주소를 저장 할 구조체, network byte order
};


sin_family : 항상 AF_INET을 설정한다. 이것은 필수다.

sin_port : 포트번호를 가진다. 2bytes 이다. 즉, 포트번호는 0~65535 의 범위를 갖는 숫자 값이다. 이 변수에 저장되는 값은 network byte order이어야 한다. 

참고로, 1024이하의 포트 번호는 'privileged port' 혹은 'reserved port' 라고 불리우는 예약된 포트이다. 따라서, 권한을 가진 프로세스만이 1024 이하의 소켓주소에 대해서 바인딩 (bind) 할 수 있다. 여기서 '권한을 가진 프로세스' 라 함은, 리눅스에서는 'CAP_NET_BIND_SERVICE' 라는 capability를 가지고 있는 사용자 영역(user space)의 프로세스를 지칭한다.

두번째 참고로, raw socket (socket type을 'SOCK_RAW' 로 생성한 소켓)은 포트 번호 라는 개념을 가지지 않는다. 포트번호는 TCP 혹은 UDP에서만 구현하는 개념이다.

sin_addr : 호스트 IP주소이다.
이 변수에는 INADDR_ 로 시작하는 값, 예를 들면 'INADDR_ANY' 와 같은 것이 저장되어야 한다. 혹은 inet_aton( ), inet_addr( ), inet_makeaddr( ) 과 같은 라이브러리가 제공하는 함수의 반환값이 저장되어야 한다. 혹은 name resolver를 통해 직접 설정도 가능하다. 이 방법은 gethostbyname( )을 알아보면 된다.

sin_zero : 8 bytes dummy data이다. 반드시 모두 0으로 채워져 있어야 한다. sockaddr_in가 sin_zero를 제외한 크기(sin_family + sin_port + sin_addr)가 8 bytes이므로, 총합이 16 bytes이다. struct sockaddr구조체와 크기를 일치시키려는 목적인 걸 눈치 챌 수 있을 것이다.

여백을 채워서 16 bytes로 만든다는 뉘앙스로 padding bytes 혹은 padding data 라고도 한다.


#struct sockaddr_in 사용 예제

아래는 소켓을 열고 원격지의 호스트 (주소 127.0.0.1, 포트 80)에 연결하는 방법을 보여준다.

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <inttypes.h>
#include <strings.h>

int main(void)
{
	int sock;
	struct sockaddr_in in;

	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		perror("socket");
		return (1);
	}

	bzero(&in, sizeof (struct sockaddr_in));
	in.sin_family = AF_INET;
	in.sin_port = htons(80);
	if (inet_pton(AF_INET, "127.0.0.1", &in.sin_addr) != 1) {
		perror("inet_pton");
		return (1);
	}

	if (connect(sock, (struct sockaddr *)&in,
				sizeof (struct sockaddr_in)) != 0) {
		perror("connect");
		return (1);
	}

	/* use socket */

	return (0);
}



sockaddr_in6 구조체 : AF_INET6인 경우

IPv6 주소체계의 소켓주소를 사용하는 구조체이다.

IPv4에 사용되는 struct sockaddr_in과는 달리, struct sockaddr_in6는 bzero( ) 또는 memset( )과 같은 함수를 통해 0으로 초기화 해주어야 하는 몇가지 멤버변수들이 추가되어 있다. 만약 strcut sockaddr_in6을 사용하기 전에 모두 0으로 초기화 하지 않으면, 프로그램은 정의되지 않은 동작을 야기할 수 있으므로 주의 해야 한다.

아래와 같은 멤버변수를 가지고 있다.

#include <netinet/in.h>

struct sockaddr_in6 {
	sa_family_t     sin6_family;   /* Address family, AF_INET6 */
	in_port_t       sin6_port;     /* port number */
	uint32_t        sin6_flowinfo; /* IPv6 flow information and traffic class */
	struct in6_addr sin6_addr;     /* IPv6 address */
	uint32_t        sin6_scope_id; /* Interface Scope ID (new in 2.4) */
};

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

sin6_family : 항상 AF_INET6 이어야만 한다.

sin6_port : IPv6 포트를 저장하는 변수이다.
이 값은 직접 조작하기보다는, ntohs( ) htons( ) 로 조작하는 같이 좋다. 혹은 앞의 2개 함수와 동등한 수준으로 직접 조작하거나 말이다.

sin6_flowinfo : IPv6 헤더와 연관된 트래픽 클래스와 플로우 레이블을 포함한다. 

sin6_addr : 16bytes (128bits)의 IPv6주소를 저장하는 변수이다.
inet_pton( ) 함수 외에도, netinet/in.h 헤더파일에는 IPv6 주소를 조작하고 테스트하기 위한 많은 매크로를 정의하고 있다. 이들에 대한 설명은, 추후 IPv6에 대해 설명하는 포스팅에서 따로 자세히 다뤄보도록 하겠다.

sin6_scope_id : sin6_addr의 주소범위에 따라 달라지는 식별자를 포함할 수 있다. 프로그램은 sin6_scope_id를 초기화 할 필요는 없다. 다양한 라이브러리 함수 호출의 결과로, 운영체제에 의해 채워지는 값이다.


#struct sockaddr_in6 사용 예제

아래 예제는 소켓을 열고 포트 12345의 로컬 IPv6 주소 :: 1 포트에 바인드하도록 준비하는 방법을 보여준다.
예제는 libsocket과 libnsl 을 링크해야 한다. (컴파일시 링크옵션 추가 필요함 : -lsocket -lnsl)

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <inttypes.h>
#include <strings.h>

int
main(void)
{
	int sock6;
	struct sockaddr_in6 in6;

	if ((sock6 = socket(AF_INET6, SOCK_STREAM, 0)) < 0) {
		perror("socket");
		return (1);
	}

	bzero(&in6, sizeof (struct sockaddr_in6));
	in6.sin6_family = AF_INET6;
	in6.sin6_port = htons(12345);
	if (inet_pton(AF_INET6, "::1", &in6.sin6_addr) != 1) {
		perror("inet_pton");
		return (1);
	}

	if (bind(sock6, (struct sockaddr *)&in6,
				sizeof (struct sockaddr_in6)) != 0) {
		perror("bind");
		return (1);
	}

	/* use server socket */

	return (0);
}




sockaddr_un 구조체 : AF_UNIX 또는 AF_LOCAL인 경우

struct sockaddr_un은 하나의 시스템에서 서로다른 프로세스 사이의 통신에 사용되는 소켓의 주소를 지정하는데 사용하는 구조체이다.

'Unix Domain Socket' 이라고도 한다. 이 Unix domain 소켓은 파일 시스템의 경로에 의해 식별된다.

struct sockaddr_un은 아래와 같은 멤버를 가지고 있다.


#include <sys/un.h>

#define UNIX_PATH_MAX    108

struct sockaddr_un {
    sa_family_t sun_family;               /* AF_UNIX */
    char        sun_path[UNIX_PATH_MAX];  /* pathname */
};

sun_family : 항상 AF_UNIX 값을 가져야 한다.

sun_path : 파일 시스템 경로를 지정한다. NUL 로 끝나는 문자열(C string)이어야 한다. 경로의 최대 길이는 NUL terminator를 포함해서 108 bytes이다.


주의 

응용프로그램이 자신의 local address를 표현할 때도 사용하며, 원격지의 상대방 주소를 표현할 때도 소켓 주소(socket address)로 표현한다.


참고 자료

https://illumos.org/man/3socket/sockaddr_in

  1. 일본어로 테두리 또는 범위라는 뜻이다. 자세한 내용은 다음 주소를 찾아가보자. https://namu.wiki/w/%EC%99%80%EA%BE%B8 [본문으로]
이 댓글을 비밀 댓글로

[네트워크/C] ifreq 구조체 (struct ifreq)

by Blogger 하얀쿠아
2017. 6. 14. 01:19 소프트웨어 Note/C

관련 헤더

#include <sys/ioctl.h>
#include <net/if.h>


설명

linux에서 네트워크 장치의 설정과 관련된 대부분의 ioctl( ) 호출에 세번째 인자로 사용하는 구조체이다.

참고로, linux의 ioctl( ) 호출시 첫번째 인자는 file descriptor인데, family나 type과 관계없이 어떠한 소켓 file descriptor라도 사용할 수 있다.

구조체의 멤버변수는 아래와 같다.


struct ifreq {
	char ifr_name[IFNAMSIZ]; /* Interface name */
	union {
		struct sockaddr ifr_addr;
		struct sockaddr ifr_dstaddr;
		struct sockaddr ifr_broadaddr;
		struct sockaddr ifr_netmask;
		struct sockaddr ifr_hwaddr;
		short           ifr_flags;
		int             ifr_ifindex;
		int             ifr_metric;
		int             ifr_mtu;
		struct ifmap    ifr_map;
		char            ifr_slave[IFNAMSIZ];
		char            ifr_newname[IFNAMSIZ];
		char           *ifr_data;
	};
};


일반적으로 개발자는, ioctl( )의 대상으로 삼고자 하는 인터페이스의 이름을 ifr_name에 설정한다.

그 외의 다른 멤버 변수들은 union 이다. 

즉, 멤버 변수 ifr_name만 IFNAMSIZ 만큼의 메모리를 차지하고, 그 외는 메모리공간을 공유한다. (시작 번지가 같다)


알려진 버그

glibc 2.1를 사용하는 경우,  헤더 net/if.h 파일에 'ifr_newname' 매크로가 누락되어 있다.

따라서 컴파일 타임 에러가 발생할 수 있다.

임시로 아래 코드를 추가하여 컴파일 에러를 해결할 수 있다.


#ifndef ifr_newname
#define ifr_newname     ifr_ifru.ifru_slave
#endif



이 댓글을 비밀 댓글로

[우분투] Ubuntu 16.04 삼바(Samba) 설치 및 사용하기

by Blogger 하얀쿠아
2017. 6. 11. 08:00 소프트웨어 Note/Linux

윈도우 PC와 리눅스 PC 두대를 사용해서 개발등을 하다 보면, 두 PC간에 파일을 빈번하게 이동할 일이 발생한다.

이럴때 선택할 수 있는 방법은 여러가지가 될 수 있다.


우선 USB메모리나, 외장 하드디스크와 같은 저장장치를 이용할 수도 있다.

혹은 이메일이나 메신저의 파일전송 기능, 혹은 인터넷 게시판에 업로드 하는 방법 등으로 전달할 수도 있다.

좀더 우아한 방법으로는 one drive나 google drive 혹은 dropbox와 같은 클라우드 저장소를 활용할 수 도 있다. 


그러나 이 방법도 한번 고려해 보자.

'Samba' 라는 프로그램을 활용해서 우분투의 디렉토리를 공유 디렉토리로 만들고, 윈도우에서 접근 할 수 있는 방법이다.










Samba 설치하기


우분투 shell에서 apt 커맨드로 간단히 설치할 수 있다.


$ sudo apt install samba


16.04 우분투에서 실제로 내가 설치했던 방법이다. 

혹은 아래와 같이 기존의 방법 이용 해도 된다.


$ sudo apt-get install samba 



Samba 계정 추가

아래 명령어를 통해 사용하려는 계정을 간단히 계정을 추가할 수 있다.

명령어를 실행시키면 곧이어 사용할 비밀번호를 두번 묻고, 모두 입력하면 계정 추가 과정이 완료된다.


나는 아래와 같이 'Jeon' 을 계정명으로 사용했다.


$ sudo smbpasswd -a [계정명]


eg. $ sudo smbpasswd -a Jeon


smbpasswd는 다양한 기능을 가지고 있으니, 궁금하다면 --help 옵션으로 자세히 살펴보자.




Samba 설정 변경 및 적용

위에서 추가한 계정명을 기억하자.

그 내용으로 /etc/samba/smb.conf 파일을 편집해야 한다.

우선, 파일을 연다.


$ sudo vi /etc/samba/smb.conf


그리고, 가장 아래로 내려간 후, 아래를 참고해서 본인에 해당하는 내용을 추가한다.



[Jeon] 부분은 본인이 smbpasswd로 추가한 계정명으로 입력한다.

comment는 이 계정에 대해 간략한 설명을 입력한다.

path는 공유하려는 디렉토리의 경로이다. 절대경로를 입력한다. 나는 /home/jeon와 그 아래의 모든 디렉토리를 공유하고자 한다.

valid users는 이 공유 디렉토리에 연결하려는 사용자이름이다. 

삼바에 접근하는 쪽(윈도우 쪽) 에서 파일 쓰기를 허용할 지를 결정하는 옵션이다. (yes/no) 를 입력한다.

create mask와 directory mask는 그대로 따라 입력하면 크게 무리가 없을 것이다.


변경한 smb.conf를 적용하기 위해, 삼바 데몬 프로세스 (smbd)를 재시작 시킨다.


$ sudo /etc/init.d/smbd restart



윈도우에서 리눅스의 저장장치 접근

지금부터 윈도우에서 리눅스 파일을 읽고, 수정하고, 지울 수 있다.

즉, 리눅스의 모든 파일을 윈도우에서 공유받게 되는 것이다.


윈도우 탐색기를 열고, 주소표시줄에 아래와 같이 리눅스 PC의 IP주소를 입력하고 엔터를 누른다.




그러면, smb.conf에서 설정한 path의 내용이 보이게 된다.





이 댓글을 비밀 댓글로
    • 리눅스
    • 2018.05.16 15:38
    마지막 \\아이피 입력 부분에서 엑세스 거부가 뜨는데 왜 이러는걸까요
    • 계정설정이 잘못되었을 것 같습니다. 현재 linux계정과 samba 계정 설정을 다시 한번 살펴보시는게 도움될것 같아요.
    • 우분투
    • 2018.06.05 18:16
    접근하는 디폴트 포트 번호가 어떻게 될까요?
    포트 포워딩해서 외부망에서 접근을 해야해서요
    • ganggang
    • 2018.07.07 15:14
    감사합니다!! 라즈베리 파이에 똑같이 따라해 성공했네요!
    그런데 netstat -tulpn | grep smbd 하니까 139, 145번 포트를 사용하는데 맞나요?
    • 쌈바
    • 2018.09.27 09:41
    계정추가하면 Failed to add entry for user 이렇게 뜨는데 뭐가 잘못된걸까요 ㅠㅠ
    • sudo smbpasswd -a [계정명]
      이 명령사용할 때, 말씀하신 에러가 발생하는 경우는, 현재 사용중인 linux에 존재하지 않는 계정명인 경우라고 합니다.
  1. 보고 잘따라했습니다 감사합니다
    • 2019.05.31 07:38
    config 뭐 대단하다고 복붙이 없어~~

[우분투] Ubuntu 16.04 화면 캡쳐 방법

by Blogger 하얀쿠아
2017. 6. 10. 16:06 소프트웨어 Note/Linux


우분투를 사용하면서 화면을 캡쳐해야 할 경우가 종종 있다.

우분투 16.04 사용하기 시작한지 2일째인데, 의외로 유용한 기능이 많은 것 같다.

그것 중 하나가, 다양한 방식의 화면 캡쳐 기능을 제공하는 것이다.


참고로 우분투 16.04 에서는 화면 캡쳐를 스크린샷(Screenshot) 이라는 용어로 표현하고 있었다.


우분투는 다음과 같이 총 6가지의 스크린샷 찍기 방법을 제공하고 있다.


스크린샷 찍기 / 활성화 된 창의 스크린샷 찍기 / 영역의 스크린샷 찍기

클립보드로 스크린샷 복사 / 활성화 된 창의 스크린샷을 클립보드로 복사 / 영역의 스크린샷을 클립보드로 복사


각 방법에 대해 단축키를 살펴보자.

당신이 설정을 변경하지 않았다면, 이 글에서 소개하는 기본 단축키가 동작할 것 이다.



스크린샷 찍기 

단축키 : Print


기본적인 스크린샷 찍기 방법이다.

전체 화면이 스크린샷으로 찍히며, 파일로 저장하기 위한 창이 뜬다.



활성화 된 창의 스크린샷 찍기 

단축키 : Alt + Print


현재 마우스로 한번 클릭해서, 맨앞에 떠있는 활성화 상태의 창 1개만 스크린샷을 찍는다.

파일로 저장하기 위한 창이 뜬다.


영역의 스크린샷 찍기 

단축키 : Shift + Print


위 단축키를 누르면 마우스가 '십자' 모양으로 변경되면서 영역을 지정할 수 있는 상태가 된다.

영역을 지정하고 나면, 파일로 저장하기 위한 창이 뜬다.


클립보드로 스크린샷 복사 

단축키 : Ctrl + Print


전체 화면을 스크린샷으로 찍는다.

파일로 저장하기 위한 창은 뜨지 않는다.

대신, 클립보드로 방금 찍은 스크린샷이 복사되어, 그림판이나 Slide편집 툴 등에 Ctrl+V 로 붙여 넣을 수 있다.


활성화 된 창의 스크린샷을 클립보드로 복사 

단축키 : Ctrl + Alt + Print


현재 마우스로 한번 클릭해서, 맨앞에 떠있는 활성화 상태의 창 1개만 스크린샷을 찍는다.

파일로 저장하기 위한 창이 뜨지는 않는다.

대신, 클립보드로 방금 찍은 스크린샷이 복사되어, 그림판이나 Slide편집 툴 등에 Ctrl+V 로 붙여 넣을 수 있다.


영역의 스크린샷을 클립보드로 복사 

단축키 : Shift + Ctrl + Print


위 단축키를 누르면 마우스가 '십자' 모양으로 변경되면서 영역을 지정할 수 있는 상태가 된다.

영역을 지정하고 나면, 파일로 저장하기 위한 창은 뜨지 않는다.

대신, 클립보드로 방금 찍은 스크린샷이 복사되어, 그림판이나 Slide편집 툴 등에 Ctrl+V 로 붙여 넣을 수 있다.



스크린샷 단축키를 변경하는 방법

원한다면 소개한 기본단축키를 입맛에 따라 변경하는 방법도 있다.

먼저, 다음 경로를 따라 키보드의 설정 창을 연다.


System Settings → Keyboard




키보드를 선택하고 열어보면, 아래와 같이 단축키를 설정할 수 있는 'Shortcuts' 탭이 있다.

선택 후 좌측 아래의 여러 항목들 중, 'Screenshots' 항목을 선택하면, 오른쪽에 Screenshot을 찍기 위한 단축키들이 기능별로 나열 된다.

단축키 부분을 마우스로 선택 후, 변경할 단축키를 입력하면 변경이 가능하다.






Print키는 어디에 있지?

보통 우분투를 사용할 정도의 사람이라면 이걸 모르지는 않겠지만.

혹시나 싶어서.

위 설명중, 단축키의 'Print' 키가 무엇인지 모른다면 아래 사진을 한번 보자.

Print Screen 키를 말하는 것이다.



일반 키보드의 'Print Screen' 키


음... 키캡 사이사이에 먼지가 보이지만 애써 외면하고 붉은 사각형만 보기로 하자.


현재 사용중인 10 key-less 일반키보드이다. (도쿄 출장 당시에 아키하바라 전자상가에 들러 구입했던 FILCO 청축 키보드이다.)

위와 같이 보통, F12 키 옆에 print screen 키가 존재한다.



노트북 키보드의 'Print Screen' 키


노트북은, 작은 키보드 면적으로 인해, key의 인쇄도 'prt sc' 와 같이 축약되어 있고, 다른 키와 중첩으로 배치되어 있어서, 'fn' 키와 같이 눌러야 동작하는 경우도 있다.

이 댓글을 비밀 댓글로

[우분투] Ubuntu 16.04 한글키보드 한글입력 방법

by Blogger 하얀쿠아
2017. 6. 10. 08:30 소프트웨어 Note/Linux

Prologue

2009년부터 사용하던 오래된 노트북 컴팩 CQ40.

그동안 약 4년정도를 윈도우7을 설치해서 사용해오다가, 포맷을 하고 우분투 16.04 를 설치해서 사용하기로 했다.

설치를 마친 후, 한글 입력을 하려니 왠걸. 되질 않는다.

기억을 더듬어보자.

회사 업무PC에 우분투 12.04를 처음 설치 한 후, 한글 입력이 곧바로는 되지 않았었다.

몇가지 설정을 더 해주고, 별도의 Nabi라는 입력기를 설치해야만 한글 입력이 가능했던 것 같다.


본격 Ubuntu 16.04 한글 입력 가능하게 만들기

한글키보드로 한글 입력을 할 수 있게 만드는 과정은 간단하다.


1. 입력기 설치( IBus 설치 )

2. 몇가지 설정


먼저, 입력기 설치다. IBus라는 입력기를 설치할 것이다. 

IBus hangul 패치버전 설치를 위한 PPA(Personal Package Archive)를 아래와 같이 추가한다.

$ sudo add-apt-repository ppa:createsc/3beol


PPA를 추가했으니 실제로 IBus와 IBus-hnagul을 설치 한다.

$ sudo apt-get update

$ sudo apt-get install ibus ibus-hangul


이제, 몇가지 설정이다.

아래그림을 참조하자. System Settings 메뉴에서 두가지만 찾으면 된다.

첫번째. Language Support.

'System Settings' → Language Support → 진입초기에 install을 요구할 경우 진행한다.

'Language for menus and windows'에서 아래로 스크롤 해보면 한국어가 보일 것이다. 만약 없다면, Install/Remove Languages 를 선택하여 한국어 혹은 Korean을 선택한다.


두번째. Text entry.

'System Settings' → Text Entry → 좌측 하단의 '+' 버튼 → Korean (Hangul) (IBus) 선택하여 추가한다.

한글언어 인 경우는 아래 사진처럼 '한국어 (Hangul)(IBus)' 라고 표시된다.


여기까지만 하면, 한/영 전환이 가능해지고, 한글입력이 가능해진다.

한영전환 키는 'Text Entry' 메뉴의 'Super+Space' 키가 기본값이다.


우분투에서 Super키라 하면, 윈도우 키를 말한다.

한영 전환을 해서 입력해보면, 아래와 같이 system tray에 입력기 아이콘이 바뀌면서 표시된다.

태극문양이 IBus hangul의 아이콘이다.



이 댓글을 비밀 댓글로
    • 즐겁게살자
    • 2017.07.16 17:02
    덕분에 드디어 깔았네요.
    감사합니다. ^ㅡ^*
    • mshan0209
    • 2017.11.01 02:39
    감사합니다.
    • ㅅㅅㅅ
    • 2017.11.07 21:51
    감사합니다 도움받고 가요
    • notebook
    • 2019.02.01 11:49
    친절하고 상세하게 써있네요. 덕분에 잘 설치했습니다 .감사합니다 :)
    • v
    • 2019.12.01 22:59
    감사합니다!!
    • 박철호
    • 2019.12.10 04:13
    저도 드디어 성공했어요.
    감사합니다

[안드로이드] 펜딩인텐트 (Pending Intent)

by Blogger 하얀쿠아
2017. 5. 23. 01:06 소프트웨어 Note/Android

안드로이드의 펜딩인텐트 (Pending Intent)


펜딩인텐트 (Pending Intent) 는 인텐트의 일종이다. 

그러면 일반 인텐트와의 차이점은 무엇인지 알아보는 것부터 시작해보자.

컴포넌트에서 다른 컴포넌트에게 작업을 요청하는 인텐트를 사전에 생성시키고 만든다는 점과 "특정 시점"에 자신이 아닌 다른 컴포넌트들이 펜딩인텐트를 사용하여 다른 컴포넌트에게 작업을 요청시키는 데 사용된다는 점이 차이점이다.

수행시킬 작업 및 인텐트(실행의도)와 및 그것을 수행하는 주체를 지정하기 위한 정보를 명시 할 수 있는 기능의 클래스라고 보면 된다. 

이해하기 쉽게 말하자면, 아래와 같이 하고 싶을때 사용할 수 있는 것이다.





A한테 이 B인텐트를 C시점에 실행하라고 해. 지금은 실행하지 말고.

이 클래스의 인스턴스는 getActivity (Context, int, Intent, int), getActivities (Context, int, Intent [], int), getBroadcast (Context, int, Intent, int) 및  getService(Context, int, Intent, int) 가 반환 하는 객체를 다른 응용 프로그램으로 전달 할 수 있으므로 앱 개발자가 명시하는 작업을 수행 할 수 있다.

PendingIntent를 다른 응용 프로그램에 제공하면 다른 응용 프로그램이 자신과 동일한 권한과 ID로 지정된 것처럼 작업을 수행 할 수있는 권한이 부여된다. 따라서 PendingIntent를 작성하는 방법에주의해야한다. 예를 들어, 제공하는 기본적인 인텐트는 컴포넌트 이름이 자신이 가진 컴포넌트들 중 하나를 명시적으로 지정해야 하며,  궁극적으로 그곳으로 보내지는 것을 보장해야한다.

사용되는 몇가지 사례

펜딩인텐트를 사용하는 대표적인 몇가지 예가 있다.

  • 사용자가 Notification을 통해 특정한 동작을 할 때, 실행되는 인텐트를 생성함 (NotificationManager가 인텐트를 실행)
  • 사용자가 AppWidget을 통해 특정한 동작을 할 때,  실행되는 인텐트를 생성함 (홈 스크린이 인텐트를 실행)
  • 미래의 특정 시점에 실행되는 인텐트를 선언함 (안드로이드의 AlarmManager가 인텐트를 실행)

안드로이드 앱을 구현할 때, 인터넷으로부터 파일을 다운로드 하는 로직은 대부분 서비스에서 이루어지도록 구성한다. 
그런데 서비스는 액티비티와 달리 화면에 나타나지 않는다. 따라서 서비스는 다운로드의 진행중이라는 사실 및 진행정도를 화면 상단에 위치한 노티피케이션의 상태바(Status Bar)를 통해서 표현한다. 다운로드가 현재 진행 중이라는 상황을 표시하는 아이콘 등으로 말이다. 그리고 다운로드가 완료된 후에는 아이콘으로 다운로드 완료의 상태를 보여주게 된다.

사용자가 상태바의 아이콘을 확인하고 안드로이드 화면의 상태바를 누르면서 나타나는 바를 잡아 아래로 끌어당기면 나타나는 화면을 노티피케이션 리스트(Notification List)  또는 확장 메시지라 한다. 그리고 만약 서비스가 이 Notification List에 '다운로드 완료' 를 표시를 추가해놓았고, 사용자가 이것을 클릭하면, 노티피케이션은 사전에 서비스에서 작성한 펜딩인텐트를 사용하여 다운로드된 파일을 읽을 수 있는 애플리케이션을 호출하고 다운로드 완료된 파일을 호출된 애플리케이션으로 재생(혹은 보여줌) 하게 된다. 

펜딩인텐트는 안드로이드 App의 각각의 컴포넌트들이 펜딩인텐트를 생성할 수 있도록 다음과 같은 메서드를 제공한다.

아래의 메서드들을 통해 펜딩인텐트를 사용하고자 하는 컴포넌트 유형을 지정해야 한다는 뜻이다.


  • getActivity(Context, int, Intent, int) -> Activity를 시작하는 인텐트를 생성함
  • getBroadcast(Context, int, Intent, int) -> BroadcastReceiver를 시작하는 인텐트를 생성함
  • getService(Context, int, Intent, int)  -> Service를 시작하는 인텐트를 생성함

잘 보면, 3개의 메서드는 모두 Context를 필요로 한다. 이 Context는 '현재 App의 Context'이다.


예제 코드

실제로 PendingIntent를 활용해 Notification 발생시키는 예제 코드를 살펴보겠다.

이 예제코드를 따라가면 아래 화면과 같은 앱을 얻게될 것이다.





예제코드를 살펴보자.

총 5개의 파일(AndroidManifest.xml, MainActivity.java, NotificationSomething.java, activity_main.xml, notification_something.xml)  을 보게 될 것이다.

하나의 android application project를 생성하고, 아래와 같이 파일을 구성하자.

layout 2개와 java코드 2개를 만들면 된다.




AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.pendingintent"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="16" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
 
        <activity android:name=".NotificationSomething">
        </activity>
    </application>

</manifest>


앱이 실행되면 처음 보여지는 메인 Activity를 구성하는 layout이다. 1개의 버튼을 가지고 있다.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.tistory.webnautes.notification.MainActivity">
 
    <Button
        android:text="노티피케이션 발생시키기"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:id="@+id/button" />
</RelativeLayout>


앱이 실행되면 처음 보여지는 메인 Activity의 소스코드이다. 버튼을 눌렀을 때, NotificationSomethings( )라는 메서드를 호출하며, PendingIntent를 통해 Notification을 발생시키고 있다. Notification을 발생시키는 intent에서 putExtra( ) 메서드를 통해 몇가지 데이터를 넘기는 것을 주목하자. 특히 notificationId는 나중에 Notification을 목록에서 지울 때 사용한다.

MainActivity.java

package com.example.pendingintent;

import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.support.v4.app.NotificationCompat;


public class MainActivity extends Activity {
	Button button = null;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		 
        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View arg0) {
                NotificationSomethings();
            }
        });
	}

    public void NotificationSomethings() {
        Resources res = getResources();
        int notfi_id = 1;
 
        Intent notificationIntent = new Intent(this, NotificationSomething.class);
        notificationIntent.putExtra("notificationId", notfi_id); //전달할 값
        notificationIntent.putExtra("extraString", "Hello PendingIntent"); //전달할 값
        PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
 
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
 
        builder.setContentTitle("상태바 드래그시 보이는 타이틀")
                .setContentText("상태바 드래그시 보이는 서브타이틀")
                .setTicker("상태바 한줄 메시지")
                .setSmallIcon(R.drawable.ic_launcher)
                .setLargeIcon(BitmapFactory.decodeResource(res, R.drawable.ic_launcher))
                .setContentIntent(contentIntent)
                .setAutoCancel(true)
                .setWhen(System.currentTimeMillis())
                .setDefaults(Notification.DEFAULT_ALL);
 
        
 /*
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
            builder.setCategory(Notification.CATEGORY_MESSAGE)
                    .setPriority(Notification.PRIORITY_HIGH)
                    .setVisibility(Notification.VISIBILITY_PUBLIC);
        }
 */
        NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        nm.notify(notfi_id, builder.build());
    }
}


첫 화면에서 발생한 Notification을 눌렀을 때 보여지는 Activity의 layout이다.

notification_something.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
 
    <TextView
        android:text="TextView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/textView" />
</LinearLayout>


첫 화면에서 발생한 Notification을 눌렀을 때 보여지는 Activity의 소스코드이다.

NotificationSomething.java

package com.example.pendingintent;

import android.app.Activity;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Bundle;
import android.widget.TextView;

public class NotificationSomething extends Activity {

	@Override
	public void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.notification_something);
        CharSequence s = "전달 받은 값: ";
        int id=0;
        String extraData = "";
 
        Bundle extras = getIntent().getExtras();
        if (extras == null) {
            s = "error";
        }
        else {
            id = extras.getInt("notificationId");
            extraData = extras.getString("extraString");
        }
        TextView t = (TextView) findViewById(R.id.textView);
        s = s+" "+ extraData + "id:" + id;
        
        t.setText(s);
        NotificationManager nm =
                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
 
        //노티피케이션 제거
        nm.cancel(id);
	}
}



관련 Reference Document 바로가기

https://developer.android.com/reference/android/app/PendingIntent.html

이 댓글을 비밀 댓글로

[안드로이드] 안드로이드 Wi-Fi를 앱에서 소스 코드(API)로 끄고 켜는 방법

by Blogger 하얀쿠아
2017. 5. 12. 00:47 소프트웨어 Note/Android

안드로이드 Wi-Fi를 코드(API)로 끄고 켜는 방법

종종 앱을 개발하다보면, Wi-Fi를 앱에서 프로그램으로 제어 하고 싶을 때가 있다.
안드로이드 장치의 Wi-Fi를 사용자의 터치 입력이 아니라, 자신이 개발중인 앱의 소스 코드 상에서, API호출을 하여 끄거나 켜는 방법을 알아보자.




우선, 아래와 같이 Wi-Fi 관련 권한들을 매니페스트 파일(AndroidManifest.xml)에 선언해야 한다.

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
<uses-permission android:name="android.permission.UPDATE_DEVICE_STATS"></uses-permission>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission>
<uses-permission android:name="android.permission.WAKE_LOCK"></uses-permission>
<uses-permission android:name="android.permission.INTERNET"></uses-permission>




이제 간단히 예제를 만들어 보겠다.

Enable 및 Disable 기능을하는 2개의 버튼을 가진 간단한 샘플 앱을 만들 것이다.


우선, 간단히 아래와 같이 xml을 통해 Layout을 만든다. 파일이름은 activity_main.xml 으로 했다.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" 
    android:background="#DE6735">

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="76dp"
        android:layout_marginTop="67dp"
        android:background="#123456"
        android:text="Enable Wifi"
        android:textColor="#fff" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/button1"
        android:layout_below="@+id/button1"
        android:text="Disable Wifi" />

</RelativeLayout>


그리고 AndroidManifest.xml 은 아래와 같이 했다.

uses-permission 부분을 신경써서 살펴보고 일치시키자.


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.wifi"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="16" />

    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
    <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS"></uses-permission>
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission>
    <uses-permission android:name="android.permission.WAKE_LOCK"></uses-permission>
    <uses-permission android:name="android.permission.INTERNET"></uses-permission>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.wifi.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>



Main Activity 소스코드인 MainActivity.java 파일에서는 아래와 같이 2개의 버튼에 listener를 달고, 각 listener 메서드 안에서 WifiManager 인스턴스를 얻어서 켜거나 끄도록 했다.

enableButton과 disableButton 의 OnClickListener 부분을 신경써서 보자.

필요하다면, 이 부분만 발췌해서 바로 본인 코드에 적용하면 될 것이다.


package com.example.wifi;

import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity {
	Button enableButton,disableButton;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		enableButton=(Button)findViewById(R.id.button1);
		disableButton=(Button)findViewById(R.id.button2);
		
		enableButton.setOnClickListener(new OnClickListener(){
			public void onClick(View v){
				WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
				wifi.setWifiEnabled(true);
			}
		});
		disableButton.setOnClickListener(new OnClickListener(){
			public void onClick(View v){
				WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
				wifi.setWifiEnabled(false);
			}
		});
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.activity_main, menu);
		return true;
	}
}


이번 포스팅에서는 Android API 를 통해 Wi-Fi를 끄거나 켜는 방법을 간단히 살펴보았다.

이 댓글을 비밀 댓글로

[안드로이드] 안드로이드 Activity

by Blogger 하얀쿠아
2017. 5. 11. 15:39 소프트웨어 Note/Android

안드로이드 액티비티란?

Activity는 일종의 애플리케이션 구성 요소로서, 사용자가 전화 걸기, 사진 찍기, 이메일 보내기 또는 지도 보기 등의 일을 하기 위해 상호작용할 수 있는 화면을 제공한다.
액티비티마다 창이 하나씩 주어지며, 안드로이드 앱 개발자는 이곳에 UI(사용자 인터페이스)를 구현하게된다. 이 창은 일반적으로 화면을 가득 채우지만, 작은 창으로 만들어 다른 창 위에 띄울 수도 있다.

하나의 애플리케이션은 보통 여러 개의 액티비티가 느슨하게 서로 묶여 있는 형태로 구성된다. 통상 한 애플리케이션 내에서 하나의 액티비티가 "Main" 액티비티로 지정되며, 사용자가 이 애플리케이션을 처음 실행할 때 이 "Main"으로 지정된 액티비티가 사용자에게 최초로 보여지게 된다. 

 각각의 액티비티는 여러 가지 작업을 수행하기 위해 또 다른 액티비티를 시작할 수 있다. 새로운 액티비티가 시작될 때마다 이전 액티비티는 중단되지만 시스템은 해당 액티비티를 스택("백 스택")에 보존하게된다. 새로운 액티비티가 시작되면, 이전 액티비티는 백 스택으로 Push되며, 새로운 액티비티가 사용자 포커스를 갖게된다. 백 스택은 기본적인 "후입선출" 방식을 준수하므로, 사용자가 현재 액티비티를 끝내고 'Back'키를 누르면 해당 액티비티가 스택에서 Pop되고, 스택상에서는 지워지며, 이전 액티비티가 재개(resume)된다.

 하나의 액티비티가 다른 하나의 새로운 액티비티의 시작으로 인해 중단된 경우, 액티비티에 "상태 변경이 발생했다" 라고 말할 수 있다. 이러한 액티비티의 상태 변경은 액티비티의 수명 주기 콜백 메서드를 통해 통지된다. 액티비티가 시스템 액티비티를 생성, 중단, 재시작, 제거하는 등의 상태 변화로 인해 받을 수 있는 콜백 메서드는 여러 가지가 있다. 각 콜백은 상태 변화에 알맞은 특정 작업을 수행할 기회를 개발자에게 제공한다. 예를 들어 액티비티가 중단(stop)되면 네트워크 또는 데이터베이스 연결과 같은 자원들을 해제(release)해야 한다. 액티비티가 재개(resume)되면, 필요한 리소스를 다시 획득(acquire)하여 중단된 작업을 다시 시작할 수 있다. 이러한 상태 전환은 모두 액티비티 수명 주기의 일부이다.

이 포스팅에서 다양한 액티비티 상태 사이의 전환을 적절히 관리할 수 있도록 액티비티 수명 주기가 작동하는 방식을 자세히 논하는 등, 액티비티를 구축하고 사용하는 기본적 방법을 설명해보겠다.

액티비티 생성

액티비티를 생성하려면 Activity의 서브클래스(또는 이의 기존 서브클래스)를 생성해야 한다. 

그리고, 서브클래스에서는 액티비티 생성, 중단, 재개, 소멸 시기 등과 같은 수명 주기의 다양한 상태 사이를 액티비티가 전환 할 때마다 시스템이 호출해주는 콜백 메서드를 구현해야 한다. 

가장 중요한 콜백 메서드는 아래 두가지다. 일단 이것만 기억하고 시작해보자.

onCreate()
이 콜백메서드는 반드시 구현해야 한다. 액티비티를 생성할 때 안드로이드 시스템이 이것을 호출한다. 또한 구현부 최상단에서는 액티비티의 필수 구성 요소를 초기화해야 한다. 무엇보다도 중요한 점은, 바로 이 콜백에서 setContentView()를 호출해야 액티비티의 사용자 인터페이스 레이아웃을 화면에 뜨게 할 수 있다는 점이다. 즉, 이걸 구현하지 않으면 화면에 아무것도 보이지않는다.

onPause()
시스템이 이 메서드를 호출하는 것은 사용자가 액티비티를 떠난다는 첫 번째 신호다.(다만 이것이 항상 액티비티가 소멸 중이라는 뜻은 아님)
현재 사용자 세션을 넘어서 지속되어야 하는 변경 사항을 커밋하려면 보통 이곳에서 해야 한다.(사용자가 다시 이 액티비티 화면으로 돌아오지 않을 수 있기 때문이다. 안드로이드 폰 사용할 때, 어떤 앱을 한참 쓰다가 홈버튼 누르고, 그날 그 앱을 실행 하지 않는 경우를 상상하자. 사용자는 하룻밤을 자고나서 그앱을 실행할 수 있고, 심지어 한 일주일 뒤에 그앱을 실행할 수도 있다.)

아직 다루진 않았지만 여러 액티비티 사이에서 원활한 사용자 환경을 제공하고, 액티비티 중단이나 심지어 소멸을 초래할 수도 있는 예상치 못한 간섭을 처리하기 위해 사용해야 하는 다른 수명 주기 콜백 메서드도 여러 가지 있다.


사용자 인터페이스 구현

한 액티비티에 대한 사용자 인터페이스는 뷰 계층—즉, View 클래스에서 파생된 객체가 제공한다. 

각각의 뷰는 액티비티 창 안의 특정한 직사각형 공간을 제어하며 사용자 상호작용에 대응할 수 있다. 
예를 들면, 어떤 뷰는 사용자가 터치하면 작업을 시작하는 버튼일 수 있다.

Android는 레이아웃을 디자인하고 정리하는 데 사용할 수 있도록 여러 가지 뷰를 미리 만들어 제공한다. 

"위젯"이란 화면에 시각적(및 대화형) 요소를 제공하는 뷰이다. 예를 들어 버튼, 텍스트 필드, 체크박스나 그저 하나의 이미지일 수도 있다.

"레이아웃"은 선형 레이아웃, 격자형 레이아웃, 상대적 레이아웃과 같이 하위 레이아웃에 대해 독특한 레이아웃 모델을 제공하는 ViewGroup에서 파생된 뷰이다. 또한, View와 ViewGroup 클래스(또는 기존 서브클래스)의 아래로 내려가서 위젯과 레이아웃을 생성하고 이를 액티비티 레이아웃에 적용할 수 있다.

뷰를 사용하여 레이아웃을 정의하는 가장 보편적인 방식은 애플리케이션 리소스에 저장된 XML 레이아웃 파일을 사용하는 것이다. 이렇게 하면 액티비티의 동작을 정의하는 소스 코드와 별개로 사용자 인터페이스 디자인을 유지할 수 있다. setContentView()로 액티비티의 UI로서 레이아웃을 설정하고, 해당 레이아웃의 리소스 ID를 전달할 수 있다. 그러나 액티비티 코드에 새로운 View를 생성하고 새로운 View를 ViewGroup에 삽입하여 뷰 계층을 구축한 뒤 루트 ViewGroup을 setContentView()에 전달해도 해당 레이아웃을 사용할 수 있다.



액티비티 수명 주기 관리

콜백 메서드를 구현하여 액티비티의 수명 주기를 관리하는 것은 강력하고 유연한 애플리케이션 개발에 대단히 중요한 역할을 한다. 액티비티의 수명 주기는 다른 액티비티와의 관계, 액티비티의 작업과 백 스택 등에 직접적으로 영향을 받는다.

액티비티는 기본적으로 세 가지 상태로 존재할 수 있다.

재개됨(Resumed)
액티비티가 화면 포그라운드에 있고 사용자 포커스를 갖고 있다. (이 상태를 때로는 "실행 중"이라고 일컫기도 한다).

일시정지됨(Paused)
다른 액티비티가 포그라운드에 나와 있고 사용자의 시선을 집중시키고 있지만, 이 액티비티도 여전히 표시되어 있다. 다시 말해, 다른 액티비티가 이 액티비티 위에 표시되어 있으며 해당 액티비티는 부분적으로 투명하거나 전체 화면을 덮지 않는 상태라는 뜻이다. 일시정지된 액티비티는 완전히 살아있지만(Activity 객체가 메모리에 보관되어 있고, 모든 상태 및 멤버 정보를 유지하며, 창 관리자에 붙어 있는 상태로 유지됨), 메모리가 극히 부족한 경우에는 예외적으로 안드로이드 시스템 판단에 따라, 중단시킬 수 있다.

정지됨(Stopped)
이 액티비티가 다른 액티비티에 완전히 가려진 상태다. (액티비티가 이제 "백그라운드"에 위치함). 
중단된 액티비티도 여전히 살아 있기는 마찬가지이다. (Activity 객체가 메모리에 보관되어 있고, 모든 상태와 멤버 정보를 유지하시만 창 관리자에 붙어 있지 않음 ). 
그러나, 이는 더 이상 사용자에게 표시되지 않으며 다른 곳에 메모리가 필요한 상황이 되면 언제든지 안드로이드 시스템이 임의대로 종료시킬 수 있다.


액티비티가 일시정지 또는 중단된 상태이면, 시스템이 이를 메모리에서 삭제할 수 있음을 기억하자.
이러기 위해서는 종료를 요청하거나(finish() 메서드를 호출) 단순히 이 액티비티의 프로세스를 중단시키면 된다.
또한, 종료 또는 중단된 이후에 액티비티가 다시 열릴 때는 처음부터 다시 생성해야 한다는 점을 상기하자.


수명 주기 콜백 구현

위에서 설명한 바와 같이 액티비티가 여러 가지 상태를 오가며 전환되면, 이와 같은 내용이 여러 가지 콜백 메서드를 통해 통지된다.

콜백 메서드는 모두 hooking 가능한 시점이다.

액티비티 상태가 변경될 때마다 적절한 작업을 수행시킬 필요가 있다면, 개발자는 이를 직접 재정의할 수 있다.

다음의 기본 액티비티에는 기본 수명 주기 메서드가 각각 하나씩 포함되어 있다.

public class ExampleActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // The activity is being created.
    }
    @Override
    protected void onStart() {
        super.onStart();
        // The activity is about to become visible.
    }
    @Override
    protected void onResume() {
        super.onResume();
        // The activity has become visible (it is now "resumed").
    }
    @Override
    protected void onPause() {
        super.onPause();
        // Another activity is taking focus (this activity is about to be "paused").
    }
    @Override
    protected void onStop() {
        super.onStop();
        // The activity is no longer visible (it is now "stopped")
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // The activity is about to be destroyed.
    }
}


 이와 같은 메서드를 모두 합쳐 '하나의 액티비티의 수명 주기 전체' 로 정의한다. 이러한 메서드를 구현함으로써 액티비티 수명 주기 내의 세 가지 중첩된 루프를 모니터할 수 있다.

 한 액티비티의 전체 수명은 onCreate() 호출과 onDestroy() 호출 사이를 말한다. 액티비티는 onCreate()에서 "전체" 상태(레이아웃 정의 등)의 설정을 수행한 다음 나머지 리소스를 모두 onDestroy()에 해제해 주어야 한다. 예를 들어, 액티비티에 네트워크에서 데이터를 다운로드하기 위해 배경에서 실행 중인 스레드가 있는 경우, 이는 해당 스레드를 onCreate()에서 생성한 다음 onDestroy()에서 그 스레드를 중단할 수 있어야 한다.

 한 액티비티의 가시적 수명은 onStart() 호출과 onStop() 호출 사이를 말한다. 이 기간 중에는 사용자가 액티비티를 화면에서 보고 이와 상호작용할 수 있다. 예컨대 onStop()이 호출되어 새 액티비티가 시작되면 이 액티비티는 더 이상 표시되지 않게 된다. 이와 같은 두 가지 메서드 중에서 사용자에게 액티비티를 표시하는 데 필요한 리소스를 유지하면 된다. 예를 들어, onStart()에서 BroadcastReceiver를 등록하고 UI에 영향을 미치는 변화를 모니터링하고 onStop()에서 등록을 해제하면 사용자는 여러분이 무엇을 표시하고 있는지 더 이상 볼 수 없게 된다. 시스템은 액티비티의 전체 수명 내내 onStart() 및 onStop()을 여러 번 호출할 수 있으며, 이때 액티비티는 사용자에게 표시되었다 숨겨지는 상태를 오가게 된다.

액티비티의 Foreground 수명은 onResume( ) 호출과 onPause( ) 호출 사이를 말한다. 이 기간 중에는 이 액티비티가 화면에서 다른 모든 액티비티 앞에 표시되며 사용자 입력도 여기에 집중된다. 액티비티는 포그라운드에 나타났다 숨겨지는 전환을 자주 반복할 수 있다. 예를 들어 , 기기가 절전 모드에 들어가거나 대화상자가 나타나면 onPause()가 호출된다. 이 상태는 자주 전환될 수 있으므로, 이 두 가지 메서드의 코드는 상당히 가벼워야 한다. 그래야 전환이 느려 사용자를 기다리게 하는 일을 피할 수 있다.


아래 그림은 액티비티가 상태 사이에서 취할 수 있는 이와 같은 루프와 경로를 나타낸 것이다. 직사각형은 콜백 메서드를 나타내며, 이 콜백 메서드에서 액티비티가 여러 상태 사이를 전환할 때 작업을 수행하도록 구현할 수 있다.


액티비티 수명 주기


이 댓글을 비밀 댓글로

[사설] 2017년 4월 대한민국 남 여 인구 그래프

by Blogger 하얀쿠아
2017. 5. 10. 01:21 NULL ptr/ NULL ptr

2017년 4월 대한민국 남 여 인구 그래프


2017년 4월 기준으로 한국의 남, 여 연령별 인구 그래프이다.

40세~59세의 남성 인구는 8,716,705 명이고, 40세~59세의 여성 인구는 8,498,683 명이다.

합하면 17,215,388 명이다.

전국 총 인구수는 51,722,903 명 이므로, 약 33%에 달하는 숫자이다.


2017년 4월, 한국의 남 여 연력별 인구 그래프



이 댓글을 비밀 댓글로