ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • websocket:채팅
    JAVA 2021. 7. 27. 17:16

    websocket + 네트워크운영 이해

    데이터 보낼 때 송수신 측에서 받는 방식

    1) TCP/IP

    수신측이 보내는 데이터는 패킷단위로 전송되어 송신측이 받는다.

    핸드쉐이크 : 서로 데이터 유실이 없는지 확인하여 계속 소통하며 확인한다.

    - 장점 : 안전하다.

    - 단점 : 느리다.

    2) UDP

    핸드쉐이크가 없다.

    - 장점 : 빠르다 (TV 같은 것, 빠르다)

    - 단점 : 데이터가 몇몇 유실될 수 있다.(가끔 끊기는 이유)

    * TCP/IP 기반의 프로토콜 종류
    (1) http
    : TCP 기반에서 더 추가하여 http 라는 규약이 생긴 것.
    (2) smtp
    (3) FTP
    (4) 사용자가 직접 TCP/IP 기반의 프로토콜을 만들 수도 있다.

    * 네트워크 운영 개념
    client - server 소통 중
    server
    1) 서버 socket 준비
    2) bind(80) : port 가 80 으로 시작하는 얘로 연결걸 때 본인이 나오겠다고 설정함
    (톰캣이면 bind 가 8080)
    3) listen (항상 대기)
    4) accept ( client 가 연결을 바랄 때 응답 )
    5) receive : 클라이언트 socket 을 전달받음
    client :
    1) 클라이언트 socket 준비
    2) connect :서버에 연결을 시도:연결을 걸기만 할 뿐 받아들이는 건 못함
    3) send : 서버가 accept 한 후 서버에 클라이언트 socket 을 전달
    4) close : 통신끝나고 클라이언트가 통화 끝냄 ( 서버에 끝냈다고 전달 하는 것 )
    socket?
    의사소통을 주고받는 도구(전화기같은 느낌)
    client 가 connect 걸어서 socket 으로 소통을 시도 -> acept 하여 server 에 있는 socket 이 반응
    * 네트워크 운영 중 서버 안의 클라이언트 끼리 소통할 수 있는 방법 ( = 이게 채팅 )
    같은 서버안이라도 서버와 클라이언트의 대화지 클라이언트끼리는 서로를 인식할 수가 없다.
    아래에 예제 참고!
    1번 방법 : 메세지를 보내는 클라이언트를 server 로 만들어 준다.
    ( 직접 server 로 만들어 위의 '네트워크 운영 개념' 대로 server가 하는 작업을 만들어줘야 한다.
    하지만 아래 spring에서 실습에서 사용한 라이브러리가 저 과정을 자동으로 해줌 )
    javascript 에서 작업
    1) new WebSocket : 기본적으로 js 에 있는 객체 ( 그래서 브라우저도 알고 있다. )
    이를 통해 클라이언트의 소켓들을 받아서 서로에게 마치 전달되는 것처럼(server처럼)만들어 줌
    spring 에서 작업 ( 현재 서버는 spring 운영으로 가는 중 )
    1) 'spring-websocket' library 의존성에 다운 ('2)' 의 클래스가 있는 라이브러리.)
    + 'jackson-databind' library ( ajax 통신할 때 다운받는것 // 이미 받았다. //없다면 아래 참고하여 다운! )

    2) TextWebSocketHandler : 이 클래스를 상속받아 handleTextMessage() 메서드를 override 해준다.
    = 메세지 처리기 ( 클라이언트에서 온 메시지 처리 )

    3) WebSocketConfigurer: 이 인터페이스를 구현하여 registerWebSocketHandlers() 메서드를 override 하여 '2)' 설정한 클래스를 add
    = 메세지 처리기 등록 ( spring 이 javascript 의 WebSocket 에 대응하여 응답하라고 알려주기 위해 등록 )

    ( 마치 resource 가 오면 WebMvcConfigurer를 상속받은 클래스로 addResourceHandlers() 메서드를 override 해서
    해당 요청명과 폴더를 add 했듯, 과정이 똑같다.)

    WebSocket 개요

    http 프로토콜이 아닌 ws 프로토콜로 두개의 프로토콜을 한 서버에서 다루는 건 힘들다. (서버 하나 = 프로젝트 하나)

    채팅 전용 서버 만드는게 프로젝트 하나를 따로 만드는것과 같은 의미이다.

    요청명만 설정해놓으면 다른 프로젝트에서 아래 예제의 프로토콜을 사용하여 (어차피 같은 ip:port 인 host 이다) 연결이 가능하다.

    1. 의존성 다운

    : spring-websocket 의 version 은 spring-webmvc API 와 똑같은 버전으로

        <!-- json --> // 있다면 다운 안받아도 됨
        <dependency>
    	  <groupId>com.fasterxml.jackson.core</groupId>
    	  <artifactId>jackson-databind</artifactId>
    	  <version>2.10.1</version>
    	</dependency>
    
    	<!-- spring-websocket -->
    	<dependency>
    	    <groupId>org.springframework</groupId>
    	    <artifactId>spring-websocket</artifactId>
    	    <version>5.0.2.RELEASE</version>
    	</dependency>
    1) jackson-databind
    이는 Spring 과 관련이 있는 것은 아니고 java 와 js 가 JSON 객체를 주고 받게 하는
    @ResponseBody, @RequestBody 를 쓰려는 의미이기 때문에
    spring 이 아니더라도 이걸 낀다. (다른 라이브러리도 존재하지만 해당 라이브러리가 가장 괜찮다.)
    2) spring-websocket
    이는 Spring 과 관련이 있으며 spring 에서 websocket 을 쓸 수 있게 하는 것이다.
    해당 라이브러리가 JSON 객체를 주고 받기 때문에 ( js 의 websocket 을 받아야 한다 ) 위의 라이브러리가 필요한 것이다.

    이 라이브러리가 jsp-spring 운영체제에서 내가 직접 했어야 할 위의 '네트워크 운영 개념' 의 server 내용을 알아서 작업해준다.
    client 내용만 설정하면 된다.(jsp안 javascript 가 client 단. java는 중재자하는 중인 실제 server 단)
    client 내용 중 close() 만 client창에서(jsp) 안해도 되고 server단에서 하면 된다.
    ( jdbc 라이브러리는 대기상태에 빠져있어서 직접 close() 해줘야 했다.)

    2. 메세지처리기 만들기
    TextWebSocketHandler 을 상속받는 클래스 생성

    js 에서 들어오는 WebSocket 을 받는 handleTextMessage() 메서드를 override

    @Component : @Configuration 들이 @ComponentScan 로 빈으로 등록하는 어노테이션 중 최상위
    @ComponentScan 이 스캔하여 Bean 등록하는 어노테이션 :
    @Component 과 이에 포함된 @Repository, @Service, @Controller
    현재 쓰는 메세지처리기는 역할이 DAO(@Repository) 도 아니고 비지니스영역(@Service) 도 아니고 컨트롤(@Controller) 도 아니기 때문에
    @Component 를 썼다.
    //연결 및 메세지 처리기
    @Component
    public class MessageHandler extends TextWebSocketHandler{
    	@Override
    	protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
    		//TextMessage payload=[ss], byteCount=2, last=true] 출력
    		System.out.println(message);//TextMessage 객체로 이 객체의 toString 이 실행되어 위처럼 출력됨
    		
    		//session 의 sendMessage() 는 받은게 TextMessage 형이니 보낼 때도 TextMessage 형이다.
    		//그래서 메세지 열만 TextMessage의 메서드인 getPayload() 로 String 형변환 후
    		//이 메세지만 꺼낸 String 을 다시 TextMessage 형변환 하여 보낸다.
    		String 메세지만 = message.getPayload();
    		TextMessage 보낼message = new TextMessage(메세지만+"를 잘받았어");
    		session.sendMessage(보낼message);
    		
    		//연결처리
    		//메세지 처리
    		
    		//끊기 처리
    	}
    }

    3. 메세지처리기 등록

    WebSocketConfigurer 을 구현한 클래스 생성

    @Configuration
    @EnableWebSocket
    public class 웹소켓환경설정 implements WebSocketConfigurer{
    
    	@Override
    	public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
    		registry.addHandler(new MessageHandler(),"/chat");
    	}
    }
    @Controller 와의 차이점 :
    WebSocket 이 돌아갈 TextWebSocketHandler 을 상속받은 클래스는 요청명이 존재해도 컨트롤과는 달리
    위에 RequestMapping 을 하는게 아니고
    WebSocketConfigurer 구현한 클래스에서 직접 매핑을 시켜줌.
    등록은 @ComponentScan 이 아니고 WebSocketConfigurer 의 클래스에서 add 하여 사용한다.

    4. web.xml 의 init-param 에 등록

    	<servlet>
    		<servlet-name>dispatcher</servlet-name>
    		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    		<init-param>
    			<param-name>contextClass</param-name>
    			<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>			
    		</init-param>
    		<init-param>
    			<param-name>contextConfigLocation</param-name>
    			<param-value>
    				config.MvcConfig
    				config.BeanConfig
    				config.웹소켓환결설정
    			</param-value>	
    		</init-param>
    		<load-on-startup>1</load-on-startup>
    		<multipart-config>
    	        <location>/upload</location>
    	        <max-file-size>5242880</max-file-size><!--5MB-->
    	        <max-request-size>20971520</max-request-size><!--20MB-->
    	        <file-size-threshold>0</file-size-threshold>
    	    </multipart-config>
    	</servlet>

    WebSocket 으로 채팅 구현하기

    1. client 상황 만들기(jsp 만들기)

    <script>
    var 웹소캣;
    var 별명;
    function 연결하다(){
    	//연결이 여기에 있으니 이 함수를 적용한 버튼부터 눌러야 웹소켓이 생성되고 session 으로 저장된다.
       웹소캣=new WebSocket("ws://localhost:8080/chat");
       웹소캣.onopen = function(){
    	  let txt별명 = document.querySelector("#txt별명");
    	  별명 = txt별명.value;
    	  // json 문자열화 해서 보내기
          웹소캣.send(JSON.stringify({'별명':별명,'상태':'입장'}));
       }
       웹소캣.onmessage=function(result){
          let 받은메세지 = result.data;
          let pMessage = document.querySelector("#pmessage");
          pMessage.innerHTML=pMessage.innerHTML+"<br>"+받은메세지;
       }
       웹소캣.onclose=function(){
    		웹소캣 = null;
       }
    }
    
    
    function 보내다(){
    	let txtmessage = document.querySelector("#txtmessage");
    	웹소캣.send(JSON.stringify({'별명':별명,'상태':'채팅',메세지:txtmessage.value}));
    }
    
    function 나가다(){
    	let txtmessage = document.querySelector("#txtmessage");
    	웹소캣.send(JSON.stringify({'별명':별명,'상태':'퇴장'));
    }
       
    
    </script>
    
    </head>
    <body>
    	별병
    	<input type="text" id="txt별명"><br>
    	<button onclick="연결하다()">연결</button><br>
    	<button onclick="나가다()">나가기</button><br>
    	메세지
    	<input type="text" id="txtmessage"/><button onclick="보내다()">전송</button><br>
    	
    	<!-- p 태그처럼 <p></p> 로 닫히는 태그들만 innerHTML 이 있다. input 태그는 value 로 -->
    	<p id="pmessage"></p>
    </body>
    ws : websocket 의 프로토콜 임을 알림(http 처럼) 이 통신규약으로 spring 의 TextWebSocketHandler 로 통신하고 알아서 close()함
    WebSocket : client 측의 socket 으로 연결을 걸 수는 있어도 받을 수는 없다.
    ( 연결을 거는 것이 메세지를 보내 답장을 받는 것이 아니고 처음에 통신연결을 거는것을 말한다. )
    spring 에서는 이 소켓을 session 이라고도 부른다 (HttpSession 과는 이름은 같은 session 이라도 다 다르다.)
    소켓을 처음에 받으면 session 을 저장하여 소켓이 계속 server 안에 있게 해야한다 (아래 예제)
    onopen : 연결이 되자마자 해당 함수 실행
    send : 해당 웹소켓에 데이터 보냄
    onmessage : 서버측에서 메세지가 오면(sendMessage()) 실행

    2. json 객체를 받을 클래스 생성

    public class 메세지Data {
    	String 메세지;
    	String 상태;
    	String 별명;
    	
    	public String get메세지() {
    		return 메세지;
    	}
    	public void set메세지(String 메세지) {
    		this.메세지 = 메세지;
    	}
    	public String get상태() {
    		return 상태;
    	}
    	public void set상태(String 상태) {
    		this.상태 = 상태;
    	}
    	public String get별명() {
    		return 별명;
    	}
    	public void set별명(String 별명) {
    		this.별명 = 별명;
    	}
    	
    }

    3. jsp 에서 메시지와 별명 등을 받아서 출력해줄 클래스

    //연결 및 메세지 처리기
    @Component
    public class MessageHandler extends TextWebSocketHandler{
    	
    	List<WebSocketSession> 연결자들 = new ArrayList<WebSocketSession>();
    	
    	//ws 프로토콜로 요청받을때마다 작동
    	@Override
    	protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
    		
    		String 메세지만 = message.getPayload();//json이 문자열화 된 것: "{별명:   ,상태:   ,}"
    
    		ObjectMapper objectMapper = new ObjectMapper();
    		// readValue() : 지금까지 ajax 통신값인 json문자열을 @RequestBody 붙은 객체의 멤버변수에 알아서 들어갔듯이 
    		// json 문자열을 해당 클래스의 멤버변수에 넣어주는 메서드
    		메세지Data 한메세지Data = objectMapper.readValue(메세지만, 메세지Data.class);
    		
    		TextMessage 보낼message = null;
    		
    		if(한메세지Data.get상태().equals("입장")) {
    			연결자들.add(session);
    			보낼message = new TextMessage(한메세지Data.get별명()+"님이 입장하였습니다.");
    			
    		}else if(한메세지Data.get상태().equals("채팅")) {
    			보낼message = new TextMessage(한메세지Data.get별명()+": "+한메세지Data.get메세지());
    			
    		}else if(한메세지Data.get상태().equals("퇴장")) {
    			보낼message = new TextMessage(한메세지Data.get별명()+"님이 퇴장하였습니다.");
    			
    		}
    		
    		for(WebSocketSession webSocketSession:연결자들) {
    			webSocketSession.sendMessage(보낼message);
    		}
    		//위의 매개변수인 session 은 List<WebSocketSession> 연결자들 처럼 모든 세션이 아니라
    		//이 요청을 한 본인만이다.
    		if(한메세지Data.get상태().equals("퇴장")) {
    			session.close();
    			연결자들.remove(session);
    		}
    		
    		//연결처리
    		//메세지 처리
    		
    		//끊기 처리
    	}
    }

    * 네트워크 형상관리
    형상관리 : 소프트웨어 생명주기의 단계적 산출물에 대한 가시성과 추적가능성을 체계화하는 품질보증 활동이다.
    ( 어떤 프로그램이 수정될 때 버전을 업그레이드 시킨다는 뜻으로 보면 될 듯 )
    1. 의미
    - 형상항목 : 버전을 바꿔야 할 대상 ( 버전을 관리할 대상 )
    - 베이스라인 : 기준선 (무조건 버전을 바꿔주는 것이 아닌 이 기준을 보며 판단)
    - 형상관리위원회 : 형상관리를 하는 곳 ( 팀내에서 팀장일 수도 있고 ..등등 )
    - CMDB(Configuration Management Database) : 버전이 어떻게 바뀌었는지 이력을 저장함 ( 히스토리 )
    2. 프로세스
    형상식별 -> 형상통제 -> 형상감사 -> 형상기록 의 절차를 가지고 있다.
    - 형상식별 : 버전을 관리할 (변경할) 대상이 있구나 식별.
    ( 형상항목을 식별 )
    - 형상통제 : 변경관리를 연계하여 수행해야 한다. 즉, 변경이 되었어도 그게 버전을 붙일만한 일인지 아직 그럴 정도의 변경이 아닌지 확인.
    ( 베이스라인이 쓰일 듯 하다. )
    - 형상감사 : 변경사항을 확인하고 검토한다.
    ( 형상관리위원회가 체크할 거 같다. )
    - 형상기록 : 형상관리한 히스토리를 남김
    ( CMDB가 쓰인다.)

    'JAVA' 카테고리의 다른 글

    VO 와 DTO  (0) 2021.08.03
    Mock-test  (0) 2021.07.27
    3Tier: 예외처리,@Qualifier  (0) 2021.07.27
    Spring:interceptor,3Tier(Presentation,Business,Dataservice)  (0) 2021.07.27
    Mybatis:설정하기  (0) 2021.07.27

    댓글

Designed by Tistory.