접속한 Client에게 Server가 현재 날짜를 전송한다.
package lecture0718.exam01;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
// Server 쪽 process
public class Exam01_DateServer {
public static void main(String[] args) {
// server socket을 만든다.
try {
ServerSocket serverSocket = new ServerSocket(5678);
System.out.println("Date Server 기동 - 포트번호(5678)");
// client의 접속을 기다린다. 기다리다가 client가 접속하면 socket 객체를 만들어낸다.
Socket socket = serverSocket.accept(); // blocking method
// 이 코드로 내려왔다는 것은 접속이 성공했다는 것이다.
PrintWriter pr = new PrintWriter(socket.getOutputStream()); // output stream을 열어준다.
// 현재 날짜를 구한다.
Date date = new Date();
// 만들어진 stream으로 date를 보낸다.
pr.println(date.toLocaleString()); // 버퍼를 이용한 스트림
// 해당 코드에서 flush()가 없으면 pr을 강제로 종료하면서 pr.close() 데이터가 내보내진다.
pr.flush(); // 이 코드가 더 안전한다.
// 자원 반납
pr.close();
socket.close();
serverSocket.close();
System.out.println("Date Server 종료");
} catch (Exception e) {
}
}
}
버퍼 안에 들어가 있는 데이터를 스트림을 통해 내보내는 시점
1. 스트림이 강제로 종료될 경우(close)
2. 버퍼에 공간이 다 차는 경우
3. method를 이용해서 flush() 시킬 경우 - 강제적으로 내보낸다.
package lecture0718.exam01;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
public class Exam01_DateClient extends Application{
TextArea textarea;
Button connBtn;
@Override
public void start(Stage primaryStage) throws Exception {
// 화면 구성
BorderPane root = new BorderPane();
root.setPrefSize(700, 500); // window 크기
textarea = new TextArea();
root.setCenter(textarea); // 화면 center에 textarea를 붙인다.
connBtn = new Button("Date 서버 접속");
connBtn.setPrefSize(150, 40);
connBtn.setOnAction(e -> {
textarea.clear();
try {
// server process에 접속을 시도한다.
Socket socket = new Socket("localhost", 5678);
// 접속에 성공했다. input stream을 열어준다.
InputStreamReader ir = new InputStreamReader(socket.getInputStream());
BufferedReader br = new BufferedReader(ir); // 한줄씩 읽을 수 있는 stream으로 감싼다.
// msg를 읽어들인다.
String msg = br.readLine(); // blocking method
// textarea에 msg를 붙인다.
textarea.appendText(msg + "\n"); // 나중에 문제가 생길 수 있다!
// 사용한 자원을 반납한다.
br.close();
ir.close();
socket.close();
textarea.appendText("서버와의 연결이 종료되었어요!");
} catch (Exception e2) {}
});
FlowPane flowPane = new FlowPane();
flowPane.setPadding(new Insets(10,10,10,10)); // 여백을 준다.
flowPane.setPrefSize(700, 40);
flowPane.setHgap(10);
flowPane.getChildren().add(connBtn); // 버튼 부착
root.setBottom(flowPane);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(); // thread가 하나 만들어지면서 start를 실행하게 된다.
}
}
간단한 Echo Program 작성해보기.
: client에서 문자열을 입력하면 server에게 갔다가 돌아와서 화면에 찍힌다.
-> client가 "/exit"를 입력하면 종료된다.
package lecture0718.exam02;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class Exam02_EchoServer {
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket = null;
PrintWriter pr = null;
BufferedReader br = null;
try {
serverSocket = new ServerSocket(5678);
System.out.println("Echo Server 기동 - 클라이언트 접속 대기");
socket = serverSocket.accept(); // blocking method
// 접속에 성공했다. stream을 열어준다.
br = new BufferedReader(new InputStreamReader(socket.getInputStream())); // 한줄씩 읽을 수 있는 stream으로 감싼다.
pr = new PrintWriter(socket.getOutputStream());
String msg = null;
while(true) {
msg = br.readLine(); // blocking method
if(msg.equals("/exit") || (msg == null)) {
break;
}
pr.println(msg);
pr.flush();
}
} catch (Exception e) {
} finally {
// 사용된 resource를 해제
try {
// close하기 전에 객체가 존재하는지 확인한다.
if(br != null) br.close();
if(pr != null) pr.close();
if(socket != null) socket.close();
if(serverSocket != null) serverSocket.close();
System.out.println("Echo Server 종료");
} catch (Exception e2) {
}
}
}
}
package lecture0718.exam02;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
public class Exam02_EchoClient extends Application{
TextArea textarea;
Button connBtn;
TextField idField, textfield;
Socket socket;
BufferedReader br;
PrintWriter pr;
@Override
public void start(Stage primaryStage) throws Exception {
// 화면 구성
BorderPane root = new BorderPane();
root.setPrefSize(700, 500); // window 크기
textarea = new TextArea();
root.setCenter(textarea); // 화면 center에 textarea를 붙인다.
connBtn = new Button("Echo Server 접속");
connBtn.setPrefSize(150, 40);
connBtn.setOnAction(e -> {
textarea.clear();
try {
socket = new Socket("localhost", 5678);
// stream을 연다.
br = new BufferedReader(new InputStreamReader(socket.getInputStream())); // 한줄씩 읽을 수 있는 stream으로 감싼다.
pr = new PrintWriter(socket.getOutputStream());
textarea.appendText("Echo Server 접속 성공!!" + "\n");
} catch (Exception e2) {}
});
idField = new TextField();
idField.setPrefSize(100, 40);
textfield = new TextField();
textfield.setPrefSize(200, 40);
textfield.setOnAction(e -> {
try {
String msg = idField.getText() + " : " + textfield.getText();
// 만들어진 stream으로 date를 보낸다.
pr.println(msg);
pr.flush();
if(textfield.getText().equals("/exit")) {
textarea.appendText("Echo Server와의 연결을 종료합니다.");
textfield.setDisable(true);
} else {
try {
String serverMsg = br.readLine(); // blocking method
textarea.appendText(serverMsg + "\n");
} catch (Exception e2) {
}
}
textfield.clear();
} catch (Exception e1) {
}
}); // 입력 상자에 글 입력 후 enter 입력하면 이벤트 처리
FlowPane flowPane = new FlowPane();
flowPane.setPadding(new Insets(10,10,10,10)); // 여백을 준다.
flowPane.setPrefSize(700, 40);
flowPane.setHgap(10);
flowPane.getChildren().add(connBtn); // 버튼 부착
flowPane.getChildren().add(idField); // 아이디 필드 부착
flowPane.getChildren().add(textfield); // 입력 상자 부착
root.setBottom(flowPane);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(); // thread가 하나 만들어지면서 start를 실행하게 된다.
}
}
-> 화면 구성(연습 문제 1 + server에게 보내는 화면)
=> 문제: 여러 명의 client를 받을 수 없다. (방 한 개짜리 채팅 프로그램)
server는 client가 들어오면 thread를 만들어서 client와 통신하게 만들어 주고, 자신은 다시 새로운 클라이언트를 기다려야 한다.
: 받은 데이터를 공유해야 하기 때문에 공유 자원으로 해야 한다.
채팅 프로그램
package lecture0718.exam03;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
public class Exam03_ChatServer extends Application {
TextArea textarea;
Button startBtn, stopBtn;
ServerSocket serverSocket;
ExecutorService executorService = Executors.newCachedThreadPool();
SharedObject sharedObject = new SharedObject();
private void printMsg(String name) {
Platform.runLater(()->{
textarea.appendText(name + "\n");
});
}
class SharedObject {
List<ClientRunnable> clients = new ArrayList<ClientRunnable>();
public synchronized void broadcast(String msg) {
clients.stream().forEach(t -> {
t.pw.println(msg);
t.pw.flush();
});
}
}
class ClientRunnable implements Runnable {
private SharedObject sharedObject;
private Socket socket;
private BufferedReader br;
private PrintWriter pw;
public ClientRunnable(SharedObject sharedObject,Socket socket) throws IOException {
this.sharedObject = sharedObject;
this.socket = socket;
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
pw = new PrintWriter(socket.getOutputStream());
}
@Override
public void run() {
String msg = null;
try {
while(true) {
msg = br.readLine();
if( msg == null ) {
break;
}
sharedObject.broadcast(msg);
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if( pw != null ) pw.close();
if( br != null ) br.close();
if( socket != null ) socket.close();
} catch (Exception e) {
e.printStackTrace();
}
sharedObject.clients.remove(this);
}
}
@Override
public void start(Stage primaryStage) throws Exception {
BorderPane root = new BorderPane();
root.setPrefSize(700, 500);
textarea = new TextArea();
textarea.setEditable(false);
root.setCenter(textarea);
startBtn = new Button("Chat Server 기동");
startBtn.setPrefSize(150, 40);
startBtn.setOnAction(e->{
textarea.clear();
printMsg("[ 채팅서버 기동 - 5555 ]");
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
serverSocket = new ServerSocket(5555);
while(true) {
printMsg("[ 클라이언트 접속 대기중 ]");
Socket socket = serverSocket.accept();
ClientRunnable cRunnable = new ClientRunnable(sharedObject,socket);
synchronized (this) {
sharedObject.clients.add(cRunnable);
}
printMsg("[ 클라이언트 접속 - 현재 클라이언트 수 : " + sharedObject.clients.size() + " ]");
executorService.execute(cRunnable);
}
} catch (IOException e) {
e.printStackTrace();
}
}
};
executorService.execute(runnable);
});
stopBtn = new Button("Chat Server 중지");
stopBtn.setPrefSize(150, 40);
stopBtn.setOnAction(e->{
printMsg("[ 서버 중지 - 프로그램 재시작 필요 ]");
executorService.shutdownNow();
});
FlowPane flowPane = new FlowPane();
flowPane.setPadding(new Insets(10, 10, 10, 10));
flowPane.setColumnHalignment(HPos.CENTER);
flowPane.setPrefSize(700, 40);
flowPane.setHgap(10);
flowPane.getChildren().add(startBtn);
flowPane.getChildren().add(stopBtn);
root.setBottom(flowPane);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setTitle("Chat Server ( Java IO )");
primaryStage.setOnCloseRequest(e->{
executorService.shutdownNow();
});
primaryStage.show();
}
public static void main(String[] args) {
launch();
}
}
package lecture0718.exam03;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
public class Exam03_ChatClient extends Application {
TextArea textarea;
TextField idfield, msgfield;
Button connBtn, disconnBtn;
Socket socket;
BufferedReader br;
PrintWriter pw;
ExecutorService executorService = Executors.newCachedThreadPool();
private void printMsg(String name) {
Platform.runLater(()->{
textarea.appendText(name + "\n");
});
}
class ReceiveRunnable implements Runnable {
private BufferedReader br;
public ReceiveRunnable(BufferedReader br) {
this.br = br;
}
@Override
public void run() {
String msg = null;
try {
while(true) {
msg = br.readLine();
if( msg == null ) {
break;
}
printMsg(msg);
}
} catch (IOException e) {
// e.printStackTrace();
} finally {
try {
if( br != null ) br.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
@Override
public void start(Stage primaryStage) throws Exception {
BorderPane root = new BorderPane();
root.setPrefSize(700, 500);
textarea = new TextArea();
textarea.setEditable(false);
root.setCenter(textarea);
connBtn = new Button("Chat 서버 접속");
connBtn.setPrefSize(150, 40);
connBtn.setOnAction(e->{
textarea.clear();
disconnBtn.setDisable(false);
try {
socket = new Socket("localhost",5555);
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
pw = new PrintWriter(socket.getOutputStream());
printMsg("Chat Server 연결 성공");
idfield.setDisable(false);
msgfield.setDisable(false);
ReceiveRunnable receiver = new ReceiveRunnable(br);
executorService.execute(receiver);
connBtn.setDisable(true);
} catch (UnknownHostException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
});
idfield = new TextField();
idfield.setPrefSize(70, 40);
idfield.setDisable(true);
msgfield = new TextField();
msgfield.setPrefSize(200, 40);
msgfield.setDisable(true);
msgfield.setOnAction(e->{
String msg = "[" + idfield.getText() + "] : " + msgfield.getText();
pw.println(msg);
pw.flush();
msgfield.clear();
});
disconnBtn = new Button("Chat 서버 접속 종료");
disconnBtn.setPrefSize(150, 40);
disconnBtn.setDisable(true);
disconnBtn.setOnAction(e->{
connBtn.setDisable(false);
idfield.setDisable(false);
msgfield.setDisable(false);
try {
printMsg("[서버연결 종료]");
// br은 blocking중이므로 pw를 먼저 close.
if( pw != null ) pw.close();
if( br != null ) br.close();
if( socket != null ) socket.close();
} catch (UnknownHostException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
disconnBtn.setDisable(true);
});
FlowPane flowPane = new FlowPane();
flowPane.setPadding(new Insets(10, 10, 10, 10));
flowPane.setColumnHalignment(HPos.CENTER);
flowPane.setPrefSize(700, 40);
flowPane.setHgap(10);
flowPane.getChildren().add(connBtn);
flowPane.getChildren().add(idfield);
flowPane.getChildren().add(msgfield);
flowPane.getChildren().add(disconnBtn);
root.setBottom(flowPane);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setTitle("Echo Server에 접속");
primaryStage.setOnCloseRequest(e->{
try {
// br은 blocking중이므로 pw를 먼저 close.
if( pw != null ) pw.close();
if( br != null ) br.close();
if( socket != null ) socket.close();
executorService.shutdownNow();
} catch (Exception e2) {
e2.printStackTrace();
}
});
primaryStage.show();
}
public static void main(String[] args) {
launch();
}
}