1. 일대일 채팅 프로그램

 

● CalcServer.java

import java.io.BufferedWriter;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class CalcServer {
	public static void main(String[] args) {
		ServerSocket ss = null;
		Socket socket = null;
		
		try {
			ss = new ServerSocket(9999);
			System.out.println("서버가 구동중입니다.");
			socket = ss.accept();
			
			InputStream is = socket.getInputStream();
			ObjectInputStream ois = new ObjectInputStream(is);
			CalcDTO dto = (CalcDTO)ois.readObject();
			
			CalcDAO dao = new CalcDAO();
			int result = dao.getResult(dto);
			
			System.out.println("첫 번째 정수 : " + dto.getNum1());
			System.out.println("두 번째 정수 : " + dto.getNum2());
			System.out.println("연산자 : " + dto.getOpCode());
			System.out.println("결과 : " + result);
			
			OutputStream os = socket.getOutputStream();
			PrintWriter pw = new PrintWriter(os);
			pw.println(result);
			pw.flush();
			pw.close();
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				socket.close();
				ss.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
	}//main()
}//class

① ServerSocket, Socket 생성 ▶ ServerSocket에 포트를 할당한다.

② 수신 스레드 객체 생성 후 start() 메서드, socket을 매개 변수로 넣는다.

③ 송신 스레드 객체 생성 후 start() 메서드, socket을 매개 변수로 넣는다.

④ 서버 소켓을 닫아준다, 소켓은 스레드에서 사용하기 때문에 서버에서 닫지 않는다.

 

 

ChatClient.java

import java.net.Socket;

public class ChatClient {
	public static void main(String[] args) {
		Socket socket = null;
		try {
			socket = new Socket("192.168.0.170", 9999);
			
			//키보드에서 메세지를 입력받아 서버로 보내는 작업(송신, 출력)   ▶  보내는 Thread
			Thread st = new ChatSenderThread(socket);
			st.start();
		} catch (Exception e) {
			e.printStackTrace();
		} 
		/*
		finally {
			try {
				//socket.close();
				// ▶ 여러번 전송해야하기 때문에 finally 구문에서 
				//   socket을 close하면 한번 이상 전송이 안된다.
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		*/
	}//main()
}//class

① Socket 생성 ▶ 서버의 ip와 포트를 할당한다.

② 송신 스레드 객체 생성 후 start() 메서드 ▶ socket을 매개 변수로 넣는다.

③ 수신 스레드 객체 생성 후 start() 메서드 ▶ socket을 매개 변수로 넣는다.

④ 소켓은 스레드에서 사용하기 때문에 클라이언트에서 닫지 않는다.

 

 

● ChatSenderThread.java

import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

//보내는 Thread : 메세지를 송신(출력)    ▶  PrintWriter
public class ChatSenderThread extends Thread{		//Thread Class를 상속
	private Socket socket;
	
	public ChatSenderThread(Socket socket) {
		this.socket = socket;
	}		
	
	@Override
	public void run() {
		try {
			OutputStream os = socket.getOutputStream();
			PrintWriter pw = new PrintWriter(os);
			Scanner scanner = new Scanner(System.in);
			
			while (true) {
				System.out.print("전송할 메세지를 입력하세요 : ");
				String msg = scanner.nextLine();
				
				if (msg.equals("bye")) {
					System.out.println("종료되었습니다.");
					System.exit(0);		//메모리에서 완전히 제거하기 위해서
					break;
				}
				
				pw.println(msg);
				pw.flush();		//강제전송
			}//while
			
			scanner.close();
			pw.close();
			
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("ChatSenderThread Exception!");
		} finally {		//Client와 Server에서 닫지 않은 socket을 Thread에서 닫아야한다.		
			try {
				socket.close();
			} catch (Exception e) {
				e.printStackTrace();
			}//try-catch
		}//try-catch-finally
			
	}//run()
	
}//class

① 소켓을 매개 변수로 받기 때문에 멤버 변수로 선언하고 기본 생성자 메서드를 만든다.

② Thread 클래스를 상속받고 run() 메서드를 오버라이드 한다.

③ 문자를 송신하기 때문에 OutputStream, PrintWriter 브릿지를 만든다.

④ 키보드로 입력하기 위해 Scanner 클래스 객체를 만든다.

⑤ 메시지를 계속 입력하기 위해 while() 문을 사용한다.

⑥ 메시지를 입력하는 문단을 while() 문 내부에 작성하고 종료 단어 bye를 만든다.

⑦ 메시지를 송신하기 위해 pw.prinln()과 pw.flush() 메서드를 사용한다.

⑧ 사용이 끝난 스캐너와 소켓을 닫아준다.

 

 

● ChatReceiverThread.java

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;

//받는 Thread : 메세지를 소신(입력)  ▶  BufferedReader
public class ChatReceiverThread extends Thread{
	private Socket socket;
	public ChatReceiverThread(Socket socket) {
		this.socket = socket;
	}
	
	@Override
	public void run() {
		try {
			InputStream is = socket.getInputStream();
			InputStreamReader isr = new InputStreamReader(is);
			BufferedReader br = new BufferedReader(isr);
			
			while (true) {
				String msg = br.readLine();
				if (msg == null) {
					System.exit(0);		//메모리 정리
					break;
				}
				System.out.println("수신 메세지 : " + msg);
			}//while
			br.close();
		} catch (Exception e) {
			//e.printStackTrace();
			//System.out.println("ChatReceiverThread Exception!");
			System.out.println("종료되었습니다.");
		} finally {
			try {
				socket.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}//run()
	
 }//class

① 소켓을 매개 변수로 받기 때문에 멤버 변수로 선언하고 기본 생성자 메서드를 만든다.

② Thread 클래스를 상속받고 run() 메서드를 오버라이드 한다.

 문자를 수신하기 때문에 InputStream, InputStreamReader, BufferedReader 브릿지를 만든다.

④ 누가 접속했는지 알기 위해 getInetAddress()와 getHostName()으로 접속자 이름을 받는다.

⑤ 메시지를 계속 수신하기 위해 while() 문을 사용한다.

⑥ while() 문 내부에 수신받는 문단을 작성하고 종료 단어 bye가 입력되었을 때 종료되는 문단도 작성한다.

⑦ 사용이 끝난 소켓을 닫아준다.

 

일대일 채팅 작동 화면

 


2. 단체 채팅 프로그램

 

● MultiChatServer.java

import java.net.ServerSocket;
import java.net.Socket;

public class MultiChatServer {
	public static void main(String[] args) {
		ServerSocket ss = null;
		Socket socket = null;
		
		try {
			ss = new ServerSocket(9999);
			System.out.println("서버가 구동중입니다.");
			while (true) {	//서버에는 다수의 클라이언트가 접속  → 접속된 클라이언트만큼 Socket을 생성
				socket = ss.accept();
				Thread pt = new MultiChatPersonThread(socket);	//다수의 클라이언트에서 동작되는 스레드
				pt.start();
			}
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("MultiChatServer Exception!");
		} 
	/*	
		finally {
			try {
				//socket.close();
				//ss.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	*/	
	}//main()
}//class

① ServerSocket, Socket 생성 ▶ ServerSocket에 포트를 할당한다.

② 불특정 다수가 서버로 들어오기 때문에 while() 문을 사용한다.

 while() 문 내부에 여러 접속을 받기 위한 Person 스레드 객체 생성 후 start() 메서드, socket을 매개 변수로 넣는다.

④ 서버 소켓과 소켓을 스레드에서 사용하기 때문에 서버에서 닫지 않는다.

 

 

● MultiChatClient.java

import java.net.Socket;

public class MultiChatClient {
	public static void main(String[] args) {
		Socket socket = null;
		
		try {
			socket = new Socket("192.168.35.241", 9999);
			
			//클라이언트 프로그램 실행 시 대화명이 미입력 되었을 때
			//사용법을 안내하는 메세지 출력
			if (args.length != 1) {
				System.out.println("프로그램 실행 시 대화명을 입력해주세요!");
				System.out.println("실행 예 : java MultiChatClient 대화명");
			}
			
			//메세지를 입력받아 서버로 보내는 작업(송신, 출력)  ▶  MultiChatSenderThread
			Thread st = new MultiChatSenderThread(socket, args[0]);
			st.start();
			
			//메세지를 수신(입력)받아 화면에 출력하는 작업  ▶  MultiChatReceiverThread
			Thread rt = new MultiChatReceiverThread(socket);
			rt.start();
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("MultiChatClient Exception!");
		} /*finally {
			try {
				socket.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}*/
	}//main()
}//class

① Socket 생성 ▶ 서버의 ip와 포트를 할당한다.

② 대화명을 args [] 배열에 받도록 안내문을 작성한다.

 송신 스레드 객체 생성 후 start() 메서드 ▶ socket을 매개 변수로 넣는다.

 수신 스레드 객체 생성 후 start() 메서드 ▶ socket을 매개 변수로 넣는다.

⑤ 소켓은 스레드에서 사용하기 때문에 클라이언트에서 닫지 않는다.

 

 

● MultiChatSenderThread.java

import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

public class MultiChatSenderThread extends Thread{		//송신, 출력  →PrintWriter
	private Socket socket;
	private String name;
	public MultiChatSenderThread(Socket socket, String name) {
		this.socket = socket;
		this.name = name;
	}
	
	@Override
	public void run() {
		try {
			OutputStream os = socket.getOutputStream();
			PrintWriter pw = new PrintWriter(os);
			pw.println(name);
			pw.flush();
			
			Scanner scanner = new Scanner(System.in);
			while (true) {
				System.out.print("서버로 전송할 메세지를 입력하세요 : ");
				String msg = scanner.nextLine();
				if (msg.equals("bye")) {
					scanner.close();
					pw.close();
					break;
				}//if
				pw.println(msg);
				pw.flush();
			}//while
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("MultiChatSenderThread Exception!");
		} finally {
			try {
				socket.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}//try-catch-finally
	}//run()


}//class

① 소켓과 대화명을 매개 변수로 받기 때문에 멤버 변수로 선언하고 기본 생성자 메서드를 만든다.

② Thread 클래스를 상속받고 run() 메서드를 오버라이드 한다.

③ 문자를 송신하기 때문에 OutputStream, PrintWriter 브릿지를 만든다.

④ 매개 변수로 받은 대화명을 송신하기 위해 pw.prinln()과 pw.flush() 메서드를 사용한다.

 키보드로 입력하기 위해 Scanner 클래스 객체를 만든다.

 메시지를 계속 입력하기 위해 while() 문을 사용한다.

 메시지를 입력하는 문단을 while() 문 내부에 작성하고 종료 단어 bye를 만든다.

종료할 때 사용이 끝난 PrintWriter 브릿지와 Scanner 클래스를 닫아 준다.

⑨ 입력받은 메시지를 송신하기 위해 pw.prinln()과 pw.flush() 메서드를 사용한다.

⑩ 사용이 끝난 소켓을 닫아준다.

 

 

● MultiChatReceiverThread.java

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;

public class MultiChatReceiverThread extends Thread{	//수신, 입력  → BufferedReader
	private Socket socket;
	public MultiChatReceiverThread(Socket socket) {
		this.socket = socket;
	}
	
	@Override
	public void run() {
		try {
			InputStream is = socket.getInputStream();
			InputStreamReader isr = new InputStreamReader(is);
			BufferedReader br = new BufferedReader(isr);
			
			while (true) {
				String msg = br.readLine();
				if (msg == null) {
					br.close();
					break;
				}
				System.out.println(msg);
			}//while
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("MultiChatReceiverThread Exception!");
		} finally {
			try {
				socket.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}//run()
}//class

① 소켓을 매개 변수로 받기 때문에 멤버 변수로 선언하고 기본 생성자 메서드를 만든다.

② Thread 클래스를 상속받고 run() 메서드를 오버라이드 한다.

 문자를 수신하기 때문에 InputStream, InputStreamReader, BufferedReader 브릿지를 만든다.

 메시지를 계속 수신하기 위해 while() 문을 사용한다.

 while() 문 내부에 수신받는 문단을 작성하고 종료 단어 bye가 입력되었을 때 종료되는 문단도 작성한다.

⑥ 종료할 때 사용이 끝난 BufferedReader 브릿지도 닫아준다.

⑦ 사용이 끝난 소켓을 닫아준다.

 

 

● MultiChatPersonThread.java

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList;

//접속된 다수의 클라이언트에서 동작되는 스레드
//접속된 모든 클라이언트의 접속 정보를 저장하고
//접속된 클라이언트에게 메세지를 전송(송신, 수신)할 수 있도록
//클라이언트의 목록을 저장할 무한 배열을 만들자 → ArrayList<>
//MultiChatServer Class의 main() 메소드와 같이 동작 : static
public class MultiChatPersonThread extends Thread{
	private Socket socket;
	private PrintWriter pw;
	static ArrayList<PrintWriter> list = new ArrayList<>();
	
	public MultiChatPersonThread(Socket socket) {
		this.socket = socket;
		try {
			OutputStream os = socket.getOutputStream();
			pw = new PrintWriter(os);
			list.add(pw);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	@Override
	public void run() {
		String name = null;
		InetAddress addr = socket.getInetAddress();
		String comName = addr.getHostName();
		
		try {
			InputStream is = socket.getInputStream();
			InputStreamReader isr = new InputStreamReader(is);
			BufferedReader br = new BufferedReader(isr);
			
			//클라이언트에서 맨 처음에 입력된 메세지(대화명)을 받아
			//접속된 다른 클라이언트 화면에 출력  ▶  sendAll()
			name = br.readLine();
			sendAll("#" + name + "(" + comName + ")님께서 입장하셨습니다!");
			// #홍길동(201-xx)님께서 입장하셨습니다!
			
			//클라이언트에서 보낸 메세지(대화 내용)를 접속된 다른 클라이언트에게 전송
			while (true) {
				String msg = br.readLine();
				if (msg == null) {
					break;
				}//if
				sendAll(name + "(" + comName + ") : " + msg);
				// #홍길동(201-xx) : 안녕하세요
			}//while
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("MultiChatPersonThread Exception");
		} finally {
			try {
				list.remove(pw);	//접속 목록에서 삭제
				sendAll("#" + name + "(" + comName + ")님께서 퇴장하셨습니다!");
				// #홍길동(201-xx)님께서 퇴장하셨습니다!
				socket.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}//try-catch-finally
	}//run()
	
	public void sendAll(String msg) {
		for (PrintWriter pw : list) {
			pw.println(pw);
			pw.flush();
		}//for-each
	}//sendAll()
}//class

① 제일 먼저 접속한 클라이언트의 모든 정보를 받도록 static을 사용하여 초기화 블록을 만들어준다.

② 초기화 블록에 PrintWriter 타입만 다루도록 제네릭스를 설정한 ArrayList를 선언 및 초기화해준다.

③ 멤버 변수 Socket과 PrintWriter를 선언하고 socket을 매개변수로 받는 생성자를 만들어준다.

④ (추후 수정)

⑤ Thread 클래스를 상속받고 run() 메서드를 오버 라이딩한다.

⑥ 접속한 사람의 정보를 알기 위해 getInetAddress()와 getHostName() 메서드를 사용한다.

⑦ 대화명 name을 readLine()으로 입력받고 sendAll로 입장 메시지를 접속해 있는 사용자들에게 뿌려준다.

⑧ 사용자가 입력한 내용을 String msg로 받아서 뿌려준다.

⑨ 퇴장 시 퇴장 메시지를 뿌려준다.

 

▲단체 채팅 작동 화면