01. TCP/IP 프로토콜 스택을 4개의 계층으로 구분해 보자. 그리고 TCP소켓이 거치는 계층구조와 UDP소켓이 거치는 계층구조의 차이점을 설명해보자.

-> 아래 그림을 참조한다.

1.  TCP 프로토콜 스택


2. UDP프로토콜 스택


02. TCP/IP 프로토콜 스택 중에서 LINK계층과 IP계층이 담당하는 역할이 무엇인지 설명해보자. 그리고 이 둘의 관계도 함께 설명해보자.

-> LINK 계층은 물리적인 영역의 표준화를 담당하고 있는 계층으로 LAN, WAN, MAN과 같은 네트워크 표준과 관련된 프로토콜을 정의하는 영역이다.

IP계층은 LINK계층에서 물리적 연결이 형성되었으니 목적지로 데이터를 전송하기 위해 어떤 경로를 선택할지를 해결하는 계층이다.


03. TCP/IP 프로토콜 스택을 4개의 계층(또는 7개의 계층)으로 나누는 이유는 무엇인가? 이를 개방형 시스템에 대한 설명과 함께 답해보자.

-> 표준화 작업을 통해 많은 사람들이 이 표준을 따르도록 하고 이런 표준을 근거로 설계된 시스템을 개방형 시스템이라고한다. 결과적으로 빠른속도의 기술발전이 가능하다.


04. 클라이언트는 connect함수호출을 통해서 서버로의 연결을 요청한다. 그렇다면 클라이언트는 서버가 어떠한 함수를 호출한 이후부터 connect함수를 호출할 수 있는가?

-> lisen함수


05. 연결요청 대기 큐라는 것이 생성되는 순간은 언제이며 이것이 어떠한 역할을 하는지 설명해보자. 그리고 accept함수와의 관계도 함께 설명해보자.

-> listen 함수가 호출되는 시점이다. 연결요청 대기 큐는 클라이언트의 연결요청 정보를 저장하는 대기공간으로, accept함수가 호출되면 이 공간에 있는 연결요청 정보를 참조하여 클라이언트와 연결한다.

 

06. 클라이언트 프로그램에서 소켓에 주소정보를 할당하는 bind 함수호출이 불필요한 이유는 무엇인가? 그리고 bind 함수를 호출하지 않았을 경우, 언제 어떠한 방식으로 IP주소와 PORT번호가 할당되는가?

-> 연결을 요청할 서버의 주소정보가 중요하기 때문에 bind함수로 주소정보를 할당할 필요는 없는데 서버와 연결하려면 소켓에 주소정보를 할당해야한다.

만약 bind함수를 호출 하지 않았을 때 connect함수호출 시 소켓에 자동으로 IP와 포트번호가 할당된다.


07. Chapter 01에서 구현한 예제 hello_server.c와 hello_server_win.c를 iterative 모델로 변경하고, 제대로 변경되었는지 클라이언트와 함께 테스트해보자.

-> 리눅스 기반 서버와 클라이언트

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
#include <stdio.h>
#include <stdlib.h>
#include <sting.h>
#include <unistd.h>
#include <srpa/inet.h>
#include <sys/socket.h>
void error_handling(char *message);
int main(int argc, char *argv[])
{
        int serv_sock;
        int clnt_sock;
        struct sockaddr_in serv_addr;
        struct sockaddr_in clnt_addr;
        socklen_t clnt_addr_size;
        char message[] = "Hello World!";
        if (argc != 2)
        {
               printf("Usage:%s <port>\n", argv[0]);
 
               exit(1);
        }
 
        serv_sock = socket(PF_INET, SOCK_STREAM, 0);
        if (serv_sock == -1)
               error_handling("socket() error");
        memset(&serv_addr, 0sizeof(serv_addr));
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        serv_addr.sin_port = htons(atoi(argv[1]));
        if (bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1
               error_handling("bind() error");
        if (listen(serv_sock, 5== -1
               error_handling("listen() error");
 
        clnt_addr_size = sizeof(clnt_addr);
        while (1
        {
               clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);                                                                                                           
               if (clnt_sock == -1)
                       error_handling("accept() error");
               write(clnt_sock, message, sizeof(message)); 
               close(clnt_sock);
        }
        close(serv_sock);
        return 0;
}
void error_handling(char *message)
{
 
        fputs(message, stderr);
        fputc('\n', stderr);
        exit(1);
 
}
cs


'열혈 TCP IP 소켓 프로그래밍' 카테고리의 다른 글

Chapter03 내용 확인 문제  (0) 2020.06.20
Chapter02 내용 확인문제  (0) 2020.06.19
Chapter01 내용 확인문제  (0) 2020.06.19


01. IP주소 체계인 IPv4와 IPv6의 차이점은 무엇인가? 그리고 IPv6의 등장 배경은?

-> IPv4는 4바이트 주소체계, IPv6는 16바이트 주소체계이다. 현재 범용적으로 사용되고있는 주소체계는 IPv4로, IPv6는 IPv4기반의 IP주소가 모두 고갈될 것을 염려하여 만들어진 표준인데, 아직 범용화되지않았다. 



02. 회사의 로컬 네트워크에 연결되어 있는 개인 컴퓨터에 데이터가 전송되는 과정을, IPv4의 네트워크 ID와 호스트ID, 그리고 라우터의 관계를 기준으로 설명하라.

-> 네트워크 ID를 이용해 해당 네트워크(라우터 or 스위치)로 데이터가 전송되고 호스트 ID를 이용해 호스트에게 데이터를 전송한다.


03. 소켓의 주소는 IP와 PORT번호 두 가지로 구성된다. 그렇다면 IP가 필요한 이유는 무엇이고 PORT번호가 필요한 이유는 또 무엇인가? 

즉, IP를 통해서 구분되는 대상은 무엇이고 PORT번호를 통해 구분되는 대상은 무엇인가?

-> IP를 이용해 호스트로 데이터가 전송되고 호스트 내의 어떤 소켓에 전송할 지 는 포트번호를 이용해 운영체제가 데이터를 적절히 분배한다.

즉, IP는 호스트를 구분하고 포트번호는 호스트 내의 소켓을 구분하는데 사용된다.


04. IP주소의 클래스를 결정하는 방법을 설명하고 이를 근거로 다음IP주소들이 속하는 클래스를 판단해보자.

-> 네트워크ID(네트워크의 구분을 위한 IP주소의 일부)와 호스트ID의 형태에 따라 분류된다. IP주소의 첫 번 째 바이트만 확인하여 네트워크 주소가 몇바이트인지 판단 가능하다. 

클래스 A의 첫 번째 바이트 범위: 0~127

클래스 B의 첫 번째 바이트 범위: 128~191

클래스 C의 첫 번째 바이트 범위: 192~223




a. 214.121.212.101 

-> 클래스C

b. 120.101.122.89

-> 클래스 A

c. 129.78.102.211

-> 클래스 B


05. 컴퓨터는 라우터 또는 스위치라 불리는 물리적인 장치를 통해 인터넷과 연결된다. 그렇다면 라우터 또는 스위치의 역할은 무엇인가?

-> 목적지로 데이터를 전달하는데 중간 매개체 역할을 한다. ( 데이터 전송 -> 라우터 -> 호스트)


06. Well-known PORT는 무엇이며 그 값의 범위는 어떻게 되는가? 

-> 포트 번호는 2바이트로 표현되기 때문에 0~65535까지 사용할 수 있다. 그런데 well-known PORT는 0부터 1023까지의 특정 프로그램에 할당하기로 예약되어있는 포트번호다. 따라서 이 범위의 값을 제외한 다른 값을 할당해야한다.


07. 소켓에 주소를 할당하는 bind함수의 두번 째 인자는 sockaddr*형 변수인데 실제로 함수를 호출할 때는 sockaddr_in의 변수를 사용한다.(sockaddr*형으로 형 변환해 사용) 그 이유는 무엇인가?

-> sockaddr 구조체는 IP번호와 PORT번호를 할당하기 쉽지 않은 구조다. 그렇기 때문에 IP주소와 PORT번호 할당이 용이한 sockaddr_in 구조체가 사용된다. 형변환해서 사용하는 이유는 sockaddr_in구조체를 이용하여 IP주소와 PORT번호를 할당하면 이 구조체 변수의 바이트 열은 sockaddr과 동일하므로 

주소 정보를 할당하기 편한 sockaddr_in을 이용한다. bind 함수의 동작은 sockaddr형 구조체를 이용하므로 sockaddr_in구조체를 이용하여 주소정보를 할당 한 후 sockaddr형으로 형변환하여 사용한다.



'열혈 TCP IP 소켓 프로그래밍' 카테고리의 다른 글

Chapter04 내용 확인문제  (0) 2020.06.20
Chapter02 내용 확인문제  (0) 2020.06.19
Chapter01 내용 확인문제  (0) 2020.06.19


01. 프로토콜이란 무엇을 의미하는가? 그리고 데이터의 송수신에 있어서 프로토콜을 정의한다는 것은 어떠한 의미가 있는가?

-> 컴퓨터 상호간의 대화에 필요한 통신 규약을 의미한다. 데이터의 송수신에 있어서 프로토콜을 정의하는 것은 데이터를 주고받기 위해 정의해 놓은 약속을 의미한다.


02. 연결지향형 소켓인 TCP소켓의 전송 특성 세가지를 나열하라.

-> 전송 중간에 데이터가 소멸되지않고 목적지로 전송됨, 전송 데이터가 순서대로 전송됨, 전송 데이터의  경계가 없음(read함수 횟수와 write함수 횟수가 다름)


03. 다음 중 비 연결지향형 소켓의 특성에 해당하는 것을 모두 고르면?

->a, c, e

a. 전송된 데이터는 손실될 수 있다.

b. 데이터의 경계가 존재하지 않는다.

c. 가장 빠른 전송을 목표로한다.

d. 한번에 전송할 수 있는 데이터의 크기가 제한되어 있지 않다.

e. 연결지향형 소켓과 달리 연결이라는 개념이 존재하지 않는다.


04. 다음 유형의 데이터 송수신에 적합한 타입의 소켓은 무엇인지 결정하고, 그러한 결정을 하게 된 이유를 설명해보자.

a. 서태지와 아이들의 실시간 라이브 방송 멀티미디어 데이터

-> UDP: 데이터 손실의 중요성이 떨어지고 전송속도가 중요함

b. 철수가 압축한 텍스트 파일의 전송

-> TCP: 압축파일은 데이터가 손상되면 안됨

c. 인터넷 뱅킹을 이용하는 고객과 은행 사이에서의 데이터 송수신

-> 은행거래는 1:1로 해야하고 데이터가 손실되면 안됨


05. 데이터의 경계가 존재하지 않는 소켓은 어떠한 타입의 소켓인가? 그리고 이러한 소켓은 데이터를 수신할 때 무엇을 주의해야하는가?

-> TCP, 전송되는 데이터의 경계가 존재하지않기 때문에 전송 데이터와 수신 데이터의 양이 같아야한다. 


'열혈 TCP IP 소켓 프로그래밍' 카테고리의 다른 글

Chapter04 내용 확인문제  (0) 2020.06.20
Chapter03 내용 확인 문제  (0) 2020.06.20
Chapter01 내용 확인문제  (0) 2020.06.19


01. 네트워크 프로그래밍에서 소켓이 담당하는 역할이 무엇인가? 그리고 소켓이라는 이름이 붙은 이유는 어디에 있는가?

-> 물리적으로 연결되어있는 네트워크 상에서 데이터 송수신에 사용할 수 있는 소프트웨어적인 장치역할을 하고, 멀리 떨어져있는 컴퓨터와 데이터를  송수신하려면 인터넷 망에 연결해야하는데 프로그래밍에서 소켓은  네트워크 망에 연결에 사용되는 도구로 사용되므로 '연결'이라는 의미가 담겨있어서 소켓이라는 표현을 사용한다. 

* 물리적 연결: 직접 연결과 인터넷(네트워크) 연결



02. 서버프로그램에서는 소켓생성 이후에 listen함수와 accept함수를 차례대로 호출한다. 그렇다면 이들의 역할은 각각 무엇인지 비교해서 설명해보자.

-> listen함수 호출 시 클라이언트가 서버에 연결을 요청 할 수 있는 상태가 되고 이어서 accept함수 호출 시 서버가 클라이언트의 연결 요청을 받아들이며 서버와 클라이언트의 연결이 완료된다.

 

03. 리눅스의 경우 파일 입출력 함수를 소켓 기반의 데이터 입출력에 사용할 수 있다. 반면 윈도우에서는 이것이 불가능하다. 그렇다면 리눅스에서는 가능하고, 윈도우에서는 불가능한 이유가 무엇인가?

-> 리눅스는 파일과 소켓이 같다. 즉, 소켓도 파일로 간주하기 때문에 파일 입출력 함수를 소켓 기반의 데이터 입출력에 사용할 수 있다.

윈도우는 소켓과 파일이 구분되기 때문에 파일입출력과 소켓입출력이 구분된다.


04. 소켓을 생성한 다음에는 주소할당의 과정을 거친다. 그렇다면 주소할당이 필요한 이유는 무엇이며, 이를 목적으로 호출하는 함수는 또 무엇인가?

-> IP주소와 포트넘버를 할당하는데, IP는 컴퓨터를 구분하는 목적으로 사용되고 포트넘버는 컴퓨터로 전송된 데이터를 소켓에 전달해주는데 사용된다.

즉 IP주소는 컴퓨터를 구분하고 포트넘버는 소켓을 구분하기 위해 사용되고 이 정보를 이용해 데이터를 주고 받기 때문에 생성한 소켓에  bind함수로 주소를 할당하는 과정을 거친다.


05. 리눅스의 파일 디스크립터와 윈도우의 핸들이 의미하는 바는 사실상 같다. 그렇다면 이들이 의미하는 바가 무엇인지 소켓을 대상으로 설명해보자.

-> 리눅스의 파일 디스크립터와 윈도우의 핸들은 소켓을 구분하고 지정하는 목적으로 소켓에 부여된 정수값이다.


06. 저 수준 파일 입출력과 ANSI표준에서 정의하는 파일 입출력 함수는 어떠한 차이가 있는가?

-> 저 수준 파일 입출력 함수는 운영체제 별로 정의하는 형태가 다르고, ANSI표준에서 정의하는 파일 입출력은 C언어 기반의 표준 라이브러리 함수이므로  모든 운영체제에서 사용가능하다.


07. 저수준 파일 입출력 함수와 ANSI표준 입출력함수를 기반으로 파일 복사 프로그램을 작성하시오.


-> 저수준 파일 입출력

2행은 open 함수를 위한 헤더, 3행은 read, write함수를 위한 헤더다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#define BUF_SIZE 100
int main(int argc, int argv[])
{
        int file1, file2;
        char buf[BUF_SIZE];
        int read_cnt;
        file1 = open("file1.txt", O_RDONLY); 
        file2 = open("file2.txt", O_CREAT | O_WRONLY | O_TRUNC);
        if (file1 == -1 || file2 == -1)
        {
               puts("open() error!!");
               return 0;
        }
        while ((read_cnt = read(file1, buf, BUF_SIZE)) != 0)
               write(file2, buf, read_cnt); 
        close(file1);
        close(file2);
        return 0;
}
 
cs


-> ANSI표준 파일 입출력
자주 쓰이는 형태이므로 익혀둘 필요가 있음
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
#include <stdio.h>
#define BUF_SIZE 100
int main(void)
{
        char buf[BUF_SIZE];
        int read_cnt;
        FILE *file1 = fopen("file1.txt""rb"); 
        FILE *file2 = fopen("file2.txt""wb"); 
        if (file1 == NULL || file2 == NULL;
 
        {
               puts("fopen() error");
               return 0;
        }
        while (1)
        {
               read_cnt = fread((void*)buf, 1, BUF_SIZE, file1); /
               if (read_cnt < BUF_SIZE)  //읽어온 데이터는 버퍼 사이즈를 넘을 수 없음
               {
                       if (feof(file1) != 0//파일의 끝에 도달= 0이아닌 값
                       {
                              fwrite((void*)buf, 1, read_cnt, file2);
                              break;
                       }
                       else
                              puts("복사 오류");
                       break;
               }
        }
        fclose(file1);
        fclose(file2);
        return 0;
}
cs


'열혈 TCP IP 소켓 프로그래밍' 카테고리의 다른 글

Chapter04 내용 확인문제  (0) 2020.06.20
Chapter03 내용 확인 문제  (0) 2020.06.20
Chapter02 내용 확인문제  (0) 2020.06.19


묵시적 인텐트란 약속된 액션을 지정하고 액션에 필요한 데이터 값을 설정하여 안드로이드에서 제공하는 기존 응용 프로그램을 실행할 수 있는

인텐트를 말합니다.

묵시적 인텐트를 활용하여 다음 6가지의 기능을 구현해보겠습니다.


1. 지정된 번호로 전화걸기 (010-1234-5678)

2. 특정 사이트 접속 하기 (www.naver.com)

3. 구글맵을 열어서 지정 좌표의 위치 확인하기 (한빛 아카데미 건물)

4. 구글홈페이지에서 원하는 검색어 입력하기 ("안드로이드 ㅋㅋ")

5. 지정된 번호로 원하는 메시지 보내기("안녕하세요.")

6. 카메라 어플 들어가기 


우선, 전화 걸기 및, 구글 맵 사용을 위해 Manifest 폴더에 사용 권한을 추가해야합니다.

<appliciation 전에 다음 3가지 문구를 작성합니다.

<uses-permission android:name="android.permission.CALL_PHONE"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<application

그리고 activity_main.xml에서 버튼을 설정해주고 MainActivity.java에서 아래처럼 코딩합니다.

ACTION_DIAL, ACTION_VIEW, ACTION_SENDTO, ACTION_IMAGE_CAPTURE, ACTION_WEB_SEARCH 등 

각각의 기능을 수행하기 위한 인텐트 액션 모드가 정해져있기 때문에 이를 이용하여 동작을 실행할 수 있습니다.

package com.example.a0610;

import androidx.appcompat.app.AppCompatActivity;

import android.app.SearchManager;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnDial=findViewById(R.id.btnDial);
Button btnWeb=findViewById(R.id.btnWeb);
Button btnGoogle=findViewById(R.id.btnGoogle);
Button btnSearch=findViewById(R.id.btnSearch);
Button btnSms=findViewById(R.id.btnSms);
Button btnPhoto=findViewById(R.id.btnPhoto);


btnDial.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Uri uri=Uri.parse("tel:010-1234-5678");
Intent intent= new Intent(Intent.ACTION_DIAL,uri);
startActivity(intent);//이걸 실행해달라

}
});
btnWeb.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Uri uri=Uri.parse("http://www.naver.com");
Intent intent= new Intent(Intent.ACTION_VIEW,uri);
startActivity(intent);//이걸 실행해달라

}
});
btnGoogle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Uri uri=Uri.parse("http://maps.google.com/maps?q="+37.559133+","+126.927824);
Intent intent= new Intent(Intent.ACTION_VIEW,uri);
startActivity(intent);//이걸 실행해달라

}
});
btnSearch.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent= new Intent(Intent.ACTION_WEB_SEARCH);
intent.putExtra(SearchManager.QUERY,"안드로이드 ㅋㅋ");
startActivity(intent);//이걸 실행해달라

}
});
btnSms.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent= new Intent(Intent.ACTION_SENDTO);
intent.putExtra("sms_body","안녕하세요.");
intent.setData(Uri.parse("smsto:"+Uri.encode("010-1234-5678")));
startActivity(intent);//이걸 실행해달라

}
});
btnPhoto.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent= new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivity(intent);//이걸 실행해달라

}
});
}
}


<실행 결과>

1. 시작 화면


2. 각 버튼의 실행 화면






소켓프로그래밍을 공부하면서 낯설었던 용어들이나 기능들의 의미를 몇 개 적어봤습니다.

(생각 날 때 마다 새로 공부한 내용을 추가하고있습니다. *오늘 6/9일)



1.  TCP/IP 프로토콜에서 소켓을 생성하는 함수는?

=> socket, accept 함수 

socket함수는 서버로 사용할 소켓을 생성하고, accept는 클라이언트와 연결하기 위해 사용할 소켓을 생성합니다.


2. UDP프로토콜에서 소켓을 생성하는 함수는?

=> socket함수

UDP는 연결 요청을 하거나 받는 listen, connect, accept함수를 사용하지 않습니다. 


3. INADDY_ANY의 의미

서버 프로그램 코드 작성 시 주소를 설정 할 때 입력받지 않고 INADDR_ANY를 많이 사용했습니다.

INADDR_ANY의 의미는 "서버프로그램에서 입력된 포트번호를 목적지로 하는 모든 연결요청을 서버가 처리하겠다" 라는 의미로 보면 됩니다.

그렇기 때문에 클라이언트에서는 쓸 필요가 없고 주로 서버에서 사용합니다.


4. main함수의 argc, argv[]의 의미

각각 다음을 의미합니다.

argc= 전달받은 인자 갯수

argv[]= 전달받은 문자열

argv[0] 실행파일명


우분투, 리눅스 등의 버클리소켓 환경에서는 argv[0]출력시 "./실행파일명" 으로 출력되고 윈속의 경우 "실행파일명"으로 출력됩니다.

윈속의 경우 별다른 설정을 하지않으면 프로젝트 이름이 출력됩니다.

서버에서 입력으로 127.0.0.1 9100 입력 시  argc는3, argv[1]="127.0.0.1", argv[2]="9100" 을 의미합니다.

서버에서 주소를 INADDR_ANY로 설정 후 포트번호만 입력했다면 argc는 2, argv[1]="9100"을 의미합니다.

argv의 타입이 char*형 배열이므로 입력한 ip주소와 포트번호는 모두 문자열이라 모두 별도의 변환 함수를 이용하여 숫자로 처리해야합니다.


5. 버클리소켓환경에서 컴파일 및 실행 하는 방법

https://jow1025.tistory.com/192


6. 윈속에서 서버-클라이언트 프로그램 실행하는 방법

1단계: 소스코드 작성 후 컴파일

2단계: 컴파일된 실행파일이 저장된 위치 확인

3단계: cmd창 열기-> 실행파일 위치로 위치 이동(cd명령어 사용)-> 실행파일명, ip주소, 포트번호 순으로 입력(띄어쓰기)

4단계: 한 프로젝트에서 서버와 클라이언트 실행파일 둘 다 만들어서 실행 할 때 

-> 서버코드 실행파일 보관 후 클라이언트 코드를 컴파일 후 실행파일명 변경 후 3단계 실행

5단계: cmd창 두 개 띄워서 각각의 서버, 클라이언트 프로그램 실행











소켓프로그래밍은 연결 프로토콜만 같으면 OS, 단말기 상관없이 연결이 가능하다고 배웠습니다.

책에서 각 챕터 소스코드를 리눅스 버클리 소켓환경과 윈속으로 나누어 설명하고있는데 두 OS끼리 연동해 보려고 공부하던 중 꼭 알아야할

중요한 IP설정 사항이 있길래 작성해봤습니다.


<한 컴퓨터로 가상 머신 속 OS와 윈도우 환경의 소켓(자바, 윈속, 파이썬 등) 연동 시 IP주소 확인하는 법>

버클리 소켓을 이용하는 경우라면 아마도 윈도우 환경에서 가상 머신을 이용해 우분투, 리눅스 등을 깔아서 사용하는 경우일 겁니다.

만약 서버와 클라이언트를 각각 다른 소켓 환경을 이용해 연동하는 작업을 할 때  IP주소를 어떻게 사용할까요?

간단한 서버-클라이언트 프로그램 코드에서 클라이언트에서 루프백주소(127.0.0.1)과 포트번호를 입력하면 서버와 클라이언트가 연결되지 않습니다. 

왜 일까요? 127.0.0.1은 루프백 주소, 즉 사용중인 컴퓨터의 IP주소를 의미하는데, 가상 머신도 컴퓨터와 별개로 IP주소가 존재합니다. 

가상 머신속 IP와 실제 컴퓨터 호스트의 IP주소는 다릅니다. 즉, 가상 머신을 하나의 새로운 컴퓨터로 보시는게 좋습니다.

그렇기 때문에 이렇게 다른 OS를 이용하는 소켓 인터페이스의 연동 시  IP주소를 직접 입력해야합니다.


가상 머신(리눅스, 유닉스, 우분투 를 사용한다는 가정)에서는 쉘 명령어로 IP주소를 확인 할 수 있습니다.

1. hostname -i(소문자) : 루프백 주소( 가상 머신 속 OS의 로컬 ip주소)=> 127.0.1.1

2. hostname -I(대문자) : 가상머신의 로컬 IP주소


로컬 주소는 실제 컴퓨터 IP와 다르기 때문에 서로다른 OS의 소켓인터페이스 연동 시 2번 주소를 이용해야합니다.

윈도우 환경에서 IP주소 확인은 cmd에서 ipconfig명령어로 쉽게 확인 할 수 있고 네이버로 치면 알 수 있습니다.

하지만, 실제 소켓 통신 때 사용할 윈도우 IP는 ipconfig를 입력해서 나온 IP중 아래 두 가지 만 사용가능합니다.


1. "이더넷 어뎁터 vmnet8"의 주소

2. 무선 lan어뎁터 와이파이 주소


그 외의 ip나 네이버의 내 ip주소 확인 검색 등의 결과로 나온 ip주소를 사용할 수 없습니다.


이더넷 어뎁터 vmnet8의 주소는 가상 머신속 환경과 연결 할 수 있는 IP이고, 무선 lan 어뎁터 와이파이 주소는 가상머신과 사용자 컴퓨터가 같은 와이파이에 연결되어있을 경우 사용가능합니다. 


따라서, 서버-클라이언트 프로그램의 코드 작성 시 경우에 따라 IP주소를 아래와 같이 사용해야 합니다.

예)가상머신의 OS로 우분투를 사용한다는 가정


1. 서버: 버클리소켓, 클라이언트: 윈속 일 때

코드의 입력 IP: 가상 머신 속 ip주소


2. 서버: 윈속, 클라이언트: 버클리소켓 일 때

코드의 입력 IP: 사용자 컴퓨터 IP주소



Android Studio를 활용한 안드로이드 프로그래밍 P382 직접 풀어보기 9-3

*문제에서는 블러링, 엠보싱기능을 추가했지만 저는 실습 문제 그대로 구현했습니다.


아래의 그림처럼 간단한 포토샵 프로그램을 만들어라.

1. 확대, 축소 기능

2. 회전 기능

3. 화면 밝기 증가, 감소

4. 화면 어둡게/밝게 하기


*실행 결과는 일일이 보여드리기가 힘들어서 생략하겠습니다. 직접 돌려보시면 좋을 것 같아여



<activity_main_XML 코드>

메뉴를 담을 레이아웃과 사진을 담을 레이아웃을 분리할 때 각 레이아웃의 폭,높이 조정에 주의하셔야 합니다.

<LinearLayout 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"
android:orientation="vertical">

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:id="@+id/iconLayout"
android:layout_weight="1">
<ImageButton
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/zoomin"
android:src="@drawable/icon_zoomin"/>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/zoomout"
android:src="@drawable/icon_zoomout"/>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/rotate"
android:src="@drawable/icon_rotate"/>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/zoombright"
android:src="@drawable/icon_bright"/>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/zoomdark"
android:src="@drawable/icon_dark"/>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/zoomgray"
android:src="@drawable/icon_ge"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="9"
android:gravity="center"
android:id="@+id/pictureLayout"
android:orientation="vertical"/>
</LinearLayout>


<MainActivity>

1. clickIcons함수를 정의하고 main문에서 호출할 수 있도록 했습니다. 메뉴 클릭 시 각 이벤트를 처리합니다.

2. 맨 마지막 버튼(화면 회색 전환)의 경우 satur값이 1이면 0으로 바꿔 회색화면으로 만들어주고 0일 시 다시 1로 바꿔줍니다.

=> 일종의 토글버튼(on/off)와 비슷합니다. 최초에 버튼 클릭시 회색으로전환, 또 한번 클릭 시 밝게 전환

아래 코드를 정확한 위치에 삽입해야합니다. 다른 위치에 코드 삽입 시 사진이 회색으로 전환되지 않습니다.

if(satur==0)cm.setSaturation(satur);// 위치 여기다가!!


package com.example.p374;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.view.View;
import android.widget.ImageButton;
import android.widget.LinearLayout;

public class MainActivity extends AppCompatActivity {
ImageButton zoomin,zoomout,zoombright,zoomdark,zoomrotate,zoomgray;
MyGraphicView graphicView;
static float scaleX=1,scaleY=1,angle=0,color=1,satur=1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setTitle("미니 포토샵");

LinearLayout pictureLayout=(LinearLayout)findViewById(R.id.pictureLayout);
graphicView=(MyGraphicView)new MyGraphicView(this);
pictureLayout.addView(graphicView);
clickIcons();
}

public void clickIcons() {
zoomin=(ImageButton)findViewById(R.id.zoomin);
zoomin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
scaleX=scaleX+0.2f;
scaleY=scaleY+0.2f;
graphicView.invalidate();
}
});
zoomout=(ImageButton)findViewById(R.id.zoomout);
zoomout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
scaleX=scaleX-0.2f;
scaleY=scaleY-0.2f;
graphicView.invalidate();
}
});
zoomrotate=(ImageButton)findViewById(R.id.rotate);
zoomrotate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
angle=angle+20;
graphicView.invalidate();
}
});
zoombright=(ImageButton)findViewById(R.id.zoombright);
zoombright.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
color=color+0.2f;
graphicView.invalidate();
}
});
zoomdark=(ImageButton)findViewById(R.id.zoomdark);
zoomdark.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
color=color-0.2f;
graphicView.invalidate();
}
});
zoomgray=(ImageButton)findViewById(R.id.zoomgray);
zoomgray.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(satur==0)satur=1;
else satur=0;
graphicView.invalidate();
}
});
}


private class MyGraphicView extends View {
public MyGraphicView(Context context) {
super(context);
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint=new Paint();
float[] array={color,0,0,0,0,
0,color,0,0,0,
0,0,color,0,0,
0,0,0,1,0};
ColorMatrix cm=new ColorMatrix(array);
if(satur==0)cm.setSaturation(satur);// 위치 여기다가!!
paint.setColorFilter(new ColorMatrixColorFilter(cm));

Bitmap picture= BitmapFactory.decodeResource(getResources(),R.drawable.img01);
int picX=(this.getWidth()-picture.getWidth())/2;
int picY=(this.getHeight()-picture.getHeight())/2;
int cenX=this.getWidth()/2;
int cenY=this.getHeight()/2;
canvas.scale(scaleX,scaleY,cenX,cenY);
canvas.rotate(angle,cenX,cenY);
canvas.drawBitmap(picture,picX,picY,paint);
picture.recycle();
}
}
}





'Android Studio를 활용한 안드로이드 프로그래밍' 카테고리의 다른 글

직접 풀어보기 9-2  (0) 2020.06.08
직접 풀어보기 9-1  (0) 2020.06.07
직접 풀어보기 8-2  (15) 2020.05.20
직접 풀어보기 8-1  (0) 2020.05.20
직접 풀어보기 7-3  (0) 2020.05.19

+ Recent posts