http://blog.naver.com/PostView.nhn?blogId=specialcase&logNo=80092963677

by givingsheart 2014. 3. 10. 20:28

셀렉터라는 녀석을.. 말 그대로 이벤트 감지 + 발송자라고 한다면,


내가 원하는 작업의 분리는
(서버)
1.클라가 보낸 어셉처리
2.클라가 보낸 리드처리
3.클라에게 브로드캐스트처리

=> 1,2를 별개의 셀렉터 객체로 생성한다.(독립 스레드) 어셉처리 코드에서 새로 만든 클라와 통신용 채널을 1에 등록하지 말고
     2에 등록한다. 그럼으로 1은 어셉만 처리, 2는 리드만 처리가 가능하다,.
     
     3에 대해서.. 비동기 처리를 하고 싶지만, 아까도 고민햇듯이.. 2번 스레드 작업의 완료가 마쳐져야 비로소 3번 작업이
     시작될수 있다.(전제조건) 그럼으로 안타깝지만.. 메서드 단위로 빼야겟다.
(클라)
1.서버가 보낸 리드처리 -> 1.1 콘솔등 view단에 전달
2.사용자의 쓰기처리 -> 2.1 서버 전송 (이때 사용하는 버퍼를 다이렉트 버퍼로 해서 바로 디바이스에 쓴다면? 흠..)

 => 1은 당연히 담당 셀렉터를 생성하고.. 처리하면 되고.. 2는 셀렉터가 표준 입력(키보드)의 엔터 이벤트를 감지할
     수 있는가가 관건.. 이게 가능할지는 모르겠음... 안되면 별개 스레드로 처리하면 되고.. implements 런에이블


'개인 프로젝트 관련 고민들 > nio채팅' 카테고리의 다른 글

nio 개념잡기  (0) 2014.03.10
첩.첩.산.중  (0) 2014.01.01
클래스 설계 조각  (0) 2014.01.01
nio 참고 자료 + 프로젝트 깨달음  (0) 2014.01.01
판단 오류2개 + 고민 3가지  (0) 2014.01.01
by givingsheart 2014. 1. 1. 16:25

조금 더 설계에 대해 고민하다가.. 완전 말림..


//서버단
1.하나의 스레드가 하나의 셀렉터(set자료구조)를 관리하게한다.
  1.1 하나의 셀렉터는 최대 63개의 클라와 통신채널, 1개의 서버 어셉용채널을 커버 (숫자가 맞는진 모르겠다)
  1.2 셀렉터와 어셉용 서버채널, 클라의 전송을 read할 채널, 연결된 클라들에 write할 채널  각각의 독립적인
       작업을 분리한후, 하나의 추상개념으로 캡슐화할 필요가 있다. (ex)채팅룸  
 
  => 셀렉터 = 하나의 스레드로 여러개의 소켓 채널을 관리한다는 의미가 맞을듯?
  
  (etc)-셀렉션키= 셀렉터(이벤트 감시자&발행자)와 연결된 채널(이벤트 수신자)간 중간에서 .. 특정 이벤트
   와 특정 채널간의 관계를 의미(한마디로 셋 컨테이너에서 iterator의 개념)
  -소켓채널의 blocking 여부는 채널 단위에서 세팅하고 그 후에 셀렉터에 등록하는 것이다.
  - 채널이란 외부의 이벤트에 대해 반응(대응)할수 있는 범위 개념. 여러 종류의 채널별로 대응할수 있는 외부
     (네트워크) 이벤트가 정의되어 있다.
  
2.동접 5000명이면.. 셀렉터 약 100개 필요? 셀렉터 풀을 만든다.;; (스레드 100개니?)

3.셀렉터에게 배정할 채널(소켓)을 미리 만들어두고 풀에 관리한다. (셀렉터.open() 해서 생성 안하게)

4.온갖 작업을 할수 있게끔 워크 스레드풀을 활용한다.
  4.1 서버측은 어셉 작업용 스레드 배정(3~5개쯤?)
  4.2  리드 작업용 스레드 배정(몇개쯤? 좀 많이?)
        내가 패킷(헤더+바디)을 사용하려 하고 +  거기다가 그제,어제 공부했던 보안 패키지 클래스를 사용하려 하다
        보니..  패킷 헤더 해석 작업이 들어가고 그 해석에 따라서 클라들에게 전송or 기타 작업을 하게 될듯하다.
  4.3 위의 클라들에게 전송 작업용 스레드 배정(음.. 이건 4.2에 순차적 의존 관계가 있으니.. 따로 스레드 처리 안될듯)
  4.4 서버에 로그or콘솔창에 뿌려줘야 하니까 이 용도로 스레드 배정(이것도 과연 필요할지.. 한글자 한글자 출력할게
       아니면.. 4.2의 리드작업을 통해 모든 데이터를 가져온후, 그에 따른 해석에 따라 콘솔에 뿌리던지.. 로그를 찍던지
       할것 임으로.. 스레드의 생성이 과연 필요할지..)

  =>서버 측에서 중요한건.. accept , read 에서 blocking이 되는 현상이다. 이걸 일단 nio패키지의 셀렉터, 다이렉트
     버퍼를 이용해 해결이 된다 하지만.. 내가 멀티스레드를 적극적으로 활용하려는 이유는 서버 컴퓨터의 성능(cpu갯수)
     을 전부 활용하고 싶기 때문이다. 한마디로 순차적 의존성(데이터든 연산이던)이 없는 비지니스에 대해서 스레드들
     을 적극적으로 사용하고 싶다. 솔직히 까놓고 얘기해서 어셉을 담당하는 스레드가 1개일 경우와 5개일 경우.. 동시접속
     요청이 1000건 발생했을때.. 1개의 스레드로는 요청1개씩 처리를 하게 되기에.. 사용자가 열이 받을 것이다.

  4.5 클라측도 장난이 아니다;; 소켓에서 읽기 작업용 스레드 배정
  4.6 패킷을 해석하는 스레드 ?? 4.5에서 처리해야 할지..(일단, 헤더만 읽으면 명령을 생성할수 있고. 흠.. 근데 바디
       데이터가 반드시 필요한 명령이라면?)
  4.7 패킷의 분석 결과가 채팅 관련 데이터라면.. 콘솔이던 사운드 재생이던 동영상 재생이던.. 작업용 스레드 배정;;
  4.8 사용자의 입력을 처리하는 스레드 1명
  4.9 입력한 값을 해석해 패킷을 구성하는 스레드.(파일 전송이면;; 파일 로드용 스레드?) 
  5.0 만들어진 패킷을 서버로 발송하는 스레드 (마찬가지로 4.9와 5.0은 4.8에 순차적 의존 관계에 있다. 왜냐하면 4.8
       에서 사용자의 엔터키or 다른 입력 완료 메세지가 발생해야 사용자의 비지니스 하나가 만들어지기 때문이다.)
       
  =>클라측에서 중요한건.. 사용자 입력 -> 서버로 전송 작업은 순차적 의존 관계에 있기 때문에 싱글 스레드면 족하지만,
     지난 글에서 고민했듯.. 사용자 입력중에도 서버가 전달해주는 다른 채팅자들의 데이터는 읽어와서 유저의 화면에
     갱신(추가)을 해줘야 한다. 즉 두개의 작업이 병렬처리됨과 동시에.. 추가로 고려할 점은 유저가 채팅중에 이미지,
     동영상 등을 전달했을때.. 이것을 처리하기 위해 새로운 작업 스레드가 필요할 것이다.
     (물론 송수신 데이터패킷을 가볍게 하기 위해 가급적 채팅 화면에 동영상 추가, 파일 전송등의 비지니스를 요청할 
      경우 서버측에서는 다른 채팅자에게 수락/거절 등의 메세지를 전달하고 다른 채팅자가 수락을 했을 경우에 
      직접 파일 전송이 아닌, 동영상등 파일의 소유자와 수락을 한 요청자간 둘이서 직접 파일을 주고 받게끔 클라프로
      그램에 명령을 전달하는것이 옳을 것이다. 서버측은 채팅 서비스를 제공함에 있어서 관리자의 입장에서 필요한
      데이터의 추출, 관리적 비지니스를 처리해야 하기 때문에.. 서버는 중계의 역할만 하는 것이 옳을 것이다) 

  =>결국 위의 여러 고민들은 스레드를 통한 병렬처리의 도입이다.(순차적 의존이 불필요한 동작들을 독립적으로 처리시
     킴) 

     (etc) 소켓채널의 경우 기본적으로 하나의 통로로서 readable,writeable 혼용이다. 즉 읽는 도중 쓰기가 있으면
     기존 읽기 작업이 멈추게 된다. (-1리턴 식으로) 이는 blocking이 된것이다. 소켓 채널이 비동기화에 대해 안전하다
     는 것은 예를 들어 하나의 스레드가 읽기 작업 도중 다른 스레드에게 제어가 넘어가 입력을 끊었을때.. 기존 읽기를
     취소하고 -1을 리턴하며, 기존 중단된 스레드 객체는 AsynchronousException을 전달 받으면서 깨어나기 때문에.. 
     catch 블럭에서 적절하게 핸들링이 가능할 것이다.

5.DirectByteBuffer 풀을 활용한다.(미리 왕창 만들어두고 재활용하기)
  5.1 non blocking 상태에선 read작업을 100% 성공한다고 확신해선 안된다. 네트워크의 사정에 따라, 네트워크 디바이
      스의 성능에 따라 .. 일부만 읽어올수 있다. 그럼으로 패킷의 헤더에 전체 패킷의 길이를 심고, 완전히 얻었을때 까지
      스레드를 반복문에서 sleep or blocking 할필요가 있다. 단 주의해야 할점은.. 네트워크 종료등의 상황이 발생시 스레
      드가 lock을 걸고 나갔다면.. deadLock 현상이 발생할수 있다. 그럼으로 스레드 작업 사용시.. 제한시간의 개념을 추가
      해줘서 위와 같은 위험을 방지한다. 
   5.2 다이렉트 버퍼는 object단위를 표현 못한다. (한마디로 일반 io의 readObject 단위가 불가능) 그럼으로 gatherring/sc
   atteringByteBuffer 인터페이스의 필요성이 발생한다.(이게 없으면 개발자가 일일히 모든 패킷에 따라 readInt , readChar
   ,length 식으로 처리를 해야한다. 아참.. 버퍼를 사용할때 flip()을 주의할것!

   위의 게더링,스케틀링 인터페이스를 이용해 바이트 버퍼를 개념단위로 생성후 객체의 데이터 구조처럼 묶어서 관리가
   가능해진다. (참고로 socketChannel 클래스에 구현이 되어있었다. 어제 이것땜에 고민한것 생각하면 -_- +)

6.한글 깨질수 있다. 안그래도 복잡해서 nio패키지중 charset인가를 외면했는데.. 장착해야 한다 ㅠ.ㅠ
  6.1 이 문제는 채팅 프로그램의 로컬라이제이션에도 걸리게 된다. 그럼으로 적절하고 유연한 처리가 필요하다.
  6.2 그럼으로 java.nio.chrset + charBuffer(2byte 유니코드 단위 다이렉트) 를 이용하면 풀리지 않을까 생각하는데..
  6.3 한글 완성형의 코드 포인트 범위는 U+AC00~U+D7AF이므로, (44032 ~ 55215 = 11183개)
        UTF-8 인코딩에서 한글은 무조건 3바이트 인코딩이다. 
        그래서 URL에 파라미터 값이 %ED%95%9C%EA%B8%80과 같이 표시된다면 UTF-8 인코딩일 확률이 높다(ISO8859,
        EUC-KR, UTF-8 인코딩 중 하나라면 말이다). (왜냐하면 4비트 6개 = 3바이트)


7.셀렉터의 register()의 3번째 메개변수로 Object를 등록할수 있는데.. (한마디로 해당 소켓의 op_read 이벤트에
  대해 특정 객체or연산을 수행가능..) 이걸 이용해 클라와 세션, 명령에 따른  비지니스 로직.. command 패턴으로 
  구현하려 고민햇었지만, 패킷 헤더에 정보를 심는 작업으로 변경.
  =>음.. 3번째 요 정보를.. 셀렉션키에 설정할수 있었지.. read입력 발생시마다 워크스레드 사동 수행 시키게 가능할
    까? 말이 되나?

 8.아참.. 아까 설계상 엄청 고민했었는데.. 내 경운 서버에서 리드 작업용 객체와 브로드 캐스트 작업용 객체를 구분하려
   했고, 패킷(헤더,보디)를 사용하기로 결정했기에.. 스캐플링 바이트 버퍼 채널 인터페이스, 개더링 바이트버러 채널 인터
   페이스, 소켓 채널 클래스를 도대체 내 리드 작업용 클래스와 브로드 캐스트 작업용 클래스가 상속받고 구현할 것인지.. 
   이리저리 고쳐가며 오랫동안 고민했었는데.. 소켓 채널 클래스 도큐 문서를 다시 확인하면서.. 소켓 채널 클래스가 이미 
   개더링,스캐플링,기타 비동기화(인터럽티블),읽기,쓰기등의 인터페이스를 구현했다는 것을 확인하고 쇼크받았다.

   => 난 지금까지 도큐문서에서 클래스 정보를 볼때.. 클래스 명세,메서드 동작, 클래스 계층구조만 살폈지.. 구현한 인터페이
   스까지(조립제품) 꼼꼼히 살펴보지 않았다. 그 클래스가 도대체 무엇을 처리하기 위해 상속 계층 구조에 포함이 되었으며,
    어떠한 인터페이스로서 조립이 되었는지.. (하다못해 자바에선 스레드를 클래스로도 제공하고, runable이란 인터페이스
    로도 제공을 한다;;)

  9.패킷(전송용 데이터의 크기및 구성 단위)의 크기를 가급적 작게 유지해야한다. header부분은 4byte 정도 배정해서
    메세지의 형태를 표현하고, 하나의 패킷의 총 길이를 담아야 할것이다. body 부분의 데이터는 최대한 줄여야 하기 때문에
    압축 알고리즘을 사용하고 수신및,송신한 클라이언트 측에서 encode decode 할수 있게끔 하는 방법도 필요하다.
    아까 생각해본 유저간 파일 전송등의 기능이 필요할 경운.. 클라이언트 간에 p2p로 데이터 전송하게끔 클라이언트 프로그램
    을 작성해두면 될것이다. 
    9.1 네트워크를 이용한다는 측면.. 내 시스템을 외부의 공격에 노출을 시키는 것임으로.. 보안의 개념(인증,권한 설정, 데이터
         암호화의 개념을 적용시켜야 하는 것은 선택이 아닌, 필수라 생각한다) 
    9.2 위의 문제들중 몇몇은 tcp layer 에서 잡아주지 않을까 싶다? udp와 다른 tcp는 신뢰 가능한 전송 데이터니까..
         네트워크 레이어에 대한 개념이 제대로 안잡혔다. 네트워크 디바이스 단부터..(다이렉트 바이트 버퍼의 개념이
         디바이스 메모리에 대한 직접 접근및 제어니까..) 몇 계층이 있었는데.. 흠..

  10. 서버의 메모리 부족을 대비해 FileChannel을 통해 파일(hdd)을 메모리로 사용하게 할 수 있다.

  11. 실제 카톡에서 어떤식으로 서비스를 제공하나 봤는데.. 대단한건 안보였다. 다른 작업(동영상 시청중)중 동시에
       카톡 프로세스도 실행되는건.. 휴대폰os 차원에서 지원해줄 일이고.. 실제 카톡 내부에서 함께 동영상 보기, 파일
       전송등은 없고.. 링크 표시(문자열 + 링크 엑세스 지원만 해주면 간단) , 몇몇 이모티콘류(이건 클라이언트 배포할때
       패키징 하면되고.. 추가시마다 업데이트 해주면 되니까..) 정도이다. 채팅 룸의 개념을 이미지로 표시함으로서 인터
       페이스가 괜찮고.. 사용하기 쉽다.

  12. 채팅 클라,서버의 뼈대를 화면처리(view)에 대해 독립적으로 구성시키고.. 각종 컨트롤(비지니스)에 대한 설계적
       고민이 필요할 것이다. (글고 역시나. 채팅이기에 udp = datagram 의 사용도 고려해 볼만하다)

  13.결국 서비스 하면서 발생할 각종 예외 상황들에 대해 미리 시뮬레이션을 통해 감지한후 처리 로직을 만들어야 한다.
      이부분은 실무 경험에 크게 좌우되리라 생각한다. (실제 서비스를 해봐야 해당 서비스에 맞는 여러가지 예외가 발생
      할테니.. 중국 해커들은 대단하다..)

하여튼 관건은.. 스레드풀및 각종 풀의 구현이었다 -_-;;

분명히 풀을 지원하는 클래스 or 패키지가 있을거라 생각해서 뒤져보았다.
(랭 패키지의 스레드 그룹 , 유틸패키지의 큐 자료구조를 이용해 직접 구현은 말이 안되고 -_-;
더 뒤져보니 유틸패키지.콘커렌트 패키지에.. 스레드 풀 익스큐터;; 익스큐터란.. 말도 안되는 팩토리의 팩토리 클래스..
스레드 팩토리까지 제공.. 익스큐터.캐쉬드스레드풀인가 느낌이 좋더만;;)

일단 풀링을 사용할 경우의 코드 형태 checkout() checkIn() 마음에 드네 하.하.하 (일단 풀에서는 맵계열vs셋계열
vs큐vs스택vs리스트계열vs어레이계열vs트리계열 과연~ 정답은? ㅋㅋ )


오늘은 설계한답시고 깝죽대고.. 코드 몇줄 구현하고.. 나머진 온갖 새로운 지식 습득하려 삽질을 하다가.. 좌절중..이다..
스레드를 100%이해를 못하는데, 어찌 스레드 팩토리, 요청 폴링, 스레드 풀링을 구현할 것인가? 완전한 비동기식 i/o, 멀티 
스레딩처리, 폴링, 풀링 개념.. 자잘하게.. 패킷 구조 설계와 확장성 고민.. 각종 디자인 패턴들.. 암호처리.. 

결국 내 정신은 안드로메다로 떠나버렸고.. 심각하게 의욕이 감퇴되고.. 우울해졌다.
애초 계획대로 초 심플하게 다자간 채팅 + nio 방식의 i/o + 병렬 처리 가능한 작업 하나당 스레드 한개씩 사용 ... 
이것조차 구현할 수 있을지 걱정이 태산이다. 이것저것 벌려놓고 제대로 하는게 없어서 속상.. ㅠㅠ 내일은 한결 낫길!

요2일동안.. jsp 진도 전혀 못나갔다. (보안관련,j2ee 프레임워크 관련,EJB3.0관련,스프링 관련,이번 채팅 프로젝트 설계 
고민, 온갖 잡다한 정보들 찾기..) 

etc)자바소켓 프로그래밍 참고 블로그 


Class NIOServerCnxnFactory



by givingsheart 2014. 1. 1. 16:23

1.클라이언트측


Client(메인프로그램)

UserInfo(유저 정보)
ServerInfo(서버 정보)

//업무관련
ServiceUnit(이벤트 발행자인 Selector와 구독자인  BusinessUnit 을 캡슐화)
BusinessUnit(op_read 이벤트에 반응하는 ReadUnit 과 , 사용자의 입력처리 이벤트(엔터)에 반응하는 WriteUnit을 병렬처리 
캡슐화)

ReadUnit(서버가 보내준 패킷을 PacketUnit을 통해 Command를 받고 해당 명령(화면출력 명령이면 콘솔등에 출력, 
시스템 관련 강제 종료(강퇴등)이면 처리 등의 커맨드 패턴을 구현한 클래스))
WriteUnit(사용자의 입력을 PacketUnit에게 전달후 결과를 통해 Command를 받고 해당 명령 서버에 전송 등을 처리)

//패킷관련 (dto)
PacketUnit(입,출력하는 패킷을 분석해서 Packet헤더, Packet바디, Packet확장 오브젝트를 구성 + 명령을 리턴)

//일반적인 채팅 프로그램은 아래처럼 확장이 안된다.

Data(이런 저런 메세지들의 루트클래스)

SingleData(Data의 하위클래스로 단일 종류의 메세지의 루트클래스)
ChatData(SingleData의 하위 클래스로 채팅용 문자열 메세지)
FileData(SingleData의 하위 클래스로 파일 이미지 전송용)

CommandData(SingleData의 하위 클래스로 특정 명령을 요청하는 데이터의 루트클래스)
LogInData(CommandData의 하위 클래스로 로그 인)
LogoutData(CommandData의 하위 클래스로 로그 아웃)

MultiplexData(Data들의 리스트 클래스로 여러 종류(채팅,커맨드가 뒤섞인) 섞인 메세지의 루트클래스)


패킷 관련해선 java.nio.channels.ScatteringByteChannel; //버퍼에서 읽는다.
java.nio.channels.GatheringByteChannel; //버퍼에 쓴다.  
를 통해 오브젝트 단위로 관리한다.


요 두 인터페이스가 중요한게.. 패킷(헤더+보디...)을 byte버퍼에서 구분해서 사용하게 해준다.
ByteBuffer header = ByteBuffer.allocateDirectBuffer(30); //30byte의 패킷 헤더 설정
ByteBuffer body = ByteBuffer.allocateDirectBuffer(1000); //1000byte의 패킷 보디 설정

header.put("multiplextData");
body.put(imageBuf); //byte로[] 변환된 이미지 데이터
body.put("이미지받아라 요놈앗!');
body.put(iconBuf); 

header.flip();
body.flip();

ByteBuffer[] packet = {header,body}; //이게 게더링(함께) 이다.!

//serverSocketChannel 이든, socketChannel 이든.. GatheringByteChannel을 임플리먼트
int writeLength = serverChannel.write(packet);

요런식으로 처리하면 될라나?


by givingsheart 2014. 1. 1. 16:22

http://blog.naver.com/PostView.nhn?blogId=tyboss&logNo=70106055352


1.가급적 객체지향적으로 입력 관련 작업을 알아서 처리하는 객체, 출력 작업을 알아서 하는 객체를 만든다.
  일하는 스머프를 만들자 -_-; (독립스레드)read스머프.doWork(), (독립스레드)write스머프(내부적으로 2차원
  연결리스트로 방들과 들어있는 유저들을 알고 있음).doWork()


selector = reactor 패턴 = 핸들링 객체와 이벤트감지 객체를 분리시키고(옵저버 패턴처럼 중간에 처리하는놈 = 
이벤트와 비지니스 로직의 분리 가능)
핸들링 객체와 이벤트 감지 객체 사이에 중계 역할의 개체(selectionKey)를 두어서 두개의 결합을 느슨하게 한다.


(추가) 내 욕심에 .. 너무 복잡도를 늘려놔서 실제 구현 성공이 어려웠다. 간단하게 시작해서, 돌아가는 코드에 패턴 적용

등 리팩토링  + 기능 구현을 했어야 했다.


너무 복잡하면 시작을 못하게 된다. 문제를 단순화 하는 습관을 들이자!!

by givingsheart 2014. 1. 1. 16:22

1.큐를 사용해서 채팅 결과를 저장 + 새로 들어온 입력 추가 .. 요런식으로 접근을 했는데.. 이게 먼 삽질인가?

 매번 들어온 입력이 있으면 읽어와서 콘솔에 뿌려주면 그만인것을..  (매번 전체 채팅 내용을 전부다 갱신해
 줘야한다고 생각을 했엇다. 이건.. 채팅프로그램이다;; 게임 처럼 모든 움직이는 오브젝트, 배경을 매 프레임
 다시 그려야 하는것이 아니다. (이건 출력 대상이 콘솔이기 때문이 아니다. gui의 윈도우 창같은 경우라도.. 
 출력할 도큐먼트의 끝에 새로 들어온 채팅 메세지를 추가해주면 되는것이다. 내 고정관념의 결과인듯 하다;;)

2.유저가 채팅을 입력하는중(엔터키 전까지)에도 멈추지 말고 계속 서버에서의 입력값이 있는지 확인하고 
  있으면 콘솔에 갱신을 해줘야 했다. 유저의 엔터키가 있을때야 하나의 서버로의 출력 이벤트가 발생하는 것이고...
  즉 서버에서 읽어서 콘솔에 뿌려주는 작업 vs 유저의 입력을 받은후 서버로 전달하는 작업은 순차적인 의존관계
  가 있어선 안됀다. (독립적인 두 작업이 병렬적 = 멀티 스레딩 으로 처리되어야 한다.)

  내 경운 이번 채팅 프로그램에서 nio 패키지를 이용한 비동기 방식의 입출력 처리 + 멀티 스레드 작업 + 메세지에
  암호화 적용인데.. 

  아까의 구성은 잘못 되었다. 좀 더 고민이 필요하다.

 3.만약 이번 채팅 프로그램을 gui 기반으로 이동시킬 경우를 대비해.. 조금 더 설계상 고민을 해야한다.

 4.채팅 프로그램의 확장성을 위해(특정 액션을 의미하는 메세지추가) 메세지(패킷)를 상속 구조를 이용해 분할한다.
   상속구조를 이용하는 이유는 서버측에서 일괄된 형태의 패킷을 받아 커맨드 패턴을 적용하고 싶기 때문이다.
   (그러기 위해서 다형성 필요)

 5.추후 로그인등 과정이 추가될수 있으니.. 클라이언트 클래스의 설계(각각의 개념요소 추출해서 클래스화 & aggregation
   식으로 고민을 하자.)

(etc) 서브버전이 저장소를 인식을 못한다. 미치겟다 ㅠㅠ


by givingsheart 2014. 1. 1. 16:21

현재까지 클라이언트 진도 5%;;(새로 접하는 개념이라 자꾸 삽질이 있음;;)


import java.io.*; //콘솔 출력용 버퍼드 라이터
import java.net.*; //
import java.nio.*; //다이렉트 바이트 버퍼
import java.nio.channels.*; //소켓및 리스너
import java.util.concurrent.*; //망할 큐 사용;;

public class Client
{
//유저정보
private UserInfo user;
private ServerInfo serverInfo;
//소켓관련
private Selector selector;
private SocketChannel socketChannel;
//메세지 타입
public static MsgType msgType;
//채팅 내용을 화면에 뿌릴 메세지 버퍼
//private ArrayList<String> chatList; 
//생각을 잘못함. nio 스트림 써야함..
//long read (ByteBuffer [] dsts) 이 채널의 바이트 순서가 지정된 버퍼에 
//읽어들입니다.
//그럼으로 nio.바이트버퍼 필요 + 다이렉트 바이트 버퍼가 빠른것 같음.
//다이렉트 byte 버퍼는, 이 클래스의 팩토리 메소드 allocateDirect 를 호출
//하면(자) 작성됩니다.
//static ByteBuffer allocateDirect (int capacity) 
    //새로운 다이렉트 byte 버퍼를 할당합니다.
//도대체 얼만큼 크게 잡아야 할까? 
//한줄에 20글자 = 40~50byte  * 여러줄.. 흠.....
//콘솔창에 몇줄이나 뜨지? 50줄?
//그럼 2000바이트 짜리를 만들어야 하나?
//그럼.. 채팅 내용이 계속 쌓이면????? 
//미치겠네..
//바이트 버퍼를 여러개 만들고 Queue같은 자료구조에 담아서 관리할까?
//너무 많이 쌓이면 먼저 들어간거 부터 빼면 되니까..
//하여튼 서버에 쓸 용도는 조그맣게.. 
//읽어와서 보관할 양은 무지 많이..? 
//아.. 미치겟네..
//public class ArrayBlockingQueue<E> 사용 결정 -_-;;;
private static final int MAX_WRITE = 100;
ByteBuffer writeBuffer; //서버에 출력용
//ByteBuffer readBuffer; //서버로 부터 입력용 -> 이걸로 콘솔에 출력
private static final int MAX_READ_COL = 100;
private static final int MAX_READ_ROW = 50; //대충 50줄 -_-;
ArrayBlockingQueue<ByteBuffer> chatQueue; //솔직히 어떤 특성의 큐인지 모르겠다. 배열과 맞는다는거만 보고;;
//생성자
Client(UserInfo user, ServerInfo serverInfo)
{
//1.기본 정보 세팅
this.user = user;
this.serverInfo = serverInfo;
this.writeBuffer = ByteBuffer.allocateDirect(MAX_WRITE);
//read 할때마다.. 다이렉트 바이트 버퍼 새로 생성해서 put하기!
this.chatQueue = new ArrayBlockingQueue<ByteBuffer>(MAX_READ_ROW);
try
{
//2.소켓 생성
this.socketChannel = SocketChannel.open(new InetSocketAddress(this.serverInfo.url, this.serverInfo.portNum));
//3.리스너 생성
this.selector = Selector.open();
//4.소켓에 뭔가 쓰여진 이벤트 리스너 등록 
//구지 SelectrionKey로 구분해야했나.. Selector에 포함시키지(사용하기 겁나 복잡하네 -_-)
this.socketChannel.register(this.selector, SelectionKey.OP_READ);
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//초 간단 인터페이스
public void chat()
{
while(true)
{
readInputStream();
writeOutputStream();
}
}
//서버가 보낸걸 읽어오기 
private void readInputStream()
{
//1.채널에 들어온 내용 있나 확인 
//2.있으면 내 큐에 추가
//3.화면에 뿌리기
drawToConsol();
}
private void drawToConsol()
{
}
//사용자의 입력을 받아 서버에 전달
private void writeOutputStream()
{
//사용자 입력 처리 스캐너 사용할까?
//서버에 보내기
sendMsgToServer();
}
private void sendMsgToServer()
{
}
}


by givingsheart 2014. 1. 1. 16:21

정보를 찾아보니 3rd 파티에서 iocp를 구현한 라이브러리를 판다고는 하는데.. 

nio를 이용해 만드는 것도 나쁘진 않다고함. 관건은.. iocp나 eventSelect처럼 소켓 디바이스에 입력이 있었을때
자동으로 호출되는 콜백 메서드 등을 찾아야함. 

패키지를 살짝 살펴본 결과.. 일반 소켓(net.socket)을 통한 스트림 연결이 아닌, nio를 지원하는 소켓 스트림이
있는듯함. java.nio.SocketChannel , ServerSocketChannel , 글고 이벤트 발송자 역할인 Selector 인가도 있음.
(SocketChannel , ServerSocketChannel 클래스는 구독자 + SelectionKey 클래스는 발송자,구독자를 느슨하게
 연결하는 adapter 클래스)
(java.nio 패키지와 그 하위 패키지들을 대충 확인할 필요가 있음)


하여간 1.스트림 열고 2.읽고,쓰고 3.닫고 할줄 알면.. 채팅 프로그램 만들수 있을거라 착각중;;(선생님께서 나중에 
4단계?에서 채팅프로그램 만든다고 하셧으니.. 미리 진행)

****************************************************************************
내 채팅 프로그램의 기획을 어떻게 할까를 결정해야함.

1. 몇대몇 채팅 프로그램 (1:1? 다자간?)
      a. 여러명이 하나의 채널에서 모여서 채팅할수 있는 프로그램인지?
      b. 1:1로 채팅하는 프로그램인지?
2. 서버의 역할 (peer to peer? broad cast server?)
       a.서버의 역할이 클라와 클라를 중계해주는 개념인지(클라a가 메세지 보내면 그것을 연결된 다른 b,c,d 클라한테 쏴주는 
          역할
       b.방장의 개념으로 방을 만들고 접속을 받아 들이는 클라인지(클라겸 서버) 

요즘 인기있는 카톡은 어떤식이지? 카톡에 로그인해서 방을 만들면 사람들이 들어오는건가?(방장이 서버역할, 서버는 개설된
방정보를 클라에게 전달후, 클라가 방을 선택하면 연결시켜주기)  vs 아니면.. 1:1로 문자 메세지 보내는건가? 중간에 사람들
이 들어올수도 있고..

흠.. 1 = a  , 2 = a (조금 어려울라나;;)

대략적인 스케치

<클라>
1.데이터
 a.유저: 별명
 b.서버 접속용 정보:서버주소,포트번호,채팅방번호(원하는 사람끼리 채팅가능하게 구분해주는 개념)
 c.i/o용 데이터:소켓,입력 스트림, 출력 스트림
                     내가 쓴 메세지&채팅 참여자가 쓴 메세지를 담아서 출력할 byte 버퍼,
                     유저의 입력을 저장할 스캐너
2.동작
  a.채팅방에 찾아간다.
  b.사람들과 채팅을 한다.(유저로부터 쓴다.쓴 내용을 서버에 보낸다. 서버로부터 읽는다. 읽은 내용을 화면에 출력한다 = i/o)
  c.채팅을 종료한다.

처음 입장시 보낸 데이터 패킷:아이피는 알아서 전달 될테니.. 별명,선택한 방 -> 자신의 방에서 놀면됨 
채팅시 보낼 데이터 패킷: 유저형 별명 + 채팅 메세지 (선택한 방은 불필요.. 근데.. 나중에 전체 공지를 추가하려면?)
=>메세지 타입 만들어야 겠음.
enum MsgType
{LOGIN,CHAT,LOGOUT};

=>한번 더 쪼개야 겠음;;
enum ChatType
{NORMAL};

=> 그럼으로 데이터 패킷은: 메세지 타입 + 종류따라 채팅 메세지, 기타등등
=> 서버측에선 메세지를 처리하는 메서드를 커맨드 패턴으로 만들어 보자 -_-;;


(실제 채팅처럼 로그인 처리=인증 , 주고 받는 데이터 암호화, java.awt나 swing을 이용해 gui로 만드는건.. 
너무 복잡해지고 능력도 안됨;;)

<서버>

1.데이터
  a.유저 데이터 리스트(채팅방의 개념) 여러개의 채팅방을 지원할거면 유저 리스트의 리스트
     유저 데이터 = url,port, 방번호(리스트의 인덱스)
  b.
2.동작
  a.유저가 1번 방에 접속한다. (1번 유저리스트에 유저 추가)
  b.유저가 채팅 메세지를 보냈다. (1번 유저리스트에게 메세지 전부 보내기)
  c.유저가 1번 방에서 채팅을 종료했다. (1번 유저리스트에서 해당 유저 삭제)
    여기서 고려할께.. 중간 삽입,삭제가 잦아지면.. 연결리스트 고려할 필요가 있음..(문제는 조회가 느림)
  d.... 서버가 할일이 없네.. 욕설 쓰면 강퇴 시낄까 , 아니면 귓속말 기능을 추가할까;;
    방장 개념을 만들어서 방장에게 강퇴 권한을 줄까.. 괜히 복잡해지겠다. 심플하고 유연하게 만들어보자.
    나중에 기능 추가가 쉽도록!

만들어보자 -_-;;

****************************************************************

내가 채팅 프로그램 만들때 필요한 클래스들.. (nio 중심으로)

<클라용>

java.nio.channels 
클래스 SocketChannel


스트림형 접속 소켓용의 선택 가능 채널입니다.

소켓 채널은, 접속 네트워크 소켓의 완전한 추상화가 아닙니다. 소켓 옵션의 바인드, 종료, 및 조작은,socket 메소드 호출에 의해 취득한 관련 Socket객체를 개입시켜 실시할 필요가 있습니다. 임의의 기존 소켓의 채널을 작성하거나 소켓 채널에 관련한 소켓으로 SocketImpl 객체를 사용하도록(듯이) 지정할 수 없습니다.

소켓 채널은, 이 클래스의 open 메소드중 1 개(살)을 호출하는 것에 의해 작성할 수 있습니다. 새롭게 작성된 소켓 채널은 오픈입니다만, 접속은 확립되어 있지 않습니다. 미접속의 채널에 대해서 입력 조작을 호출하려고 하면(자),NotYetConnectedException 가 throw 됩니다. 소켓 채널을 접속하려면 , 그 connect 메소드를 호출합니다. 접속된 소켓 채널은, 클로즈 할 때까지 접속된 채로 있습니다. 소켓 채널이 접속되고 있을지 어떨지는,isConnected 메소드의 호출에 의해 판단할 수 있습니다.

소켓 채널은 「비블록 접속」을 지원합니다. 소켓 채널의 작성과 원격 소켓에의 링크의 확립 프로세스는 connect 메소드에 의해 개시되어 나중에finishConnect 메소드에 의해 완료합니다. 접속 조작이 실행중일지 어떨지는,isConnectionPending 메소드의 호출에 의해 판단할 수 있습니다.

소켓 채널의 입력측과 출력측은, 실제로 채널을 클로즈 하는 일 없이, 따로 따로 「정지」됩니다.관련 소켓 객체의 shutdownInput 메소드를 호출해 채널의 입력측을 정지한 뒤, 한층 더 채널을 읽어내려고 하면(자), 스트림의 마지막을 나타내는 -1 이 돌려주어집니다. 관련 소켓 객체의 shutdownOutput메소드를 호출해 채널의 출력측을 정지한 뒤, 한층 더 채널에 기입하려고 하면(자),ClosedChannelException 가 throw 됩니다.

소켓 채널은,Channel 클래스로 지정되고 있는 비동기 클로즈 조작과 자주(잘) 닮은 「비동기 정지」를 지원합니다. 한편의 thread가 소켓의 채널에 대한 read 조작의 도중에 블록 되고 있는데, 이제(벌써) 한편의 thread가 소켓의 입력측을 정지했을 경우, 블록 된 thread의 read 조작은, 바이트를 일절 읽어내는 일 없이 종료해,-1 를 돌려줍니다. 한편의 thread가 소켓의 채널에 대한 기입 조작의 도중에 블록 되고 있는데, 이제(벌써) 한편의 thread가 소켓의 출력측을 정지했을 경우, 블록 된 thread는 AsynchronousCloseException 를 받습니다.

소켓 채널은, 복수의 병행 thread로 안전하게 사용할 수 있습니다데이터 그램 채널은 병행 read 및 기입을 지원합니다. 다만, read를 실시하는 thread도 기입을 실시하는 thread도 특정의 시점에서는 항상 1 개 이하입니다connect 메소드와 finishConnect 메소드는 서로 동기 하고 있어, 어느 쪽인지 한편의 메소드의 호출중에 읽어들여 또는 기입해 조작을 개시하려고 하면(자), 이 처리는 최초의 호출이 완료할 때까지 블록 됩니다.


=>명세 요약

1.소켓 채널 생성하려면 open() 사용

2.알아서 connect()

3.비동기 i/o

4.스레드 사용 가능



static SocketChannelopen (SocketAddress  remote) 
          소켓 채널을 오픈해, 원격 주소에 접속합니다.

longread (ByteBuffer [] dsts) 
          이 채널의 바이트 순서가 지정된 버퍼에 읽어들입니다.

longwrite (ByteBuffer [] srcs) 
          이 채널의 바이트 순서가 지정된 버퍼로부터 써냅니다.



클래스 java.nio.channels.spi. AbstractSelectableChannel 로부터 상속된 메소드
blockingLock , configureBlocking , implCloseChannel , implCloseSelectableChannel , implConfigureBlocking , isBlocking , isRegistered , keyFor ,provider , register
 
클래스 java.nio.channels. SelectableChannel 로부터 상속된 메소드
register
 
클래스 java.nio.channels.spi. AbstractInterruptibleChannel 로부터 상속된 메소드
begin , close , end , isOpen
 
클래스 java.lang. Object 로부터 상속된 메소드
clone , equals , finalize , getClass , hashCode , notify , notifyAll , toString , wait , wait , wait
 
인터페이스 java.nio.channels. Channel 로부터 상속된 메소드
close , isOpen



<서버용>


java.nio.channels 
클래스 ServerSocketChannel


by givingsheart 2014. 1. 1. 16:20
| 1 |