해당 포스트는 "안드로이드 통신+보안 프로그래밍" 책의 내용을 요약한 것이다.
: 통신 프로그램의 절반 이상은 웹과 관련있다. 웹은 다음의 특징을 가진다.
- URI(Uniform Resource Identifier)와 URL(Uniform Resource Locator)로 리소스(자원)의 위치를 지정한다.
- HTTP 프로토콜을 사용한다.
- 리소스는 음악,영상과 같은 미디어뿐만 아니라 관련된 리소스를 처리할 수 있는 HTML 언어로 만들어진 문서를 지원한다.
※ HTTP 프로토콜
: 클라이언트와 서버 사이에 이루어지는 요청과 응답으로 구성된 프로토콜이다. 보통 클라이언트에서 서버에 HTML 문서나 데이터를 요청하면 서버는 요구한 정보를 응답한다. 이와 같이 HTTP 프로토콜은 크게 요청 메시지와 응답 메시지가 존재한다. 요청 메시지는 요청 라인, 헤더, 엔티티로 구성되고 응답 메시지는 상태 라인, 헤더, 언티티로 구성된다. 각 구성 요소의 구분은 CRLF로 한다.
- 요청 라인, 상태 라인 : 메시지를 하나의 문장으로 요약한 것이다. 만약 서버에 동영상을 저장한다고 하면 요청 메시지는 수백만 바이트 이상이 될 것이다. 서버는 이 요청 메시지를 다 읽고 판단하기에는 시간이 너무 오래 걸린다. 따라서 하나의 문장으로 요약된 요청/상태 라인을 통해 시간을 절약할 수 있다.
- 헤더 : 엔티티 속성, 서버/클라이언트 현재 상태, 요청하는 리소스 정보를 갖는다. 예로 클라이언트가 요청한 리소스가 다른 서버에 있다면, 해당 서버 주소를 헤더에 넣어 알려준다. 또한 클라이언트 리소스 캐시 정보가 오래되었다면 해당 리소스를 바꾸라는 정보를 헤더에 넣어 알려준다.
- 엔티티 : 리소스를 말한다.
1. HTTP 요청 라인
: "Request-Method SP Request-URI SP HTTP-Version" 으로 표현한다. 예로 "GET http://www.google.com/index.html HTTP/1.1"와 같이 표현한다. SP는 Space 약자로 공백을 나타낸다. 요청 라인은 무엇을 달라 또는 데이터를 어디에 입력하라 등의 명령어로 구성된다. 먼저 Request-Method에 대해서 알아보자.
- OPTIONS : 현재 웹서버에서 지원하는 메서드의 종류를 알려준다. 응답 메시지 내 "Allow" 헤더에 지원하는 메서드 종류를 나타낸다.
- GET : 요청 URI가 가리키는 리소스를 요청(얻고자)할 때 사용한다. 요청 메시지의 크기는 제한되지만 특정 형식에 맞춰 있어 POST 메서드나 PUT 메서드보다 서버 처리 속도가 빠르다.
- HEAD : GET과 비슷하지만 요청 내용은 상태라인과 헤더 정보로 국한된다. 주로 클라이언트 캐시 정보 갱신 목적으로 이용한다.
- POST : 서버에게 필요한 데이터를 제공하고 서버에서 작업한 결과값을 반환받을 때 사용한다. 새로운 리소스를 업로드하거나 수정할 때 사용한다. 요청 메시지의 크기 제한은 없고 GET에 비해 처리 속도가 떨어진다.
- PUT : 요청 URI를 사용하여 서버내 리소스로 저장하겠다는 의미로 사용된다. URI가 기존에 존재하면 수정하고 없으면 새롭게 입력한다.
- DELETE : 서버에서 URI가 가리키는 리소스를 삭제하라는 의미로 사용된다.
모든 HTTP 지원 서버는 GET과 POST 메서드를 지원해야 한다. 만약 응답 메시지로 501 오류 코드를 받으면 메서드는 지원 예정이거나 아직 구현되지 않는 메서드를 뜻한다. 400 오류 코드를 받으면 해당 메서드는 지원하지 않는다는 뜻이다. 요청 URI는 절대 URI와 상대 URI로 나뉜다. 상대 URI를 사용한다면 Host 헤더에 호스트와 포트번호를 표기해야 한다.
2. HTTP 상태 라인
public class HttpGet { public static void main(String[] args) { SocketChannel server = null; WritableByteChannel destination; String host = "www.naver.com"; try{ int port = 80; String path = "/"; SocketAddress serverAddress = new InetSocketAddress(host,port); server = SocketChannel.open(serverAddress); server.configureBlocking(true); //상대 URI 형식으로 요청 라인 표현 String request = "GET " +path+" HTTP/1.1\r\n" + "Host: "+host + "\r\n" + "\r\n"; CharBuffer requestChars = CharBuffer.wrap(request); //HTTP 프로토콜이 지원하는 표준 문자셋으로 변환 Charset charset = Charset.forName("ISO-8859-1"); ByteBuffer requestBytes = charset.encode(requestChars); //HTTP 요청 메시지를 서버에 전송 server.write(requestBytes); //화면에 출력할 채널 생성 destination = Channels.newChannel(System.out); ByteBuffer data = ByteBuffer.allocateDirect(32*1024); //HTTP 응답 메시지의 헤더 정보를 화면에 출력할 지 결정 boolean Headers = true; int responseCode = -1; while(server.read(data) != -1){ data.flip(); //상태 코드를 읽는 작업을 수행한다. 상태 라인은 HTTP/1.1과 한 바이트 공백 뒤에 //상태 코드 3바이트가 나오므로 다음과 같이 한다. if(responseCode == -1){ responseCode = 100*(data.get(9) - '0') + 10*(data.get(10) - '0') + 1*(data.get(11) - '0'); } //정상 여부를 확인한다. if(responseCode<200 || responseCode >=300) System.out.println("HTTP Error: "+responseCode); //헤더 출력 여부를 결정한다. if(!Headers){ try{ //헤더와 엔티티는 '\r\n\r\n'으로 구분한다. for(;;){ if((data.get()==13) && (data.get() == 10) && (data.get() == 13) && (data.get() == 10)) break; } }catch(BufferUnderflowException e){ //엔티티를 만나지 못한 상태에서 마지막 위치 limit를 만났을 경우 //3바이트 앞으로 이동한 후 읽었던 바이트를 전부 삭제한다. data.position(data.position()-3); data.compact(); continue; } } //버퍼로 부터 데이터를 읽어 화면에 출력한다. while(data.hasRemaining()) destination.write(data); data.clear(); } }catch(Exception e){ e.printStackTrace(); }finally{ try{ if(server!=null&& server.isOpen()) server.close(); }catch(IOException e){} } } }
※ URL, URI
- URL : 인터넷 상에 존재하는 서버의 리소스(파일, 디렉토리) 위치를 가리킨다
- URN : 위치와 무관한 리소스 이름을 나타낸다.
- URI : 인터넷 상에 존재하는 리소스의 위치와 이름을 가리킨다.
: URI 는 URL(위치)와 URN(이름)을 사용한 것이다. 따라서 서버의 리소스는 URI를 통해 접근한다. 하지만 URI과 URL는 인터넷 상에서 별도 구분없이 사용된다. URL는 서버를 가리키기에 사용자 입장에서 알기 쉽지만 이름까지 일일이 알 수는 없기 때문이다. 그래서 대부분 URL을 사용하여 사이트를 지정하면 디폴트 웹 페이지(index.html)과 함께 조건에 맞는 리소스를 제공한다.
- URL 구성요소 : scheme://<user>:<password>@<host>:<port>/<url-path>?query_string#fragment
1. 스킴(Scheme) : 스키마라고도 부르며 'http', 'ftp' 같은 프로토콜을 나타낸다.
2. 호스트(Host) : 서버를 가리킨다. ex)www.google.com
3. 포트번호(Port) : TCP 포트 번호를 나타낸다. HTTP의 경우 80이 된다.
*안드로이드는 호스트와 포트를 합하여 기관(Authority)로 표현한다.
4. 경로(Path) : 구체적인 자원의 위치를 나타낸다.
5. 쿼리(Query) : 서버에 전달되는 매개변수를 말한다. "name=value&name1=value1" 로 표현한다. 쿼리에 사용하는 value는 US-ASCII(영문자)를 사용해야 되는 보장이 없어 시스템에서 지원하는 Charset으로 인코딩 작업이 필요하다.
6. 프래그먼트(fragment) : 리소스의 상세 부분을 의미한다. 예로 URL이 이북을 가리킨다면 몇 번째 페이지를 언급할 때 사용한다.
* user,password는 보안 문제로 생략한다.
- URL 메서드
1. Object getContent(Class[] type) : URL이 가리키는 위치 내 리소스를 가져와 Object 객체로 반환한다. 매개변수의 타입이 리소스 객체 타입과 다르면 null을 반환한다.
2. Object getContent() : 요청한 리소스를 가져와 Object 객체로 반환한다.
3. URLConnection openConnection() : URL이 가리키는 호스트의 리소스 주소를 갖는 URLConnection 객체를 반환한다.
4. InputStream openStream() : URL이 가리키는 리소스의 응답 메시지 엔티티를 수신하고 InputStream 객체로 반환한다. HTTP의 헤더나 프로토콜 정보는 제공하지 않는다.
ex)
public class URLReader { public static void main(String[] args) throws Exception{ URL google = new URL("http://www.naver.com/"); BufferedReader in = new BufferedReader(new InputStreamReader(google.openStream())); String line; while((line = in.readLine())!=null) System.out.println(line); in.close(); } }
'안드로이드 > 통신' 카테고리의 다른 글
8. 웹뷰와 웹뷰에서 자바스크립트 사용하기 (0) | 2017.07.23 |
---|---|
7. URLConnection, HttpURLConnection, HttpsURLConnection (0) | 2017.07.22 |
5. 소켓 채널 통신, NIO 버퍼, 클라이언트/서버 소켓 채널 구현 (0) | 2017.07.21 |
4. UDP 서버 프로그램 구현, 브로드캐스트 통신 (2) | 2017.07.20 |
3. 안드로이드 채팅 서버 소켓 프로그램 구현 (0) | 2017.07.20 |