Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions community_plus/Client1.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package communication;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

public class Client1 {
public static void main(String[] args) {
Client1 client = new Client1();
client.start();
}
public void start() {
Scanner sc = new Scanner(System.in);
Socket socket = null;
BufferedReader in = null;

try {
// 수신 (수신하는 쓰레드에 소켓(데이터 이동 창구)을 연결함)
socket = new Socket("localhost", 7000);
System.out.println("[서버와 연결되었습니다]");
// 송신 (데이터를 서버로 송신하는 쓰레드를 생성 & 시작함) (똑같은 소켓(데이터 이동 창구) 사용함)
String name = "user" + (int)(Math.random() * 10);
// 송신 쓰레드를 virtual thread로 변경
Thread.startVirtualThread(new SendTask(socket, name)); // 데이터 송신 쓰레드
// 수신 (무한루프를 돌며 서버로부터 데이터 수신함)
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (in != null) {
String inputMsg = in.readLine();
if (("[" + name +"]님이 나가셨습니다.").equals(inputMsg)) break;
System.out.println("From : " + inputMsg);
}
} catch (IOException e) { // 예외처리
System.out.println("[서버 접속끊김]");
} finally { // catch에 잡히지 않고 try이후 실행될 코드
try { socket.close(); // 소켓연결 끊기 // MENU 페이지로 돌아가기
}
catch (IOException e) { e.printStackTrace(); }
}
}
}

//송신 작업을 처리하는 SendTask 클래스 (Runnable을 구현)
class SendTask implements Runnable{
Scanner sc = new Scanner(System.in);
Socket socket = null;
String name;

// 생성자
public SendTask(Socket socket, String name) {
this.socket = socket;
this.name = name;
}

// run 메서드 (.start()호출 시, run()이 내부에서 호출됨)
public void run() {
try {
// 처음 연결되면 서버에게 클라이언트의 이름을 전달함
PrintStream out = new PrintStream(socket.getOutputStream());
out.println(name);
out.flush();
// 무한루프를 돌며 사용자의 입력을 반복적으로 송신함
while (true) {
String outputMsg = sc.nextLine();
out.println(outputMsg);
out.flush();
if ("quit".equals(outputMsg)) break;
}
} catch(IOException e) { e.printStackTrace(); }
}

}
43 changes: 43 additions & 0 deletions community_plus/Client2.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package communication;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

public class Client2 {
public static void main(String[] args) {
Client2 client = new Client2();
client.start();
}
public void start() {
Scanner sc = new Scanner(System.in);
Socket socket = null;
BufferedReader in = null;

try {
socket = new Socket("localhost", 7000);
System.out.println("[서버와 연결되었습니다]");

String name = "user" + (int)(Math.random() * 10);
Thread sendThread = new SendThread(socket, name);
sendThread.start();

in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (in != null) {
String inputMsg = in.readLine();
if (("[" + name +"]님이 나가셨습니다.").equals(inputMsg)) break;
System.out.println("From : " + inputMsg);
}
} catch (IOException e) {
System.out.println("[서버 접속끊김]");
} finally {
try { socket.close(); }
catch (IOException e) {
e.printStackTrace();
}
}
}
}
103 changes: 103 additions & 0 deletions community_plus/Server.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package communication;

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.Collections;
import java.util.List;

public class Server {
// MENU에서 커뮤니티 서비스로 들어오는 도입메서드
public void start() {
// 변수 초기화
ServerSocket serverSocket = null; // 클라이언트의 요청을 받기위한 준비를 하는 서버소켓
Socket socket = null; // 서버에 접속 요성을 하는 클라이언트 소켓
// 실제 // 소켓 = 데이터를 주고받을 수 있는 창구 역할 (송/수신, 양방향)
try {
serverSocket = new ServerSocket(7000);
while(true) {
System.out.println("[클라이언트 연결대기중]");
socket = serverSocket.accept(); // 접근을 요청한 클라이언트 소켓을 socket변수에 저장함
handleClient(socket); // 클라이언트 처리를 virtual thread로 수행
}
} catch (IOException e) { e.printStackTrace(); }
}
}

// 클라이언트 처리를 virtual thread로 수행하는 메서드
private void handleClient(Socket socket) {
Thread.startVirtualThread(() -> {
try {
ReceiveTask receiveTask = new ReceiveTask(socket);
receiveTask.run();
} catch (IOException e) {
e.printStackTrace();
}
});
}

// 수신하는 쓰레드를 만드는 ReceiveThread 클래스
class ReceiveTask implements Runnable {
// 클라이언트가 접속할 때마다/연결을 종료할 때마다 해당 출력 스트림을 리스트에 추가/제거하는 방식으로 관리함
static List<PrintWriter> list = Collections.synchronizedList(new ArrayList<PrintWriter>());

// 변수 초기화
Socket socket = null;
BufferedReader in = null;
PrintWriter out = null;

// 생성자
public ReceiveTask (Socket socket) {
this.socket = socket;
try {
// out : 클라이언트로부터 받은 메시지에 대해 서버가 응답해야할 경우를 대비
out = new PrintWriter(socket.getOutputStream());
// in : getInputStream() - 소켓으로부터 입력스트림을 가져와 바이트 단위로 데이터 읽음
// new InputStreamReader() - 바이트를 문자로 변환하는 문자스트림
// new BufferedReader() - 문자 스트림을 버퍼링하여 효율적으로 읽음
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
list.add(out);
} catch (IOException e) { e.printStackTrace(); }
}

// run 메서드 (.start()호출 시, run()이 내부에서 호출됨)
public void run() {
// 변수 초기화
String name = "";
// 실제
try {
// 도입부
name = in.readLine(); // 이름 전달받음
System.out.println("[" + name + " 새 연결 생성]");
sendAll("[" + name + "]님이 들어오셨습니다.");
// 무한루프를 돌며 클라이언트에게 전달받은 데이터를 출력 & quit파악
while (in != null) {
String inputMsg = in.readLine();
if ("quit".equals(inputMsg)) break;
sendAll(name + ">>" + inputMsg);
}
} catch (IOException e) {
System.out.println("[" + name +" 접속끊김]");
} finally {
// 엔딩
sendAll("[" + name +"]님이 나가셨습니다.");
list.remove(out); // 클라이언트에게 출력하는 출력스트림을 제거함
try {socket.close();} // 소켓 닫음 -> 그에따라 그 소켓을 통해 작업하던 송/수신 쓰레드들도 종료됨
catch (IOException e) { e.printStackTrace(); }
}
System.out.println("[" + name +" 연결종료]");
}
// sendAll 메서드
public void sendAll(String s) {
for (PrintWriter out: list) { // 클라이언트에게 연결된 모든 출력스트림한테
out.println(s); // s를 출력하게함
out.flush(); // flush() : PrintWriter객체의 버퍼를 비움 & 모든 버퍼링된 데이터를 출력하게함
// println()이 자동으로 flush를 수행하지만, 그렇지 않을 수도 있기에, 안전성을 고려해 적어줌
}
}

}