카테고리 없음

[오픈소스] 3. Chat 소스 분석 2

hyerm_2 2019. 5. 17. 00:08
반응형
SMALL

<ChatServer.java>

import java.net.*;

import java.io.*;

import java.util.*;

 

public class ChatServer {

 

    public static void main(String[] args) {

        try{ //try를 통하여 문제 상황이 있을경우 catch를 통해 보내준다.

            ServerSocket server = new ServerSocket(10001); //ServerSocket(자바에서 서버 프로그램을 개발할 때 쓰임) 클래스로 객체 "server"를 만들었다. 포트번호를 "10001"로 할당하였다.

            System.out.println("Waiting connection..."); //"연결을 기다리라는" 메세지를 출력한다.

            HashMap hm = new HashMap(); //HashMap(키와 데이터 값의 한쌍으로 묶어서 관리하며 키의 중복을 허용하지 않음)클래스로 객체 "hm"을 만들었다.

            while(true){ //true이면 while문을 반복한다.

                Socket sock = server.accept(); //Socket(Client에서 서버로 접속하거나 Server에서 accpet하는데 필요) 클래스를 "sock"으로 선언하는데, ServerSocket의 객체 server로 accept함수를 호출한다.

                                             //accept(Client가 들어오는 것을 대기함,Client가 해당포트로 연결 시도하면, "accept"메소드는 "Socket"클래스를 생성하여 반환함)

   //"sock"이 Client와 1:1 연결된 소켓

                ChatThread chatthread = new ChatThread(sock, hm); //ChatThread 클래스의 "chatthread" 객체를 만들어주는데, 파라미터로 Socket과 HashMap을 보낸다.

                chatthread.start(); //ChatThread 클래스를 시작한다.

            } // while

        }catch(Exception e){

            System.out.println(e);

        } //catch를 통하여 오류 상황을 잡아주며, 오류 메세지를 출력한다.

    } // main

}

 

class ChatThread extends Thread{ //Thread 클래스를 상속받는 ChatThread를 선언한다.

    private Socket sock; //Socket(Client에서 서버로 접속하거나 Server에서 accpet하는데 필요) 클래스를 "sock"으로 선언한다.

    private String id; //String 타입의 "id"를 선언한다.

    private BufferedReader br; //BufferedReader(문자 입력 스트림으로부터 문자들을 읽어 들임) 클래스를 "br"로 선언한다.

    private HashMap hm; //HashMap(키와 데이터 값의 한쌍으로 묶어서 관리하며 키의 중복을 허용하지 않음)클래스로 객체 "hm"을 만들었다.

    private boolean initFlag = false; //boolean 타입의 "endflag"를 선언한다.

    public ChatThread(Socket sock, HashMap hm){ //ChatThread클래스의 생성자 이다.

        this.sock = sock; //파라미터로 Socket "sock"이 들어왔을때, 이 클래스 내에 선언된 "sock"에 할당한다.

        this.hm = hm; //파라미터로 HashMap "hm"이 들어왔을때, 이 클래스 내에 선언된 "hm"에 할당한다.

        try{ //try를 통하여 문제 상황이 있을경우 catch를 통해 보내준다.

            PrintWriter pw = new PrintWriter(new OutputStreamWriter(sock.getOutputStream())); //OutputStreamWriter와 sock.getOutputStream을 사용하여 Socket에 출력할 수 있는 PrintWriter의 새로운 객체를 만들어준다.

            br = new BufferedReader(new InputStreamReader(sock.getInputStream())); //InputStreamReader와 sock.getInputStream을 사용하여 Socket으로 부터 읽을 수 있BufferedReader의 새로운 객체를 만들어준다.

            id = br.readLine(); //String 타입의 "id"를 통하여 BufferedReader로 String을 읽어들인다.

            broadcast(id + " entered."); //broadcast의 메소드로 "id"와 "entered"라는 파라미터를 보내준다.

            System.out.println("[Server] User (" + id + ") entered."); // "사용자가 들어왔다" 는 메세지를 보내준다.

            synchronized(hm){ //한 Thread만이 접근할 수 있도록 lock을 걸어준다.

                hm.put(this.id, pw); //HashMap에 put 함수를 통하여 "id"와 PrintWriter를 보내준다.

            }

            initFlag = true; //boolean 타입의 initFlag를 true로 바꿔준다.

        }catch(Exception ex){

            System.out.println(ex);

        } //catch를 통하여 오류 상황을 잡아주며, 오류 메세지를 출력한다.

    } // construcor

    public void run(){ //run이라는 함수를 실행한다. (BufferedReader을 통해 읽은 line을 출력하는 함수)

        try{

            String line = null;

            while((line = br.readLine()) != null){ //try를 통하여 (문제 상황이 있을경우 catch를 통해 보내줌) String 클래스타입의 "line"을 BufferedReader를 통하여 읽고, 반복문을 통하여 이것이 null 이 될때 까지 실행한다.

                if(line.equals("/quit")) //"line"이 "quit"과 동일하다면, 반복문을 종료한다.

                    break;

                if(line.indexOf("/to ") == 0){ //"line"의 "to"라는 인덱스가 0일 경우, (indexOf메소드는 해당 문자의 인덱스를 출력함.)

                    sendmsg(line); //sendmsg 메소드에 "line"을 보낸다.

                }else

                    broadcast(id + " : " + line); //그렇지 않으면, broadcast 메소드에 "id"와 "line"을 보내준다.

            }

        }catch(Exception ex){

            System.out.println(ex); //catch를 통하여 오류 상황을 잡아주며, 오류 메세지를 출력한다.

        }finally{

            synchronized(hm){ //한 Thread만이 접근할 수 있도록 lock을 걸어준다.

                hm.remove(id); //HashMap에 remove 함수를 통하여 "id"를 삭제한다.

            }

            broadcast(id + " exited."); //broadcast메소드에 "id"와 함께 "exited"라는 문자열을 파라미터로 보낸다.

            try{

                if(sock != null)

                    sock.close();

            } //Socket이 null이 아니면 Socket을 close한다.

            catch(Exception ex){} //catch를 통하여 오류 상황을 잡아준다.

        }

    } // run

    public void sendmsg(String msg){ //sendmsg라는 메소드를 선언한다.

        int start = msg.indexOf(" ") +1; //파라미터로 들어온 msg의 " " 인덱스+1을 "start"라는 정수타입에 수를 넣어준다.

        int end = msg.indexOf(" ", start); //파라미터로 들어온 msg의 " " 부터 start까지의 index 를 "end"라는 정수타입에 수를 넣어준다.

        if(end != -1){ //"end"가 -1일 경우, 즉 "msg"가 없을 경우,

            String to = msg.substring(start, end); //String 타입의 "to"에 "msg"의 substring(첫번째 인수부터 두번째 인수까지의 문자열을 읽어냄) 메소드를 통하여 "start"부터 "end"까지의 문자열을 넣어준다.

            String msg2 = msg.substring(end+1); //String 타입의 "msg2"에 "msg"의 "end+1"번째 까지의 문자열을 넣어준다.

            Object obj = hm.get(to); //Object타입의 "obj"를 HashMap "hm"을 get 메소드(지정한 키에 해당하는 데이터 반환)를 통해 "to"에 해당하는 데이터를 넣어준다.

            if(obj != null){ //Object 타입의 "obj"가 null이 아닐때,

                PrintWriter pw = (PrintWriter)obj; //PrintWriter 타입의 "pw"를 "obj"로 할당한다.

                pw.println(id + " whisphered. : " + msg2); //PrintWriter을 통하여 이 메세지를 출력한다.

                pw.flush(); //PrintWrter의 버퍼를 비워준다.(flush 함수는 버퍼를 비우는 함수로, close와 함께 쓴다.)

            } // if

        }

    } // sendmsg

    public void broadcast(String msg){ //broadcast라는 메소드를 선언한다.

        synchronized(hm){ //한 Thread만이 접근할 수 있도록 lock을 걸어준다.

            Collection collection = hm.values(); //Collection클래스의 "collection"을 선언해주는데, 이에 values(HashMap의 모든 값들 반환) 메소드를 통하여 HashMap의 값들을 넣어준다.

            Iterator iter = collection.iterator(); //Iterator클래스의 "iter"을 선언해주는데, Collection의 iterator로 할당한다.

            while(iter.hasNext()){ //"iter"가 다음 내용이 있을 때 까지,

                PrintWriter pw = (PrintWriter)iter.next(); //PrintWriter타입의 "pw"를 "iter"의 다음 값으로 할당한다.

                pw.println(msg); //"msg"를 PrintWriter을 통하여 출력한다.

                pw.flush(); //PrintWrter의 버퍼를 비워준다.(flush 함수는 버퍼를 비우는 함수로, close와 함께 쓴다.)

            }

        }

    } // broadcast

}

 

 

<ChatClient.java>

import java.net.*;

import java.io.*;

 

public class ChatClient {

 

    public static void main(String[] args) {

        if(args.length != 2){ //실행시 입력하는 String "args"의 길이가 2가 아니면 (즉, 입력 되지 않았으면)

            System.out.println("Usage : java ChatClient <username> <server-ip>"); //다음 내용을 출력한다.

            System.exit(1); //시스템을 종료한다.

        }

        try{Socket sock = null; //Socket(Client에서 서버로 접속하거나 Server에서 accpet하는데 필요) 클래스를 "sock"으로 선언한다.

        BufferedReader br = null; //BufferedReader(문자 입력 스트림으로부터 문자들을 읽어 들임) 클래스를 "br"로 선언한다.

        PrintWriter pw = null; //PrintWriter(Print(), Println() 메소드를 사용해 출력) 클래스를 "pw"로 선언한다.

        boolean endflag = false; //boolean 타입의 "endflag"를 선언한다.

 

            sock = new Socket(args[1], 10001); //Socket의 새로운 객체를 만들어주는데, 실행시 입력한 "args"와, 포트번호 10001을 넣어준다.

            pw = new PrintWriter(new OutputStreamWriter(sock.getOutputStream())); //OutputStreamWriter와 sock.getOutputStream을 사용하여 Socket에 출력할 수 있는 PrintWriter의 새로운 객체를 만들어준다.

            br = new BufferedReader(new InputStreamReader(sock.getInputStream())); //InputStreamReader와 sock.getInputStream을 사용하여 Socket으로 부터 읽을 수 있BufferedReader의 새로운 객체를 만들어준다.          

            BufferedReader keyboard = new BufferedReader(new InputStreamReader(System.in)); //InputStreamReader을 사용하여 입력하는 것으로부터 읽어들일 BufferedReader를 선언한다.

            // send username.

            pw.println(args[0]); //PrintWriter을 사용하여 Socket에 입력한 "args" 를 출력한다.

            pw.flush(); //PrintWrter의 버퍼를 비워준다.(flush 함수는 버퍼를 비우는 함수로, close와 함께 쓴다.)

            InputThread it = new InputThread(sock, br); //InputThread 함수 "it"을 선언하는데, 파라미터로 Socket과 BufferedReader를 보내준다.

            it.start(); //InputThread를 시작한다.

            String line = null; //String 클래스의 "line"을 선언한다.

            while((line = keyboard.readLine()) != null){ //앞에서 선언한 BufferedReader 클래스의 "keyboard"를 사용하여 String을 읽어 들이는데, 이것이 null일때 까지 반복한다.

                pw.println(line); //PrintWriter을 사용하여 "line"을 출력한다.

                pw.flush(); //"PrintWriter"의 버퍼를 지운다.

                if(line.equals("/quit")){ //읽어들인 "line"이 "quit"이라는 String과 동일 할 경우,

                    endflag = true; //boolean 타입의 endflag를 true로 바꾸고,

                    break; //반복문을 빠져 나온다.

                }

            }

            System.out.println("Connection closed."); //반복문이 종료 된후, "연결이 종료" 되었다는 메세지를 출력한다.

        }catch(Exception ex){ //예외가 발생하였을 때 (catch를 사용),

            if(!endflag) // 만약 boolean 타입의 "endflag"가 false이면,

                System.out.println(ex); //오류 메세지를 출력한다.

        }finally{

            try{

                if(pw != null)

                    pw.close();

            }

            catch(Exception ex){} //catch를 통하여 오류 상황을 잡아준다.

            try{

                if(br != null)

                    br.close();

            } //try를 통하여 (문제 상황이 있을경우 catch를 통해 보내줌) BufferedReader가 null이 아니면, BufferedReader를 close한다.

            catch(Exception ex){} //catch를 통하여 오류 상황을 잡아준다.

            try{

                if(sock != null)

                    sock.close();

            } //try를 통하여 (문제 상황이 있을경우 catch를 통해 보내줌) Socket이 null이 아니면, Socket을 close한다.

            catch(Exception ex){} //catch를 통하여 오류 상황을 잡아준다.

        } // finally - catch문 뒤에 쓰인다. (exception이 throw 되었는지, 필요한 catch가 존재하는지에 대한 역할을 함)

    } // main

} // class

 

class InputThread extends Thread{ //Thread 클래스를 상속받는 InputThread 클래스를 만들어준다.

    private Socket sock = null; //Socket(Client에서 서버로 접속하거나 Server에서 accpet하는데 필요) 클래스를 "sock"으로 선언한다.

    private BufferedReader br = null; //BufferedReader(문자 입력 스트림으로부터 문자들을 읽어 들임) 클래스를 "br"로 선언한다.

    public InputThread(Socket sock, BufferedReader br){ //InputThread클래스의 생성자 이다.

        this.sock = sock; //파라미터로 Socket "sock"이 들어왔을때, 이 클래스 내에 선언된 "sock"에 할당한다.

        this.br = br; //파라미터로 BufferedReader "br"이 들어왔을때, 이 클래스 내에 선언된 "br"에 할당한다.

    }

    public void run(){ //run이라는 함수를 실행한다. (BufferedReader을 통해 읽은 line을 출력하는 함수)

        try{

            String line = null;

            while((line = br.readLine()) != null){

                System.out.println(line);

            }//try를 통하여 (문제 상황이 있을경우 catch를 통해 보내줌) String 클래스타입의 "line"을 BufferedReader를 통하여 읽고, 반복문을 통하여 이것이 null 이 될때 까지 출력한다.

        }catch(Exception ex){ //catch를 통하여 오류 상황을 잡아준다.

        }finally{ //catch문 뒤에 쓰인다. (exception이 throw 되었는지, 필요한 catch가 존재하는지에 대한 역할을 함)

            try{

                if(br != null)

                    br.close();

            } //try를 통하여 (문제 상황이 있을경우 catch를 통해 보내줌) BufferedReader가 null이 아니면, BufferedReader를 close한다.

            catch(Exception ex){} //catch를 통하여 오류 상황을 잡아준다.

            try{

                if(sock != null)

                    sock.close();

            } //try를 통하여 (문제 상황이 있을경우 catch를 통해 보내줌) Socket이 null이 아니면, Socket을 close한다.

            catch(Exception ex){} //catch를 통하여 오류 상황을 잡아준다.

        }

    } // InputThread

}

 

//내가 쓴거는 메인의 PrintWriter로 넣어주고, 다른사람이 보낸 Socket에 잇는 것을 InputThread를 통하여 읽어서 써준다.

 

"ServerSocket"으로 연결대기하다가, Client가 연결시도하면 "Socket"을 생성하여 해당 "Socket"으로 데이터를 주고 받는다.

 

반응형
LIST