서버와 클라이언트가 멀티스레드를 바탕으로 순서에 상관없이 자유롭게 대화할 수 있는 채팅 문제입니다 이를 해결하는 다음 프로그램을 해석하세요
| 서버 | 클라이언트 |
|
[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 // '안녕하세요' 입력
E006 // 서버에서 보낸 데이터([Server]반갑습니다) 도착
E007 // 'quit' 입력
E008 // 서버에서 보낸 데이터([Client]quit) 도착
E009 // 메인스레드와 수신전용스레드가 모두 종료하여 프로그램 종료
// 파일명 : ./Chapter17/MultiThreadChatClient.java
import chat.ChatNetwork;
// 1:1 채팅(클라이언트)
public class MultiThreadChatClient
{
1 public static void main(String[] args) {
2 new ChatNetwork( "localhost" );
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키(다음)를 눌러도 확인할 수 있습니다