JAVA 프로그래밍

문제

서버와 클라이언트가 멀티스레드를 바탕으로 순서에 상관없이 자유롭게 대화할 수 있는 채팅 문제입니다 이를 해결하는 다음 프로그램을 해석하세요 
서버 클라이언트
[Server]님이 들어오셨습니다. 대화(종료시 quit)를 입력하세요
[Client]님이 들어오실때까지 잠시만 기다려주세요
[Client]님이 들어오셨습니다
[Client]안녕하세요
반갑습니다
[Client]님이 나가셨습니다
quit
[Server]님이 나가셨습니다
[Client]님이 들어오셨습니다. 대화(종료시 quit)를 입력하세요
[Server]님이 들어오셨습니다
안녕하세요
[Server]반갑습니다
quit
[Client]님이 나가셨습니다

 

서버 클라이언트
[Server]님이 들어오셨습니다. 대화(종료시 quit)를 입력하세요
[Client]님이 들어오실때까지 잠시만 기다려주세요
[Client]님이 들어오셨습니다
[Client]안녕하세요
반갑습니다
[Client]오랜만이네요
[Client]그동안 잘 지내셨어요?
덕분에 잘 지내고 있습니다
신경써 주셔서 감사합니다
[Client]님이 나가셨습니다
quit
[Server]님이 나가셨습니다
[Client]님이 들어오셨습니다. 대화(종료시 quit)를 입력하세요
[Server]님이 들어오셨습니다
안녕하세요
[Server]반갑습니다
오랜만이네요
그동안 잘 지내셨어요?
[Server]덕분에 잘 지내고 있습니다
[Server]신경써 주셔서 감사합니다
quit
[Client]님이 나가셨습니다

 

서버 클라이언트
[Server]님이 들어오셨습니다. 대화(종료시 quit)를 입력하세요
[Client]님이 들어오실때까지 잠시만 기다려주세요
[Client]님이 들어오셨습니다
[Client]안녕하세요
반갑습니다
[Client]오랜만이네요
[Client]그동안 잘 지내셨어요?
덕분에 잘 지내고 있습니다
신경써 주셔서 감사합니다
[Client]앗~
[Client]지금 나가봐야 할 것 같아요
[Client]죄송합니다
다음에 뵈요
[Client]님이 나가셨습니다
quit
[Server]님이 나가셨습니다
[Client]님이 들어오셨습니다. 대화(종료시 quit)를 입력하세요
[Server]님이 들어오셨습니다
안녕하세요
[Server]반갑습니다
오랜만이네요
그동안 잘 지내셨어요?
[Server]덕분에 잘 지내고 있습니다
[Server]신경써 주셔서 감사합니다
앗~
지금 나가봐야 할 것 같아요
죄송합니다
[Server]다음에 뵈요
quit
[Client]님이 나가셨습니다

 

알고리즘

채팅 서버 초기화 
   네트워크 서버 연결 초기화
      서버 소켓 연결
      클라이언트에서 서버로 연결 요청시 연결 활성화
      네트워크 입출력 연결 후 데이터가 도착할 때까지 대기

입력 받은 데이터를 상대편에게 보내기
   입력한 대화 내용 읽기
   상대편에게 네트워크로 데이터 전송

상대편에게 데이터 받아서 출력
   상대편이 네트워크로 보낸 데이터 읽기
   읽은 대화 내용 출력

채팅 종료
   네트워크 종료

  멀티스레드 기반 클라이언트-서버의 네트워크 통신 구조

 

  멀티클라이언트 서버 프로그램의 클래스 다이어그램

 

  멀티스레드 기반 클라이언트-서버 채팅 프로그램의 네트워크 연결 초기화 과정

 

  멀티클라이언트 서버 프로그램의 데이터 전송 과정

 

 

  멀티스레드 기반 클라이언트-서버 채팅 프로그램의 네트워크 종료 과정

 
 

프로그램 코드

E001	// 클라이언트에서 네트워크 연결 요청
E002	// 클라이언트 데이터가 도착할 때까지 계속 대기
E003	// (수신전용스레드와 별개로) 메인 스레드 N41 이후 실행중
E004	// 대화를 입력할 때까지 계속 대기
E005	// 클라이언트에서 보낸 데이터([Client]안녕하세요) 도착
E006	// '반갑습니다' 입력
E007	// 클라이언트에서 보낸 데이터([Client]quit) 도착
E008	// 'quit' 입력
E009	// 메인스레드와 수신전용스레드가 모두 종료하여 프로그램 종료
	 
	// 파일명 : ./Chapter17/MultiThreadChatServer.java
	 
	import chat.ChatNetwork;
	 
	// 1:1 채팅(서버)
	public class MultiThreadChatServer
	{
1		public static void main(String[] args) {
2			new ChatNetwork();
3		}
	}
	// 파일명 : ./src/chat/ChatNetwork.java
	package chat;
	import java.util.Scanner;
	import network.Network;
	 
	// 멀티스레드 기반 1:1 채팅 클래스
	public class ChatNetwork implements Runnable
	{
	 private Network network;
	 
	 // 채팅 서버 초기화 
CN1b	 public ChatNetwork() {
	  System.out.println( "[Server]님이 들어오셨습니다. 대화(종료시 quit)를 입력하세요" );
	  System.out.println( "[Client]님이 들어오실때까지 잠시만 기다려주세요" );
	 
	  // 네트워크 초기화
CN11	  network =
CN12	            new Network();
CN13	  network.connectAsServer( this );
	  network.write( "[Server]님이 들어오셨습니다" );
CN14	  this.write( "[Server]" );
CN1e	 }
	 
	 // 채팅 클라이언트 초기화 
CN2b	 public ChatNetwork( String serverIP ) {
	  System.out.println( "[Client]님이 들어오셨습니다. 대화(종료시 quit)를 입력하세요" );
	 
	  // 네트워크 초기화
CN21	  network =
CN22	            new Network();
CN23	  network.connectAsClient( serverIP, this );
	  network.write( "[Client]님이 들어오셨습니다" );
CN24	  this.write( "[Client]" );
CN2e	 }
	  
	 // 입력 받은 데이터를 상대편에게 보내기 
CN3b	 public void write( String id ) {
	  try {
	   Scanner scan = new Scanner( System.in );
	   // 입력한 대화 내용 읽기 
CN31	   for ( String message = null; ( message =
CN32	                                            scan.nextLine() ) != null; ) {
	    if( message.contains( "quit" ) ) {
	     System.out.println( id + "님이 나가셨습니다" );
	     network.write( id + "님이 나가셨습니다" );
CN33	     network.write( id + message );
CN34	     break;
	    }
	    // 상대편에게 네트워크로 데이터 전송
CN35	    network.write( id + message );
CN36	   }
	   scan.close();
	  } catch ( Exception e ) {
	   e.printStackTrace();
	  }
CN3e	 }
	 
	 // 상대편에게 데이터 받아서 출력 
	 @Override
CN4b	 public void run() {
	  // 상대편이 네트워크로 보낸 데이터 읽기 
CN41	  for ( String message = null; ( message =
CN42	                                           network.read() ) != null; ) {
	   // 읽은 대화 내용 출력 
	   if( message.contains( "quit" ) ) {
	    System.out.println( message );
CN43	    network.write( message );
CN44	    break;
	   }
	   else {
CN45	    System.out.println( message );
	   }
CN46	  }
	  // 채팅 종료 
CN47	  network.close();
CN4e	 }
	}
 
 
	// 파일명 : ./src/network/Network.java
	package network;
	import java.io.*;
	import java.net.*;
	 
	// 네트워크 연결 및 입출력 클래스
	public class Network
	{
		public static final int serverPort = 7700;
		protected ServerSocket serverSocket;
		protected Socket socket;
		private BufferedReader in;
		private PrintWriter out;
		private Thread waitForCounterpart;
	 
		// 네트워크 클래스 초기화
N1b		public Network() {
			serverSocket = null;
			socket = null;
			in = null;
			out = null;
			waitForCounterpart = null;
N1e		}
		 
		// 네트워크 서버 연결 초기화 
N2b		public void connectAsServer( Runnable obj ) {
			try {
				// 서버 소켓 연결 
				serverSocket = new ServerSocket( serverPort );
				// 클라이언트에서 서버로 연결 요청시 연결 활성화 
N21				socket =
N22				         serverSocket.accept();
				// 네트워크 입출력 연결 후 데이터가 도착할 때까지 대기 
N23				connectInOut( obj );
			} catch ( Exception e ) {
				e.printStackTrace();
			}
N2e		}
		 
		// 네트워크 클라이언트 연결 초기화 
N3b		public void connectAsClient( String serverIP, Runnable obj ) {
			try {
				// 클라이언트에서 서버로 연결 요청 
N31				socket =
N32				         new Socket( serverIP, serverPort );
				// 네트워크 입출력 연결 후 데이터가 도착할 때까지 대기 
N33				connectInOut( obj );
			} catch ( Exception e ) {
				e.printStackTrace();
			}
N3e		}
	 
		// 네트워크 입출력 초기화
N4b		public void connectInOut( Runnable obj ) {
			try {
				// 네트워크 입출력 연결
				in = new BufferedReader( new InputStreamReader( socket.getInputStream() ));
				out = new PrintWriter( socket.getOutputStream(), true );
				// 네트워크에 데이터가 도착할 때까지 대기 시작
				waitForCounterpart = new Thread( obj );
N41				waitForCounterpart.start();
			} catch ( Exception e ) {
				e.printStackTrace();
			}
N4e		}
		 
		// 상대편이 네트워크로 보낸 데이터 읽기
N5b		public String read() {
			try {
				if ( this.isConnecting() == true )
N51					return
N52					       in.readLine();
			} catch ( Exception e ) {
				e.printStackTrace();
			}
			 
			return null;
N5e		}
		 
		// 상대편에게 네트워크로 데이터 전송 
N6b		public void write( String data ) {
			try {
				if ( this.isConnecting() == true )
N61					out.println( data );
			} catch ( Exception e ) {
				e.printStackTrace();
			}
N6e		}
	 
		// 네트워크 연결 상태 확인
		public boolean isConnecting() {
			if ( ( socket != null )
					&& ( in != null )
					&& ( out != null )
					&& ( waitForCounterpart != null )
					&& ( waitForCounterpart.getState() != Thread.State.TERMINATED ) )
				return true;
			else
				return false;
		}
		 
		// 네트워크 종료 
N7b		public void close() {
			try {
				if ( waitForCounterpart != null ) {
					waitForCounterpart.interrupt();
					waitForCounterpart = null;
				}
				if ( in != null ) {
					in.close();
					in=null;
				}
				if ( out != null ) {
					out.flush();
					out.close();
					out = null;
				}
				if ( socket != null ) {
					socket.close();
					socket = null;
				}
				if ( serverSocket != null ) {
					serverSocket.close();
					serverSocket = null;
				}
			} catch ( Exception e ) {
				e.printStackTrace();
			}
N7e		}
	}

실행 순서

 
 					※ 실행순서 및 메모리상태는 A키(이전) 및 D키(다음)를 눌러도 확인할 수 있습니다