본문 바로가기
코스웨어/11년 내장형하드웨어

[내장형]윤병도_20110722 JAVA(클래스,getter/setter함수),멀티플렉싱(select함수)

by 알 수 없는 사용자 2011. 7. 22.
728x90
반응형

*객체지향프로그래밍

 객체지향 프로그래밍은 과거 코드의 재사용을 통해서 생산성을 향상시킬 수 있다
 객체지향 프로그램은 과거 절차지향 프로그램보다 사람중심의 코드문법이다 (기존 언어를 수용해서 기존 프로그래머를 흡수, 구조적 언어의 문법을 가져옴)
절차지향언어의 구조체의 문제점인 함수의 공통사용문제(함수를 세밀하게 사용 불가능)를 해결하기 위해서 각각의 메소드를 클래스마다  만들어 주는 형식으로 객체의 특성(동작)을 명확하게 드러내는 함수사용가능
아래에는 그 예를 나타낸다

 
위에서 생성자(constructor)함수는 클래스와 이름이 동일해야 한다
java에서는 main함수는 반드시 클래스로 감싸 줘야 한다
eclipse에서 두개의 클래스를 만들고  메인이 포함된 클래스에서 다른 클래스를 사용해서 객체를  생성해보자
먼저 같은 package에서 main을 포함한는 클래스를 만들고 같은 package에서 다음과 같은 Doggy클래스를 만들어서 main을 포함하는 클래스에서 Doggy클래스의 객체를 instance해 보자
다음과 같이 package를 오른쪽 클릭한 후

new -> class선택하여서 Doggy라는 클래스를 만들고 다음과 같이
member variable을 선언한 후 저장

package창에 Doggy클래스밑에 다음과 같이 멤버변수들이 표시된다



저장한 후 DoggyTest class로 가서 다음과 같이 실행하면

 
그 실행결과를 보면

위에서 Doggy 클래스로 톰과 뽀삐라는 객체를 만들었음을 볼 수 있다
위에서는 멤버메소드 가 없어서 구조체와 차이가 없지만  멤버메소드를 넣은 경우의 사용예를 보자
아래에서 멤버메소드를 가지는 고양이를 만들어 보자

실행하면

다음과 같이 멤버메소드 (meaw())가 실행되는 것을 볼 수 있다

*캡슐화
자바는 멤버가 위와같이 데이터속성을 나타내므로 밖으로 그 속을 드러내지않고 숨긴다(encapsulation)
객체지향의 주요 3가지( encapsulation, polymorphism,inheritance)요소중 하나인 캡슐화에 대해서
알아보자
캡슐화는 우리가 프로그램의 상세한 부분까지 알지 않고도 프로그램을 작성할 수있게 해 주는 개념이다
우리는 이미 만들어서 검증받은 클래스를 가져와서 사용하기만 하면 된다 즉 우리는 그 인터페이스만 이해하면
되는 것이다
그런데 객체지향 프로그램은 캡슐화 되어있는 자료에 함부로 접근하지 못하도록 여러가지 지시자를 사용하고 있다
다음과 같은 지시자를 사용해서 클래스내 멤버들의 상속의 한계를 정할 수 있다( 우리가 이때까지 본 구조체의 기본 지시자는 public이다)
  private String name;
  protected int leg;
  public String color
java의 기본 멤버 지시자는 private로  만든다
위의 멤버들을 private로 선언한후 실행하면 다음과 같은 에러가 난다
Exception in thread "main" java.lang.Error: Unresolved compilation problems:
 The field Doggy.name is not visible 
 The field Doggy.name is
not visible

그러면 private로 선언하면서도 에러가 나지않게 하는 방법은 getter,setter함수를 사용하는 것이다
method를 통한 접근은 지시자가 무엇이든지 에러가 나지 않는다
//DoggyTest클래스
//Doggy클래스
위에서 this 는 객체 자신을 가리킨다

그 실행결과를 보면


위에서 getter/setter함수를 사용해서 객체의 멤버에 접근하면 우리가 의도하지 않게 중요 데이터를 변경하는
일을 다음과 같이 막을 수 있다
  void setcolor(String n){
   //보안코드
   // 만약 색을 금지된 색으로 변경시키면 거부
   //로그정보
   //누가 색을 변경시켰는지 로그를 남김
    this.color =n;
  }


위의 방식은 network프로그램상에서 실행시 원격지 주소와 코드를 만든곳의 주소가 다르므로 위와같이 메소드를 이용하면 직접 메모리에 접근하지 않아서 (??) 주소를 맞출 수 있는 방식이기도 하다
(그 원리는 어떤건지 댓글 부탁드립니다)
java로  네트워크상에서 jsp서비스를 할 때 껍데기는javascript로 코어는 javabeans로 만든다

*생성자
생성자를 이용해서 멤버를 초기화하는 방법을 살펴보자
우리가 생성자를 만들지 않으면 컴파일러가 자동으로 만들어 준다
인자의 갯수에 따라서 불리는 생성자가 다름을 보자
//Doggy클래스
위에서 인자가 없는 생성자는 이름을 기본적으로 넣어준다
또 인자가 있는 생성자는 인자의 갯수에 따라서 불리는 생성자가 다르다
//DoggyTest 클래스
그 결과는

각각의 생성자가 인자의 갯수에 따라 불려져서 각각의 객체를 만든것을 볼 수 있다
====================================================================
====================================================================

*멀티플렉싱의 개념
서버가 여러개의 클라이언트와 메시지를 주고 받기 위해서는 앞에서 배운 여러 프로세스를 생성후 각각의 프로세스가 각각의 클라이언트를 담당하는 멀티 프로세싱과 경량의 스레드를 생성후 각각의 스레드가 각각의 클라이언트를 담당하는 멀티 스레딩이 있다.
그러나 멀티테스킹은   프로세스와 스레드가 증가하면 증가할 수록 각각의 프로세스와 스레드간의 스케줄링과 문맥전환등의 추가적인 작업이  오버헤드를 점점 더 증가시킨다
이에  하나의 프로세스가 여러 클라이언트와 통신을 할 수 있게 만든 것이 멀티 플렉싱이다
그러나 이를 위해서는 하나의 프로세스가 다수의 소켓을 핸들링하는 방법이 필요하다
즉  하나의 프로세스가 해당 클라이언트의 여러개의  파일 디스크립터를 묶어서 관리를 하는 것이다
여기서 하나의 프로세서가 여러개의 클라이언트를 연결하는데 문제가 발생함을 알 수 있다
만약 서버가 a,b,c라는 클라이언트와 연결되어서 메시지를 받거나 보내기 위해서 어느 임의의 클라이언트의 연결함수(read,write)에 블로킹 되어 있으면 다른 클라이언트와의 통신은 그 클라이언트와의 블로킹이 해결되기 전까지는
불가능함을 알 수 있다  또 한번 블로킹을 해결해도 다음은 어떤 클라이언트와 통신을 해야 할지 결정해야 하는 문제도 있다
이와 같은 문제점을 해결하기 위해서 임의의 클라이언트와 블로킹되기 전에 어떤 클라이언트가 통신을 하기를 원하
는지 알고 난 다음 그 함수와의 통신을 실행하고 다시 다음 대기 클라이언트를 기다리는 식으로 프로그램이 실행된
다면 위와 같은 문제를 해결 할 수 있다
위에서 어떤 클라이언트가 통신을 하기를 원하는지 알려주는 함수가 바로 select 함수이다

*select함수의 사용법
select함수를 사용하기 위해서는 먼저 서버가 여러 소켓 디스크립터를 묶어서 정보를 담을  수 있는 소켓셋(식별자 리스트)가 3개 필요하다

각각의 소켓셋은 읽기셋, 쓰기셋 ,예외셋이라고 하며  읽기셋은 클라어언트가 서버로 연결요청을 하거나 메시지를 보낸것을 알기 위해서 존재하며, 쓰기셋은 메시지를 받을 준비가 된 클라이언트가 있음을 알기 위해서 존재하며  예외셋은 클라이언트로 부터 긴급상황시 전송되는 OOB(out-of-band)메시지를 수신했는지 알고자 할때  이용된다

소켓셋은  fd_set 구조체로 되어 있으며 내부 변수로는 fd_count와 fd_array가 있다. fd_count는 fd_array에 저장된 소켓의 개수를 나타내며 fd_array는 소켓 디스크립터가 저장되는 배열을 말한다.
fd_array에는 소켓 디스크립터의 번호크기 순서대로 차례로 대입된다

typedef struct fd_set

{

u_int fd_count;

SOCKET fd_array[FD_SETSIZE];

} fd_set;


select( )는 각 소켓셋에 저장된 소켓에 변화가 생길 때까지 기다리고 있다가 소켓이 어떠한 동작을 하면 동작한 소켓을 제외한 나머지 소켓의 비트를  모두 제거한다.  즉 select( )가 실행되고 나서 소켓셋에 남아있는 소켓은 어떠한 동작이 이루어진 소켓이 된다.
아래에서 보면 readset의 내용을 tempset에 저장하는데 그 이유가 바로 동작을 제외한 소켓을 지워버리기 때문이다
실질적으로는 메모리를 절약하기 위해서 각각의 소켓셋은 비트벡터로 표시된다 예를 들면 소켓 디스크립터의 배열이 크기 순으로 2,5,7이고 동작한 소켓이 7이라면 7을 가리키는 비트벡터만 1로 셋 되고 나머지는 0이 된다
비트벡터와 그 값 자체가 and연산된다고 보면 나중에 그 값들을 읽으면 소켓7만 읽히고 나머지 소켓들은 0으로 읽힌다


int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);


select()함수의 인자를 보면 이 중 n은 검사가 필요없는 가장 작은 식별자값으로써 최대 식별자  값보다 1이 작다
위에서 보면 소켓셋에 담을 수 있는 소켓 디스크립터의 최대갯수는 시스템 정의 상수인 FD_SETSIZE로 정의 되어 
있지만 그 수가 상당히 크므로 매번 그 크기만큼 검사하면 비효율적이므로 이를 효율적으로 검사하기 위해서 정수
n을 전달하여 그 크기+1까지만 검사한다
 그 다음에는 각 소켓셋(읽기셋, 쓰기셋, 예외셋)이 파라미터로 들어가며 만약 NULL이 들어가면 그 소켓셋은 대상 리스트에 대한 입/출력 감시를 하지 않는다
 마지막 파라미터인 timeout은 NULL로 설정하면 읽기셋, 쓰기셋, 예외셋에 삽입한 소켓 중에 변화가 생길 때까지 대기하고 있다가 변화가 발생한 소켓의 수를 리턴하게 된다.
timeout을 양수로 설정한 경우에는 변화가 발생한 소켓이 생길 때까지 대기하고 있다가 설정한 시간이 되면 변화가 발생한 소켓이 없더라도 대기상태를 해제하게 된다.
 이때 변화가 발생한 소켓이 없다면 0을 리턴하게 된다. 그리고 timeout 값이 0으로 설정되면 대기시간 없이 바로 리턴하게 된다

우리가 소켓셋에 소켓식별자를 추가하거나 지우는 등의 일을 하기 위해서 사용하는 함수는 아래의 매크로함수들이다


FD_ZERO( )는 소켓셋을 초기화할 때 사용되는 함수이다
void FD_ZERO(fd_set *fdset);
파라미터 fdset은 초기화하고자 하는 소켓셋의 포인터이며 해당 주소에 존재하는 소켓셋에 삽입되어 있던 소켓을 모두 삭제하는 기능을 한다

FD_SET( )는 소켓을 소켓셋에 삽입하고자 할 때 사용되는 함수이다
void FD_SET(int fd, fd_set *fdset);
파라미터 fd는 소켓셋에 집어넣을 소켓이고, fdset은 소켓을 삽입할 소켓셋의 주소이다.

FD_ISSET( )는 소켓이 소켓셋에 존재하는 지 알고자 할 때 사용되는 함수이다
int FD_ISSET(int fd, fd_set *fdset);
소켓 fd가 소켓셋 fdset에 현재 존재하는지 알려주는 기능을 한다. 만약 소켓 fd가 소켓셋 fdset에 존재하면 0이 아닌 소켓값을 리턴하게 되고 소켓이 해당 소켓셋에 존재하지 않으면 0을 리턴하게 된다.

FD_CLR( )은 소켓셋 fdset에서 소켓 fd를 삭제하고자 할 때 사용되는 함수이다
void FD_CLR(int fd, fd_set *fdset);
fd 는 소켓셋에 삽입할 소켓 파일 디스크립터이고 fdset는 소켓셋의 주소이다

select( )를 이용해서 여러 클라이언트와 메시지를 주고받기 위해서는 다음과 같은 절차를 반복하여 실행해야 한다. 첫 번째로 FD_ZERO()를 이용해 읽기셋, 쓰기셋, 예외셋 각각의 셋을 비워준다.
두 번째로 각각의 소켓셋에 소켓을 삽입한다.
여기서 만약 클라이언트로부터 메시지가 전송되었는지를 알고 싶은 소켓이 있다면 읽기 셋에 소켓을 삽입하고 메시지를 보낼 수 있는 준비가 되어있는 지 알고 싶은 소켓이 있다면 쓰기셋에 소켓을 삽입한다.
긴급한 메시지인 OOB 메시지가 전송되었는지를 알고 싶은 소켓이 있다면 예외셋에 소켓을 삽입한다.
세 번째로 select()를 호출해서 소켓에 변화가 생길 때까지 대기상태에 있다가 소켓에 변화가 생기면 변화가 발생한 소켓만 제외하고 나머지 소켓을 소켓셋에서 삭제한 후 변화가 발생한 소켓의 수를 리턴한다.
 마지막 네 번째로 각각의 소켓셋에 남아있는 소켓을 알아내서 적절한 함수를 호출하게 되며 이후에는 다시 첫 번째 절차로 반복 실행된다.

그러면 select()함수를 사용해서 멀티플렉싱 통신을 실현하는 과정을 살펴보자 (일단 읽기만 가능한 서버)

일단 서버에서 먼저 socket()를 통해 서버소켓을 생성한다. 이후 bind()를 통해 서버 소켓에 주소 정보를 부여하고 listen()을 통해 클라이언트가 접속하기를 기다린다.
 다음 소켓셋 중 읽기셋인 readset을 생성하여 클라이언트가 접속했는지 혹은 클라이언트가 메시지를 전송했는지를 감지할 수 있도록 한다.
또한, 임시 소켓셋인 tempset을 별도로 생성하여 변화가 생긴 소켓 이외에 나머지 모든 소켓을 tempset에 저장할 수 있도록 한다.
 이는 select()에서 변화가 생긴 소켓이 존재하면 이외에 나머지 모든 소켓은 삭제되기 때문이다.
 tempset에 서버소켓을 삽입한 후 다시 readset에 tempset을 삽입한 다음 select()를 호출하여 소켓에 변화가 생길 때까지 대기하고 있는다.
 이후 읽기셋에 변화가 생겼을 시 변화가 생긴 소켓을 찾아내기 위해 FD_ISSET()를 호출한다.
이때 변화가 생긴 소켓이 서버소켓일 경우에는 클라이언트에서 서버로 접속한 경우이기 때문에 클라이언트의 접속을 허락하고 클라이언트와 메시지를 주고받는 클라이언트 소켓을 기존에 전체 소켓이 저장되어 있던 tempset에 저장한다.
또한, 변화가 생긴 소켓이 클라이언트 소켓일 경우에는 클라이언트가 메시지를 전송한 것이기 때문에 클라이언트가 전송한 메시지를 read()로 읽어 들이게 된다  







728x90