[오픈소스] 3. Chat 소스 분석 2
<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를 통하여 읽어서 써준다.