전이중 통신

Jan 30, 2024
전이중 통신

1. 전이중 통신

notion image
내가 따로 요청을 안하더라도 동시에 요청하고, 요청받을 수 있는 것 (실시간 채팅)
클라이언트가 언제 요청을 보내고 데이터를 받을지 알 수 없는 상황인 경우, 서버는 클라이언트의 요청을 기다리지 않고, 필요한 시점에 데이터를 쓰고 읽을 수 있다. 서버는 데이터를 언제든지 받을 준비가 되어 있고, (리스너처럼) 클라이언트가 요청을 보내면 그때 데이터를 쓰고 읽을 수 있다는 것. 즉, While 루프를 사용하여 지속적으로 요청을 확인해야 하므로, 스레드(Thread)를 사용 이 스레드는 메세지를 읽기만 하니, 따로 BufferedWrite하는 (쓰기만 하는) 스레드도 필요하다 = 동시에 작업하니 멀티 스레드 사용! (카톡을 생각하면 이해하기가 쉽다)
💡
서버는 소켓을 생성하여 클라이언트의 연결을 받아들이고, 클라이언트는 소켓을 생성하여 서버에 연결한다.
 

[ 전이중 통신 스레드 묶음 ]

notion image
notion image
💡
전이중은 스테이트 풀 (서버도 상태를 기억하고, 클라이언트도 상태를 기억하기 때문)
 

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); } } 나중엔 이 부분을 메세지전송() 같은 메소드로 바꿔야한다. 그래야 메소드만 때리면 되니까
 

 
notion image
계속 3이 찍히는 것 확인! 이 메시지를 키보드로 입력받아 보자!
 

[ PrintWriter ]

notion image
자동으로 버퍼를 관리하며, 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); } } }
 

 
notion image
notion image
 
[ 3단계 ] - 스레드 필요!
스레드 돌려서 보내는 것만 할 것임
notion image
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); } } }
notion image
notion image
 
[ 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); } } }
notion image
ipconfig로 각자의 ip를 확인. ip와 동일한 포트번호로 연결한 후, a컴퓨터가 서버, b컴퓨터가 클라이언트가 되어서 채팅한 모습
 
💡
메인은 스레드를 때리는 용도로만 써라!
💡
만약, 코드가 완벽한데 통신이 잘 안된다면 ‘방화벽’ 을 확인할 것! 설정 - 방화벽에서 openjdk
 

 
💡
BufferedReader ♥ readLine()
💡
BufferedWriter ♥ flush()
 
Share article

codingb