프로젝트 복습.. 끄적이며

지환·2023년 11월 3일
0

JAVA

목록 보기
39/39
post-thumbnail

내가 궁금한 부분을 적는다.

  1. initDisplay -> init() 하는 부분 시점의 차이다. 이해 완료
  2. socketclinet 와 socketclinetthread가 있다. 근데 지금 내가 궁금한 건
    소켓클라이언트에서 -> 소켓클라이언트스레드[동직]를 한다 UI와 관련된 동작을 한다.
    여기 함수로 본다면 initDisplay와 init() 이야

내가 궁금한건 그러면

SocketServer와 SocketServerThread가 있는데,
SocketServer도 UI가 구현이 되어 있고 소켓클라이언트도 UI가 구현이 되어 있는데

  1. SocketClinet에서 하는 동작을 SocketClinetThread에서 run 메소드를 통해 스레드를 동작하게 한다.
    그렇기 떄문에 SocketClient에서 하는 행동들을 SocketClinetThread에서 구현하고

  2. SocketServer에서 하는 내부 동작들을 SocketServerThread에서 구현한다. 이 부분도 동일하게 소켓클라이언트에게 띄워주는 것이 아니라
    소켓서버UI에 띄워준다.

  3. 자, 그러면 소켓서버는 클라이언트의 정보를 어떻게 얻어와 ?
    -> server.accept() 이 부분에서 클라이언트의 정보를 얻어와 그래서 그 정보를 가지고 SocketServerThread에서 동작하게끔 하는거지

클라이언트 스레드와 서버스레드가 존재하잖아?

  • 두 스레드의 역할에 대해서 생각해 볼 필요성이 있다.

  • 우리가 소켓을 연결하고 소통하는 곳은 어디야? - 소켓클라이언트와 / 소켓서버야

  • 소켓은 여기로 돌아다니는건 맞아, 왜냐하면 Server = accept.()정보-> 클라이언트
    그리고 그걸 가지고 -> 서버스레드를 돌려 그렇게 된다면 서버스레드에선 이 정보를 쥐고 동작을 진행하겠지
    socket = server.accept();
    SocketServerThread tst = new SocketServerThread(this);

  • 이렇게 해당 클라이언트의 정보를 넘겨준다.[핵심 포인트 중 하나다]

경유해서 가는거지 -> 클라이언트(소켓)+ 소켓서버 실제 동작 하는 것은 스레드에서 한다.
단지 그 해당 정보를 읽어 오는 것(소켓서버와 소켓 클라이언트에서 진행된다.)

UI는요? 각각의 동작을 해당 스레드에서 처리한다.
클라이언트에서 소켓을 서버로 보내 -> socket = new Socket("172.30.1.66",3002); -> 서버측 포트넘버를 작성하여 바인딩한다.
그리고 oos 말한다 ois 듣는다. 라고 생각하자. 소켓 연동이 되고 서버에서는 이 클라이언트의 정보를

socket = server.accept(); 받아 들인다. -> 이렇게 되고 나서 클라이언트는 뭘 하냐면 말한다(oos.writeObject) 이 부분이 클라이언트가 서버로 데이터를 전송하는 부분이다.

  • 그렇게 한 다음에 클라이언트 스레드가 시작된다.

여기서 시점의 문제가 하나 더 발생한다. -> 말하고 -> 스레드를 동작시킨다. 그렇게 된다면 서버가 받고 먼저 해당 소켓 부분을 읽어들이고 -> 클라이언트 UI창에 해당 부분이 떠야지 맞는 것이다.

서버 연결 먼저함 -> 이제 클라이언트 부른다. -> 그렇게 되면 서버가 먼저 시작되고
클라이언트가 뒤이에 ~~~가 입장하였습니다 라고 나온다.

그러면 서버스레드쪽에서 oos.writeObject라고 쓴 부분은 -> 클라이언트 스레드에 보낸거네?

	this.ss = ss;
	this.client = ss.socket

이 두개는 다르다. this.ss는 서버의 기능을 쓸 수 있는 것이고,
ss.socket은
-> 서버의 기능 + 클라이언트의 정보를 듣고 ss.socket은 말 할 수 있다.

이제 SocketClientThread를 복습해보자.

  • tc는 소켓클라이언트의 정보를 쥐고있다. 다른 말로 하자면, tc에는 oos와 ois가 존재한다. 그렇기 때문에 소켓클라이언트에서 읽어들인 서버의 정보를 ois.readObject() 하게 된다면 서버가 보낸 내용들을 읽을 수 있게 된다. == 양뱡향 통신을 할 수 있게 된다.

적용 사례는

msg = (String)tc.ois.readObject();
  • 만약에 100#키위#안녕하세요 이렇게 보냈다고 가정 했을 때
st = new StringTokenizer(msg,"#");

이 부분으로 인해 #을 기준으로 문자열을 슬라이싱 하게 된다. 근데 프로토콜에서 st.nextToken()를 해버렸기 때문에

100#키위#안녕하세요` -> 키위#안녕하세요 << 만 남게된다.

만약에 case 100번일 경우

에 닉네임인 키위를 읽어들이게 되고, 그 키위를 tc(클라이언트UI담당).jta_display.append하게 된다. -> 쉽게 생각해서 화면에 띄우게 된다.

그리고 자바 컬렉션 프레임워크인 벡터 객체를 생성해서 닉네임을 추가해준다.

만약에 case 201번일 경우

이것도 동일하다 닉네임을 읽어오고 (st.nextToken())으로 메세지도 읽어온 다음에 해당 닉네임과 메세지를 동일하게 붙인다.

만약에 case 202:번일 경우

해당 부분은 총 3가지로 구성되고 닉네임과, 바꿀 닉네임, 메세지이며 사용자는

이렇게 되면 자 우리는 프로토콜을 설계했는데
만약에 이렇게 된다면 메세지 부분에 afterName이 들어갈 수 있는거 아냐? 라고 할 수 있다.

100#키위#안녕하세요

라고 했다고 가정해보자.

앞에서 프로토콜로 100번은 짤라갔고

a님이 입장하였습니다.
202#a#ㅁㄴㅇ#a의 대화명이 ㅁㄴㅇ으로 변경되었습니다.

대화명 변경을 하는 순간
이렇게 나온다.

202-프로토콜
a-기존이름
ㅁㄴㅇ-메세지
#기준으로 옆 대화가 afterName이 된다.

서버스레드 코드

					case 202:{
						String nickName = st.nextToken();
						String afterName = st.nextToken();
						String message = st.nextToken();
						this.chatName = afterName;
						broadCasting(202
								+"#"+nickName
								+"#"+afterName
        						+"#"+message);
					}break;

닉네임 변경 부분이다

소켓클라이언트스레드 코드

					case 202:{
						String nickName = st.nextToken();
						String afterName = st.nextToken();
						String message = st.nextToken();
						//테이블에 대화명 변경하기
						for(int i=0;i<tc.dtm.getRowCount();i++) {
							String imsi = (String)tc.dtm.getValueAt(i, 0);
							if(nickName.equals(imsi)) {
								tc.dtm.setValueAt(afterName, i, 0);
								break;
							}
							/**
  • 닉네임 변경을 누르는 순간 이벤트처리
		else if(jbtn_change == obj) {
			String afterName = JOptionPane.showInputDialog("변경할 대화명을 입력하세요.");
			if(afterName == null || afterName.trim().length()<1) {
				JOptionPane.showMessageDialog(this
				, "변경할 대화명을 입력하세요"
				, "INFO", JOptionPane.INFORMATION_MESSAGE);
				return;
			}
			try {
				oos.writeObject(202
						   +"#"+nickName
						   +"#"+afterName
						   +"#"+nickName+"의 대화명이 "+afterName+"으로 변경되었습니다.");
			} catch (Exception e) {
				// TODO: handle exception
			}
		}
	}//////////////////////end of actionPerformed
  • afterName에 변경할 대화명에 대한 값을 입력 받는다.
				oos.writeObject(202
						   +"#"+nickName
						   +"#"+afterName
						   +"#"+nickName+"의 대화명이 "+afterName+"으로 변경되었습니다.");

이렇게 소켓서버측에 보낸다. oos로 그렇게 되면 소켓서버측에선 해당 내용을 ois로 서버스레드에서 처리하게 된다.
그렇게 되면

다시 슬라이싱 시작
서버측 스레드

					case 202:{
						String nickName = st.nextToken();
						String afterName = st.nextToken();
						String message = st.nextToken();
						this.chatName = afterName;
						broadCasting(202
								+"#"+nickName
								+"#"+afterName
        						+"#"+message);
					}break;
  • 202: 프로토콜
  • nickname : 기존 닉네임을 받고
  • aftername : 변경할 닉네임을 받는다.
  • nickName+"의 대화명이 "+afterName+"으로 변경되었습니다. 시전

서버측 스레드에서 이걸 읽는 것이다. 그렇게 되면 서버측 스레드에 브로드캐스팅이라고 채팅방 안에 들어와 있는 모든 사람들에게 이 정보를 알린다.

	//현재 입장해 있는 친구들 모두에게 메시지 전송하기 구현
	public void broadCasting(String msg) {
		for(SocketServerThread sst:ss.globalList) {
			sst.send(msg);
		}
	}

그리고 나서 send를 하게 되는데

	public void send(String msg) {
		try {
			oos.writeObject(msg);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

이렇게 또 oos로 클라이언트측 스레드로 보내는 것이다.

구체적으로 설명하면 클라이언트측에서 값을 받고 ois 그리고 그 값을 Thread(this)를 시킴으로써 정보를 같이 공유하게 된다. 그렇게 되면 클라이언트 스레드에서 작동한다

  • 클라이언트
public void init() {
		try {
			//서버측의 ip주소 작성하기
			socket = new Socket("172.30.1.66",3002);
			oos = new ObjectOutputStream(socket.getOutputStream());
			ois = new ObjectInputStream(socket.getInputStream());
			oos.writeObject(100+"#"+nickName); //클라이언트에서 서버로 데이터를 전송하는 부분
			SocketClientThread tct = new SocketClientThread(this);
			tct.start();
		} catch (Exception e) {
			System.out.println(e.toString());
		}
	}

읽어들인 클라이언트 스레드에서 동작하게 되는데 서버로부터 뭘 받았다?

						broadCasting(202
								+"#"+nickName
								+"#"+afterName
        						+"#"+message);
					}break;

이 저장된 값을 받은 것이다. 그렇게 되면 이제 이 정보를 가지고 클라이언트 스레드에서 작동을 시작한다.

다시 슬라이싱 시작

프로토콜 자르고 : 202
닉네임 자르고 : 기존 닉네임
바꿀 닉네임 : 바꿀 닉네임
메세지 : 메세지

를 쥐고 프로토콜을 동작을 수행한다. 이 부분을 보면 첫 번째, 클라이언트에서 액션처리 -> 소켓서버 -> 소켓서버 스레드 case 202 -> 브로드 캐스팅 ->샌드 -> 소켓서버스레드 -> 해당 프로토콜 처리 이런식으로 진행한다.

그런 다음에 for문을 수행하는데 row의 수만큼 진행한다. tc.dtm.getRowCount는 tc(클라이언트).dtm(데이터를 쥐고 있는).getRowCount -> 로우의 수를 센다. 라는 의미다.

그 값을 읽어들인 부분이 (String)tc.dtm.getValueAt(i, 0); 이렇게 수행하게 되면

이렇게 읽어들이고 그 값을 imsi라는 변수에 담는다. 그런다음
닉네임과 imsi가 같으면(읽어들인 값) 여기서

닉네임은 서버로부터 받은 닉네임

imsi는 테이블로부터 읽어들인 닉네임 값 이 동일한지 비교하고 같다면 -> afterName으로 setValueAt한다.

만약에 210 : 일 경우 (삭제)

이 부분도 동일하게 작동한다.

클라이언트 UI에서 해당 동작하는 이벤트 처리를 했을 때 해당 클라이언트 클래스에선 이 동작으로 인한 값들을 oos.writeObject한다. -> 서버스레드에서 읽고 -> 서버쪽 프로토콜 브로드캐스팅 하고 -> 샌드 -> 클라이언트쪽으로 보내 -> 클라이언트 스레드에서 이 부분을 프로토콜에 맞게끔 해당 위치로 이동하고(case) -> 이 작업을 수행한다.
이 작업은 밑에 보이는 작업이다.

String nickName = st.nextToken();
sc.jta_display.append(nickName+"님이 회원탈퇴 했습니다.\n");
c.jta_display.setCaretPosition
(sc.jta_display.getDocument().getLength());
	for(int i=0;i<sc.dtm.getRowCount();i++) {
			String n =(String)sc.dtm.getValueAt(i, 0);
				if(n.equals(nickName)) {
						sc.dtm.removeRow(i);

이 닉네임들은 어떻게 알아요?

클라이언트 쪽에서 해당 정보를 넘겨준다. ->

		else if (jbtn_del == obj) {
			 deleteSelectedRow();

			String del = "회원탈퇴.";
			try {
				oos.writeObject(210
						   +"#"+nickName
						   +"#"+del);
				jtf_msg.setText("");
			}
			catch (Exception e) {
				// TODO: handle exception
			}

이 부분을 넘겨주겠지 -> 210#닉네임#"회원탈퇴"
이걸 가지고 -> 서버스레드에 보내준다

						String nickName = st.nextToken();
						String message = st.nextToken();
						String del = "회원탈퇴 했습니다.";
						broadCasting(210
								+"#"+nickName + "#" + del);
						oos.writeObject(210 + "#" + nickName + "#"+ del);
						LogoutRequest(nickName);

앞에서 프로토콜에서 숫자 짤리고 -> 닉네임으로 nickname 해당 값 받고 -> message(회원탈퇴)로 전달된다. 이 부분을 브로드캐스팅하여 샌드한다(보낸다->클라이언트쪽에다)

밑을 보면 oos.writeObject -> 삭제 하고
LogoutRequest보자.

	public void LogoutRequest(String nickName) {
	    try {
	    	System.out.println(nickName);
	        String message = nickName + "님이 탈퇴하였습니다.";
	        send(210 + "#" + nickName + "#" + message);
	        // 사용자 목록 업데이트
	        removeList(nickName);
	    } catch (Exception e) {
	        e.printStackTrace();
	    }
	}

이 부분도 수정해야된다. 닉네임을 받고 문제는 여기서 또 샌드를 한다는 것이다. send를 해버려서 또 클라리언트 쪽에다가 보낸다. 이 부분을 수정해야된다. -> 일단 그 내용을 샌드쪽으로 해서 보냈고, -> removeList를 수행하는데 -> 이 부분은 클라이언트스레드를 관리하는 소켓서버 리스트에 저장 되어 있는 부분을 삭제하는 역할이다. --- > 서버 스레드 끝

	public void removeList(String nickName) {
	    for (SocketServerThread sst : ss.globalList) {
	        if (sst != null && sst.chatName.equals(nickName)) {
	            ss.globalList.remove(this);
	            break;
	        }
	    }
	}

removeList 하게 된다면 닉네임을 입력 값으로 받게 된다. 소켓스레드에서 : ss.globalist(소켓클라이언트스레드가 저장되어 있는 곳)을 돌면서 소켓클라이언트를 순회한다. 회원탈퇴 기능은 본인을 탈퇴 시키는 것이기 떄문에 ss.globalList.remove에 (this)를 넣어준다.

					case 210:{
						String nickName = st.nextToken();
						sc.jta_display.append(nickName+"님이 회원탈퇴 했습니다.\n");
						sc.jta_display.setCaretPosition
						(sc.jta_display.getDocument().getLength());
						for(int i=0;i<sc.dtm.getRowCount();i++) {
							String n =(String)sc.dtm.getValueAt(i, 0);
							if(n.equals(nickName)) {
								sc.dtm.removeRow(i);
							}

그리고 소켓클라이언트에게 이 정보를 전송하면 그걸 스레드에서 처리한다. 그렇게 되면 서버에서 보낸 정보를 210번 프로토콜(case:210)에 들어가고 해당 부분은 열을 삭제하는 부분이다.

	//클라이언트에게 말하기 구현
	public void send(String msg) {
		try {
			oos.writeObject(msg);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}/////////////////end of send
	
	

이렇게 send에서 보낸 값들을 받게 된다. msg는 무엇일가?
msg = (String)ois.readObject(); ->(소켓서버스레드)
읽은 값이 메세지인데 2가지가 있다. 하나는 브로드캐스트로 이 메세지를 -> 소켓클라이언트에게 보낸다. 이걸 스레드에서 처리한다.

리팩토링 대상

		else if(jbtn_exit==obj) {
			try {
				logout();
			    try {
			        oos.writeObject(210 + "#" + nickName + "#");
			    } catch (IOException e) {
			        e.printStackTrace();
			    }
			    
				dispose();
			} catch (Exception e) {
				
			}
		}
  • 해당 부분은 지금 동일한 프로토콜과 닉네임을 사용하고 있으므로 이 부분은 뺴줘도 상관없다. --> 아직 반영 안함
    로그아웃 하였습니다 라고 리팩토링을 진행해야 된다.

  • 프로토콜 로그아웃 하나 설계해서 -> 스레드에 추가만 해주면 된다. 서버랑 클라이언트에 전송만 할 수 있게끔 하면 된다.

삭제처리 서버스레드에 어차피 샌드에서 oos 해주고 있는데 굳이 이걸 왜 하니?

					case 210:{
//						String nickName = st.nextToken();
//						ss.globalList.remove(this);
//						broadCasting(500
//								+"#"+nickName);
						String nickName = st.nextToken();
						String message = st.nextToken();
						String del = "회원탈퇴 했습니다.";
						broadCasting(210
								+"#"+nickName + "#" + del);
						oos.writeObject(210 + "#" + nickName + "#"+ del);
						LogoutRequest(nickName);
						// 이 부분에서 refresh해주는

oos.writeObject(210 + "#" + nickName + "#"+ del); 이 부분 뺴라. --> 해당부분 처리완료

profile
아는만큼보인다.

0개의 댓글