위 글은 김성박선생님의 자바 강의를 바탕으로 쓰여졌습니다.
더 자세한 내용은 아래 링크를 통해 알아보실 수 있습니다.
우리는 다음과 같은 기능을 원한다.
클라이언트에서
/ 로 시작하는 것은 명령어로 취급한다.
/quit : 접속종료
/create 방제목: 방생성
/list : 방 목록보기
/join 방 번호: 방에 입장
/exit : 방에 빠져 나간다.
ChatRoom을 관리하는 클래스를 만들자.
package chat2;
import java.util.ArrayList;
import java.util.List;
public class ChatRoom {
private int id;
private String title;
private List<ChatThread> chatThreadList;
public ChatRoom(){
chatThreadList = new ArrayList<>();
}
public void addChatThread(ChatThread chatThread){
chatThreadList.add(chatThread);
chatThread.setChatRoom(this);
}
public void removeChatThread(ChatThread chatThread){
chatThreadList.remove(chatThread);
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@Override
public String toString() {
return "ChatRoomService{" +
"id=" + id +
", title='" + title + '\'' +
'}';
}
}
private ChatRoom chatRoom;
public void setChatRoom(ChatRoom chatRoom){
this.chatRoom = chatRoom;
}
package chat2;
import java.util.ArrayList;
import java.util.List;
public class ChatRoom {
private int id;
private String title;
private List<ChatThread> chatThreadList;
public ChatRoom(){
chatThreadList = new ArrayList<>();
}
public void addChatThread(ChatThread chatThread){
chatThreadList.add(chatThread);
chatThread.setChatRoom(this);
}
public void removeChatThread(ChatThread chatThread){
chatThreadList.remove(chatThread);
chatThread.setChatRoom(null);
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@Override
public String toString() {
return "ChatRoomService{" +
"id=" + id +
", title='" + title + '\'' +
'}';
}
}
public void broadcast(String msg){
for(int i=0;i<chatThreadList.size();i++){
ChatThread chatThread = chatThreadList.get(i);
chatThread.sendMessage(msg);
}
}
public ChatRoom(int id, String title){
this.id = id;
this.title = title;
chatThreadList = new ArrayList<>();
}
package chat2;
import java.util.ArrayList;
import java.util.List;
public class ChatRoom {
private int id;
private String title;
private List<ChatThread> chatThreadList;
public ChatRoom(int id, String title){
this.id = id;
this.title = title;
chatThreadList = new ArrayList<>();
}
public void broadcast(String msg){
for(int i=0;i<chatThreadList.size();i++){
ChatThread chatThread = chatThreadList.get(i);
chatThread.sendMessage(msg);
}
}
public void addChatThread(ChatThread chatThread){
chatThreadList.add(chatThread);
chatThread.setChatRoom(this);
}
public void removeChatThread(ChatThread chatThread){
chatThreadList.remove(chatThread);
chatThread.setChatRoom(null);
}
public int getId() {
return id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@Override
public String toString() {
return "ChatRoomService{" +
"id=" + id +
", title='" + title + '\'' +
'}';
}
}
ChatRoomService는 ChatRoom을 관리하는 클래스이다.
ChatRoomService에서 추가하고 싶은 기능은 아래와 같다.
- ChatRoom을 생성하는 메소드
public ChatRoom createChatRoom(String title){
ChatRoom chatRoom = new ChatRoom(GEN_ID, title);
GEN_ID++;
return chatRoom;
}
- ChatRoom의 목록을 출력하는 메소드
public Iterator<ChatRoom> getChatRoomIterator(){
return chatRoomList.iterator();
}
package chat2;
import java.util.Iterator;
import java.util.List;
public class ChatRoomService {
private static int GEN_ID = 1;
private List<ChatRoom> chatRoomList;
public ChatRoom createChatRoom(String title){
ChatRoom chatRoom = new ChatRoom(GEN_ID, title);
GEN_ID++;
return chatRoom;
}
public Iterator<ChatRoom> getChatRoomIterator(){
return chatRoomList.iterator();
}
}
위의 부분을 지우고, ChatRoomService객체를 공유객체로 만들자.
위의 코드에서 List<ChatThread> list
부분을 설정하자.
package chat2;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class ChatThread extends Thread {
private String name;
private BufferedReader br;
private PrintWriter pw;
private Socket socket;
ChatRoomService chatRoomService;
private ChatRoom chatRoom;
public ChatThread(Socket socket, ChatRoomService chatRoomService)throws Exception {
this.socket =socket;
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
this.br = br;
this.pw = pw;
this.name = br.readLine();
this.chatRoomService = chatRoomService;
}
public void sendMessage(String msg){
pw.println(msg);
pw.flush();
}
@Override
public void run() {
// ChatThread는 사용자가 보낸 메세지를 읽어들여서,
// 접속된 모든 클라이언트에게 메세지를 보낸다.
// 나를 제외한 모든 사용자에게 "00님이 연결되었습니다"..
try{
broadcast(name + "님이 연결되었습니다.", false);
String line = null;
while((line = br.readLine()) != null){
if("/quit".equals(line)){
break;
}
broadcast(name + " : " + line,true);
}
}catch(Exception ex){
// ChatThread가 연결이 끊어졌다는 것.
//ex.printStackTrace();
broadcast(name + "님이 연결이 끊어졌습니다.", false);
this.list.remove(this);
}
}
private void broadcast(String msg, boolean includeMe){
List<ChatThread> chatThreads = new ArrayList<>();
for(int i=0;i<this.list.size();i++){
chatThreads.add(list.get(i));
}
try{
for(int i=0;i<chatThreads.size();i++){
ChatThread ct = chatThreads.get(i);
if(!includeMe){ // 나를 포함하고 있지 말아라.
if(ct == this){
continue;
}
}
ct.sendMessage(msg);
}
}catch(Exception ex) {
System.out.println("///");
}
}
public void setChatRoom(ChatRoom chatRoom) {
this.chatRoom = chatRoom;
}
}
위의 코드에서 broadcast의 기능은 모두에게 보내는 것이 아닌 방에 속한 사람들에게만 보내고 싶으니깐 코드를 제거해주도록 하자.
package chat2;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class ChatThread extends Thread {
private String name;
private BufferedReader br;
private PrintWriter pw;
private Socket socket;
ChatRoomService chatRoomService;
private ChatRoom chatRoom;
public ChatThread(Socket socket, ChatRoomService chatRoomService)throws Exception {
this.socket =socket;
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
this.br = br;
this.pw = pw;
this.name = br.readLine();
this.chatRoomService = chatRoomService;
}
public void sendMessage(String msg){
pw.println(msg);
pw.flush();
}
@Override
public void run() {
// ChatThread는 사용자가 보낸 메세지를 읽어들여서,
// 접속된 모든 클라이언트에게 메세지를 보낸다.
// 나를 제외한 모든 사용자에게 "00님이 연결되었습니다"..
try{
String line = null;
while((line = br.readLine()) != null){
if("/quit".equals(line)){
break;
}
}
}catch(Exception ex){
// ChatThread가 연결이 끊어졌다는 것.
//ex.printStackTrace();
}
}
public void setChatRoom(ChatRoom chatRoom) {
this.chatRoom = chatRoom;
}
}
if(line.length() >= 9){
String title = line.substring(8);
ChatRoom chatRoom = chatRoomService.createChatRoom(title);
this.chatRoom = chatRoom;
}
this.chatRoom = chatRoom
은 ChatThread 자체도 ChatRoom을 가르키게끔 하는 코드이다.
else if(this.chatRoom != null){
chatRoom.broadcast(name + ":" + line);
}
@Override
public void run() {
// ChatThread는 사용자가 보낸 메세지를 읽어들여서,
// 접속된 모든 클라이언트에게 메세지를 보낸다.
// 나를 제외한 모든 사용자에게 "00님이 연결되었습니다"..
System.out.println("ChatThread" + Thread.currentThread().getName());
try{
String line = null;
while((line = br.readLine()) != null){
System.out.println("line -" + line);
if("/quit".equals(line)){
break;
}
else if(line.indexOf("/create") == 0){ // 방 생성 요청
if(line.length() >= 9) {
String title = line.substring(8);
ChatRoom chatRoom = chatRoomService.createChatRoom(title);
this.chatRoom = chatRoom;
this.chatRoom.addChatThread(this);
}else{
System.out.println("방 제목을 입력하세요.");
}
}
else if(line.indexOf("/join") == 0){ // 방 입장
}
else if(line.indexOf("/exit") == 0){ // 방에서 빠져나가기
}
else if(line.indexOf("/list") == 0){ // 방에서 빠져나가기
}
else if(this.chatRoom != null){
System.out.println("속한 방에 브로드캐스트 합니다."+ line);
chatRoom.broadcast(line);
}else{
System.out.println("방에 들어와있지 않아요. " + line);
}
// 내가 방에 있을 경우, 해당 방에 있는 사용자에게 메세지 전송하기.
}
}catch(Exception ex){
// ChatThread가 연결이 끊어졌다는 것.
//ex.printStackTrace();
}
}
else if(line.indexOf("/create") == 0){ // 방 생성 요청
if(line.length() >= 9) {
String title = line.substring(8);
ChatRoom chatRoom = chatRoomService.createChatRoom(title);
this.chatRoom = chatRoom;
this.chatRoom.addChatThread(this);
}else{
System.out.println("방 제목을 입력하세요.");
}
this.chatRoom.addChatThread(this)
를 작성한다. else if(line.indexOf("/exit") == 0){ // 방에서 빠져나가기
this.chatRoom.removeChatThread(this);
}
ChatRoom클래스의 removeChatThread메소드도 수정하자.
public void removeChatThread(ChatThread chatThread){
chatThreadList.remove(chatThread);
chatThread.setChatRoom(null);
broadcast(chatThread.getName() + "님이 퇴장하셨습니다.");
}
마지막 줄에 broadcast() 메소드를 통해, 퇴장한것을 다른 사람에게 메세지 보낼 수 있다.
else if(line.indexOf("/list") == 0){ // 방에서 빠져나가기
Iterator<ChatRoom> chatRoomIterator= chatRoomService.getChatRoomIterator();
while(chatRoomIterator.hasNext()){
ChatRoom cr = chatRoomIterator.next();
pw.println(cr.getId() + " - " + cr.getTitle());
pw.flush();
}
}
else if(line.indexOf("/join") == 0){ // 방 입장
try {
chatRoomService.join(Integer.parseInt(line.substring(6)), this);
} catch(Exception ex){
pw.println("방 번호가 잘못 되었습니다.");
pw.flush();
}
}
ChatRoomService 클래스에서 join메소드 만들기
public void join(int id, ChatThread chatThread) {
for(int i=0;i<chatRoomList.size();i++){
ChatRoom chatRoom = chatRoomList.get(i);
}
package chat2;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
public class ChatClient {
public static void main(String[] args) throws Exception {
if(args.length != 1){
System.out.println("사용법 : java com.example.chat2.ChatClient 닉네임");
return;
}
String name = args[0];
Socket socket = new Socket("127.0.0.1", 8888);
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
BufferedReader keyboard = new BufferedReader(new InputStreamReader(System.in));
// 닉네임 전송
pw.println(name);
pw.flush();
// 백그라운드로 서버가 보내준 메세지를 읽여들여서 화면에 출력한다.
InputThread inputThread = new InputThread(br);
inputThread.start();
// 클라이언트는 읽어들인 메세지를 서버에 전송한다.
try{
String line = null;
while((line = keyboard.readLine()) != null) {
if("/quit".equals(line)) {
pw.println("/quit");
pw.flush();
break;
}
pw.println(line);
pw.flush();
}
}catch(Exception ex){
System.out.println("...");
}
try{
br.close();
}catch(Exception ex){
System.out.println("111");
}
try{
pw.close();
}catch(Exception ex){
System.out.println("222");
}
try{
System.out.println("socket close!!");
socket.close();
}catch(Exception ex){
System.out.println("333");
}
}
}
class InputThread extends Thread {
BufferedReader br;
public InputThread(BufferedReader br){
this.br = br;
}
@Override
public void run() {
try{
String line = null;
while((line = br.readLine()) != null){
System.out.println(line);
}
}catch(Exception ex){
System.out.println("...");
}
}
}
package chat2;
import java.util.ArrayList;
import java.util.List;
public class ChatRoom {
private int id;
private String title;
private List<ChatThread> chatThreadList;
public ChatRoom(int id, String title){
this.id = id;
this.title = title;
chatThreadList = new ArrayList<>();
}
public void broadcast(String msg){
System.out.println("ChatRoom에서 메세지 브로드캐스트" + msg);
for(int i=0;i<chatThreadList.size();i++){
ChatThread chatThread = chatThreadList.get(i);
chatThread.sendMessage(msg);
}
}
// 방에 입장했을때,
public void addChatThread(ChatThread chatThread){
chatThreadList.add(chatThread);
chatThread.setChatRoom(this); //
}
public void removeChatThread(ChatThread chatThread){
chatThreadList.remove(chatThread);
chatThread.setChatRoom(null);
broadcast(chatThread.getName() + "님이 퇴장하셨습니다.");
}
@Override
public String toString() {
return "ChatRoom{" +
"id=" + id +
", title='" + title + '\'' +
'}';
}
public int getId() {
return id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
package chat2;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
// ChatRoom을 관리하는 클래스..
// 싱글턴으로 만들어주는 것이 좋다..! - 메모리에 하나씩 만들어 준다.
public class ChatRoomService {
private static int GEN_ID = 1;
// 메모리에 오직 1개 생긴다.
private List<ChatRoom> chatRoomList;
// 생성자
public ChatRoomService(){
this.chatRoomList = new ArrayList<>();
}
public ChatRoom createChatRoom(String title){
System.out.println("방 생성을 합니다. : " + title);
ChatRoom chatRoom = new ChatRoom(GEN_ID, title);
GEN_ID++;
chatRoomList.add(chatRoom);
return chatRoom;
}
public Iterator<ChatRoom> getChatRoomIterator(){
return chatRoomList.iterator();
}
public void join(int id, ChatThread chatThread) {
for(int i=0;i<chatRoomList.size();i++){
ChatRoom chatRoom = chatRoomList.get(i);
if(chatRoom.getId() == id ){
chatRoom.addChatThread(chatThread);
break;
}
}
}
}
package chat2;
import java.net.ServerSocket;
import java.net.Socket;
public class ChatServer {
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(8888);
// 동시성 문제를 해결하기 위해서..!
ChatRoomService chatRoomService = new ChatRoomService();
while(true) {
Socket socket = serverSocket.accept();
System.out.println("접속 : " + socket);
ChatThread chatThread = new ChatThread(socket, chatRoomService);
chatThread.start();
}
}
}
package chat2;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Iterator;
public class ChatThread extends Thread {
private String name;
private BufferedReader br;
private PrintWriter pw;
private Socket socket;
ChatRoomService chatRoomService;
private ChatRoom chatRoom;
public ChatThread(Socket socket, ChatRoomService chatRoomService)throws Exception {
this.socket =socket;
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
this.br = br;
this.pw = pw;
this.name = br.readLine();
this.chatRoomService = chatRoomService;
}
public void sendMessage(String msg){
System.out.println("sendMessage : " + msg);
pw.println(msg);
pw.flush();
}
@Override
public void run() {
// ChatThread는 사용자가 보낸 메세지를 읽어들여서,
// 접속된 모든 클라이언트에게 메세지를 보낸다.
// 나를 제외한 모든 사용자에게 "00님이 연결되었습니다"..
System.out.println("ChatThread" + Thread.currentThread().getName());
try{
String line = null;
while((line = br.readLine()) != null){
System.out.println("line -" + line);
if("/quit".equals(line)){
break;
}
else if(line.indexOf("/create") == 0){ // 방 생성 요청
if(line.length() >= 9) {
String title = line.substring(8);
ChatRoom chatRoom = chatRoomService.createChatRoom(title);
this.chatRoom = chatRoom;
this.chatRoom.addChatThread(this);
}else{
System.out.println("방 제목을 입력하세요.");
}
}
else if(line.indexOf("/join") == 0){ // 방 입장
try {
chatRoomService.join(Integer.parseInt(line.substring(6)), this);
} catch(Exception ex){
pw.println("방 번호가 잘못 되었습니다.");
pw.flush();
}
}
else if(line.indexOf("/exit") == 0){ // 방에서 빠져나가기
this.chatRoom.removeChatThread(this);
}
else if(line.indexOf("/list") == 0){ // 방에서 빠져나가기
Iterator<ChatRoom> chatRoomIterator= chatRoomService.getChatRoomIterator();
while(chatRoomIterator.hasNext()){
ChatRoom cr = chatRoomIterator.next();
pw.println(cr.getId() + " - " + cr.getTitle());
pw.flush();
}
}
else if(this.chatRoom != null){
System.out.println("속한 방에 브로드캐스트 합니다."+ line);
chatRoom.broadcast(line);
}else{
System.out.println("방에 들어와있지 않아요. " + line);
}
// 내가 방에 있을 경우, 해당 방에 있는 사용자에게 메세지 전송하기.
}
}catch(Exception ex){
// ChatThread가 연결이 끊어졌다는 것.
//ex.printStackTrace();
}
}
public void setChatRoom(ChatRoom chatRoom) {
this.chatRoom = chatRoom;
}
}