1. 전이중 통신
내가 따로 요청을 안하더라도 동시에 요청하고, 요청받을 수 있는 것 (실시간 채팅)
클라이언트가 언제 요청을 보내고 데이터를 받을지 알 수 없는 상황인 경우, 서버는 클라이언트의 요청을 기다리지 않고, 필요한 시점에 데이터를 쓰고 읽을 수 있다. 서버는 데이터를 언제든지 받을 준비가 되어 있고, (리스너처럼) 클라이언트가 요청을 보내면 그때 데이터를 쓰고 읽을 수 있다는 것. 즉, While 루프를 사용하여 지속적으로 요청을 확인해야 하므로, 스레드(Thread)를 사용 이 스레드는 메세지를 읽기만 하니, 따로 BufferedWrite하는 (쓰기만 하는) 스레드도 필요하다 = 동시에 작업하니 멀티 스레드 사용! (카톡을 생각하면 이해하기가 쉽다)
서버는 소켓을 생성하여 클라이언트의 연결을 받아들이고,
클라이언트는 소켓을 생성하여 서버에 연결한다.
[ 전이중 통신 스레드 묶음 ]
전이중은 스테이트 풀 (서버도 상태를 기억하고, 클라이언트도 상태를 기억하기 때문)
2. 전이중 통신 코드 +PrintWriter
1. 서버 생성 2. 클라이언트 생성 3. 클 -> 서버 (메시지를 지속적으로 전송) 지속적으로 = while 4. 서버 -> 클 (메시지를 지속적으로 전송) <- 여기부터 스레드
[ 1단계 ]
package ex17.multi; 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.nio.charset.Charset; public class Server { public static void main(String[] args) { try { ServerSocket serverSocket = new ServerSocket(20000); Socket socket = serverSocket.accept(); //소켓 연결 완료됨 //버퍼 만들기 (received) BufferedReader br = new BufferedReader( new InputStreamReader(socket.getInputStream()) ); //break 없다. 계속 받아야하니까! while (true) { String requestMsg = br.readLine(); System.out.println("클라이언트로부터 받은 메시지 : " + requestMsg); } } catch (IOException e) { throw new RuntimeException(e); } } }
package ex17.multi; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; public class Client { public static void main(String[] args) { try { Socket socket = new Socket("localhost", 20000); PrintWriter pw = new PrintWriter(socket.getOutputStream(), true); //메세지 보내는 것만 while 돌면 되겠네? while (true) { pw.println("3"); try { Thread.sleep(500); } catch (InterruptedException e) { throw new RuntimeException(e); } } } catch (IOException e) { throw new RuntimeException(e); } } }
while (true) { pw.println("3"); try { Thread.sleep(500); } catch (InterruptedException e) { throw new RuntimeException(e); } } 나중엔 이 부분을 메세지전송() 같은 메소드로 바꿔야한다. 그래야 메소드만 때리면 되니까
계속 3이 찍히는 것 확인! 이 메시지를 키보드로 입력받아 보자!
[ PrintWriter ]
자동으로 버퍼를 관리하며, flush() 메서드를 호출하여 버퍼를 비우고 데이터를 출력할 수 있다. 생성자에 true를 전달하면, println() 메서드를 호출할 때마다 자동으로 flush를 수행! 즉, PrintWriter을 쓰면 별도로 flush()나 BufferedWriter를 사용할 필요가 없다!!
PrintWriter는 자바에서 텍스트 기반의 출력을 담당하는 클래스.
주로 파일이나 다른 출력 스트림에 텍스트 데이터를 쓰는 데 사용
[ 2단계 ]
package ex17.multi; 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.nio.charset.Charset; public class Server { public static void main(String[] args) { try { ServerSocket serverSocket = new ServerSocket(20000); Socket socket = serverSocket.accept(); //소켓 연결 완료됨 //버퍼 만들기 (received) BufferedReader br = new BufferedReader( new InputStreamReader(socket.getInputStream()) ); //break 없다. 계속 받아야하니까! while (true) { String requestMsg = br.readLine(); System.out.println("클라이언트로부터 받은 메시지 : " + requestMsg); } } catch (IOException e) { throw new RuntimeException(e); } } }
package ex17.multi; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.util.Scanner; public class Client { public static void main(String[] args) { try { Socket socket = new Socket("localhost", 20000); Scanner sc = new Scanner(System.in); PrintWriter pw = new PrintWriter(socket.getOutputStream(), true); //메세지 보내는 것만 while 돌면 되겠네? while (true) { String keykboardMsg = sc.nextLine(); pw.println(keykboardMsg); //키보드에 글 적기 전까지는 대기가 되니까 sleep 필요 없음!! } } catch (IOException e) { throw new RuntimeException(e); } } }
[ 3단계 ] - 스레드 필요!
스레드 돌려서 보내는 것만 할 것임
package ex17.multi; 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.nio.charset.Charset; public class Server { public static void main(String[] args) { try { //1. 소켓과 버퍼 만들기 ServerSocket serverSocket = new ServerSocket(20000); Socket socket = serverSocket.accept(); BufferedReader br = new BufferedReader( new InputStreamReader(socket.getInputStream()) ); //2. 메시지 받기 스레드 new Thread(() -> { while (true) { try { String requestMsg = br.readLine(); System.out.println("클라이언트로부터 받은 메시지 : " + requestMsg); } catch (Exception e) { e.printStackTrace(); } } }).start(); } catch (IOException e) { throw new RuntimeException(e); } } }
package ex17.multi; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.util.Scanner; public class Client { public static void main(String[] args) { try { //1. 소켓과 버퍼 만들기 Socket socket = new Socket("localhost", 20000); Scanner sc = new Scanner(System.in); PrintWriter pw = new PrintWriter(socket.getOutputStream(), true); //메세지 보내는 것만 while 돌면 되겠네? //2. 메시지 전송 스레드 new Thread(() -> { while (true) { //키보드에 글 적기 전까지는 대기가 되니까 sleep 필요 없음!! String keykboardMsg = sc.nextLine(); pw.println(keykboardMsg); } }).start(); //3. 메시지 읽기 스레드 } catch (IOException e) { throw new RuntimeException(e); } } }
[ 4단계 ] - 3단계의 반대를 붙여넣기…
[ 서버 ]
package ex17.multi; 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.nio.charset.Charset; import java.util.Scanner; public class Server { public static void main(String[] args) { try { //1. 소켓과 버퍼 만들기 ServerSocket serverSocket = new ServerSocket(20000); Socket socket = serverSocket.accept(); Scanner sc = new Scanner(System.in); PrintWriter pw = new PrintWriter(socket.getOutputStream(),true); BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); //2. 메시지 받기 스레드 new Thread(() -> { while (true) { try { String requestMsg = br.readLine(); System.out.println("클라이언트로부터 받은 메시지 : " + requestMsg); } catch (Exception e) { e.printStackTrace(); } } }).start(); //3. 메시지 쓰기 스레드 new Thread(() -> { while (true) { //키보드에 글 적기 전까지는 대기가 되니까 sleep 필요 없음!! String keykboardMsg = sc.nextLine(); pw.println(keykboardMsg); } }).start(); } catch (IOException e) { throw new RuntimeException(e); } } }
[ 클라이언트 ]
package ex17.multi; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.util.Scanner; public class Client { public static void main(String[] args) { try { //1. 소켓과 버퍼 만들기 Socket socket = new Socket("192.168.0.94", 20000); Scanner sc = new Scanner(System.in); PrintWriter pw = new PrintWriter(socket.getOutputStream(), true); BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); //메세지 보내는 것만 while 돌면 되겠네? //2. 메시지 전송 스레드 new Thread(() -> { while (true) { //키보드에 글 적기 전까지는 대기가 되니까 sleep 필요 없음!! String keykboardMsg = sc.nextLine(); pw.println(keykboardMsg); } }).start(); //3. 메시지 읽기 스레드 new Thread(() -> { while (true) { try { String requestMsg = br.readLine(); System.out.println("서버로부터 받은 메시지 : " + requestMsg); } catch (Exception e) { e.printStackTrace(); } } }).start(); } catch (IOException e) { throw new RuntimeException(e); } } }
ipconfig로 각자의 ip를 확인. ip와 동일한 포트번호로 연결한 후, a컴퓨터가 서버, b컴퓨터가 클라이언트가 되어서 채팅한 모습
메인은 스레드를 때리는 용도로만 써라!
만약, 코드가 완벽한데 통신이 잘 안된다면 ‘방화벽’ 을 확인할 것!
설정 - 방화벽에서 openjdk
BufferedReader ♥ readLine()
BufferedWriter ♥ flush()
Share article