본문 바로가기

IT Trend

Chat 서비스 분석

- Chat 서비스는 말 그대로 채팅 서비스를 의미한다. 

- 수많은 SNS에서 쓰이는 chat서비스가 어떤 원리로 구동되는지 알아본다.



1. ChatClient


- 채팅서비스를 사용하게 되는 유저들이 쓰는 프로그램이다.

- 에코 서비스의 클라이언트 프로그램과는 다르게 ChatClient는 쓰레드를 가지고 있다. 

- ChatClient의 쓰레드는 유저가 문자열을 입력하는 동시에, 상대방이 보낸 문자를 읽을 수 있게 한다.

- 메소드 변수를 사용하여 프로그램 시작과 동시에 자신의 id와 ip주소를 입력한다.



<ChatClient 소스코드 (1)>


1) System.out.println("사용법 : java ChatClient id 접속할서버ip"); 

   System.exit(1);

   //id와 ip를 적으면 배열 개수가 2가 되기 때문에 만약 2가 아니라면 잘못입력 된것이므로 프로그램을 종료한다. 


2) sock = new Socket(args[1], 10001); //args[1]은 서버 ip이므로 소켓에 ip주소를 전달한다.

3) pw = new PrintWriter(new OutputStreamWriter(sock.getOutputStream())); //서버에 데이터를 전달할 수 있는 스트림을 생성한다.

4) br = new BufferedReader(new InputStreamReader(sock.getInputStream())); //서버로부터 데이터를 받아 올 수 있는 스트림을 생성한다.

5) BufferedReader keyboard = new BufferedReader(new InputStreamReader(System.in));//사용자가 콘솔상에서 직접 입력하는 스트림을 생성한다.


----------------------------------> 총 스트림을 3개 생성하는 것!!!

6) pw.println(args[0]); //서버로 사용자 id 전송!

7) InputThread it = new InputThread(sock, br); //클라이언트의 스레드이다. 데이터를 받으면서 동시에 문자를 입력하도록 한다.

8) while((line = keyboard.readLine()) != null){ // 사용자가 입력한 문자열을 입력받아 서버로 전송하는 역할을 한다.

9) pw.println(line); //pw 서버로 이어지는 출력 스트림.

10) if(line.equals("/quit")){ //만약 사용자가 /quit를 입력했다면, endflag를 true로 바꾸고 while문을 빠져나온다.







<ChatClient 소스코드(2)>


/* InputThread는 클라이언트 프로그램이 입력을 하는 동시에 데이터를 받을 수 있게 해준다.

 * 이 쓰레드는 들어오는 데이터를 계속해서 받아주는 역할을 한다.

 * 왜 스트림이 3개나 생성되었는지 이해하면 쉽다.

 */


1) public InputThread(Socket sock, BufferedReader br) 

//생성과 동시에 소켓 넘버와 서버로부터 데이터를 읽어오는 입력 스트림의 주소를 받는다.

2) while((line = br.readLine()) != null) //서버로부터 데이터를 읽음과 동시에 화면에 출력해준다.



2. ChatServer


- ChatServer 또한 마찬가지로 메인서버는 클라이언트의 접속을 받고 쓰레드를 할당해준다.

- 클라이언트가 실행될 때 마다 쓰레드가 생성되어 서버의 일을 수행한다.

- ChatServer의 특징은 귓속말이 가능하고,  여러사람들과 채팅이 가능하다는 점이다.

- 특히 유저가 문자를 보냈을때 다른사람의 채팅창에 출력이 되는 broadcast 기능이 있는 메소드가 추가되어있다.

- 귓속말이 가능 한 sendmsg 메소드가 있다.

- 컬렉션 프레임워크 중 하나인 hashmap에 대한 이해가 필요하다.



<ChatServer 소스코드 (1) >


/* 기존의 EchoServer와 비슷하다.*/



1) ServerSocket server = new ServerSocket(10001); //서버 소켓 인스턴스 생성! 소켓넘버를 파라미터로 받고 클라이언트의 접속을 확인해준다.


2) HashMap hm = new HashMap(); //해시맵 생성!


3) Socket sock = server.accept(); // 클라이언트의 접속을 확인하고 동시에 소켓인스턴스를 생성한다.

4) ChatThread chatthread = new ChatThread(sock, hm); //서버 프로그램의 스레드인 Chatthread를 생성한다.





<ChatServer 소스코드 (2) >


/* 생성자의 파라미터로 소켓 넘버와 해시맵 참조값을 전달한다. */


1) PrintWriter pw = new PrintWriter(new OutputStreamWriter(sock.getOutputStream())); //클라이언트에게 데이터를 전달 해주는 출력 스트림 생성!


2) br = new BufferedReader(new InputStreamReader(sock.getInputStream())); //클라이언트로 부터 데이터를 받을 입력스트림 생성!

3) id = br.readLine(); // 맨 첫번째 배열이었던 id를 읽어와 저장한다. 

4) broadcast(id + "님이 접속하였습니다."); // 같이 채팅하는 유저들에게 알린다.

5) System.out.println("접속한 사용자의 아이디는 " + id + "입니다.");


/* 해시맵에 id와 서버의 출력스트림 참조값을 저장한다.

 

* 만약 스레드가 동시에 해시맵을 참조한다면, 충돌가능성이 있기 때문에 동기화를 시켜준다.

 

*/

6) synchronized(hm){ hm.put(this.id, pw); }






<ChatServer 소스코드 (3) >



1) while((line = br.readLine()) != null) // 문자열을 읽어온다.


2) if(line.equals("/quit")) //사용자가 종료를 선언하는 문자열을 입력했을때 while문을 빠져나온다.


3) if(line.indexOf("/to ") == 0)

// 해당 문자열이 첫번째 글자의 위치 번호를 반환한다. 만약 "/to " 가 첫번째에 있다면 "/"의 위치 번호 0을 반환한다.  

4) sendmsg(line); // 귓속말 메소드에 전달

5) broadcast(id + " : " + line); // 위의 어느 조건도 만족하지 않는 다면 채팅방에 문자를 출력한다.






<ChatServer 소스코드 (4) >


/* 귓속말을 보내는 메소드 sendmsg */

1) int start = msg.indexOf(" ") +1; // " "이 위치하고 있는 번호에 1을 더해 " " 다음이 시작지점임을 저장한다. 여기서는 "/to "<이부분>

2) int end = msg.indexOf(" ", start); // start이후 그 다음 " "가 나오는 곳의 위치번호를 저장하여 끝을 알린다. 

3) String to = msg.substring(start, end); //처음번호와 과 끝번호 사이에 저장되어있는 문자를 출력한다. 여기서는 유저의 id를 의미한다.

4) String msg2 = msg.substring(end+1); // 끝번호 다음 부터 메세지가 입력 되므로 그다음부터 끌까지의 문자열을 ms2에 저장한다.

5) Object obj = hm.get(to); //to에 해당하는 데이터 즉 출력스트림의 참조값을 오브젝트 인스턴스에 저장. 모든 클래스가 Object 클래스를 상속하기 때문에 가능.


6) PrintWriter pw = (PrintWriter)obj; // 해당 참조값을 pw에 저장

7) pw.println(id + " 님이 다음의 귓속말을 보내셨습니다. :" + msg2); //해당 출력스트림을 가지고 있는 사람에게 귓속말 전달



/* 채팅에 접속하고 있는 유저 전체에 문자 보내는 메소드 broadcast */


1) Collection collection = hm.values(); //해시맵에 저장되어 있는 모든 출력스트림의 참조값을 collection에 저장한다. 

2) Iterator iter = collection.iterator(); //iterator를 생성하여 각각의 데이터를 참조하여 잡근하도록 한다.


/* 결국 해시맵에 저장되어 있는 모든 사람에게 메세지를 전잘하는 것이다.

                * 해당 데이터 즉, 출력스트림으로 파라미터로 전달받은 메세지를 전송한다.

                */

3) while(iter.hasNext()) //데이터가 있다면, PrintWriter pw = (PrintWriter)iter.next(); // 참조변수를 pw에 저장한다. 


4) pw.println(msg); // 메세지 전체 전달



'IT Trend' 카테고리의 다른 글

EchoServer 분석소스  (0) 2016.12.12
EchoClient 분석소스  (0) 2016.12.12
에코 서비스 분석  (0) 2016.12.12
객체란 무엇인가?  (0) 2016.10.10
객체지향으로 무인시스템 설계하기  (0) 2016.10.10