1. 단방향 통신이란?
요청 하는 자 : 클라이언트 (a) / 요청 받는 자 : 서버 - 갑 (b)
(ex) 넷플릭스(서버)야, 나(클라이언트)한테 영화 좀 보여줘)
(근데 지금은 단방향이라 서버가 의미 없음)
정보가 한 방향으로만 전송되는 통신 방식. 데이터가 한 쪽에서 다른 쪽으로만 흐르는 방식으로, 일방적인 데이터 전송이 이루어진다. 한 쪽에서 데이터를 보내는 역할을 하는 발신자(또는 송신자) 다른 쪽에서 데이터를 받는 역할을 하는 수신자로 구성 되어 있다.
이렇게 양 끝단에 소켓을 달고 소켓끼리 통신하기 때문에 ‘소켓 통신’이라고도 부른다.
송신자 소켓 / 수신자 소켓
* 단방향 통신에서 타겟은 일반적으로 소켓(Socket) 이다.
* 버퍼는 파일과 소켓을 포함한 다양한 타겟에서 사용
(데이터가 소켓을 타고 내려가야지 OS에서 세그먼트로 분할되고 패킷이 형성 되니까!)
(BufferedWriter 연결을 모니터에 하면 모니터가 타겟인 것?? )
BufferedWriter를 모니터에 연결하는 것은 모니터를 타겟으로 하는 것이 아니다.
파일이나 소켓과 같은 출력 대상에 데이터를 쓰기 위해 사용되는 클래스
(보조스트림을 버퍼로 만드는 것)
통신을 하는데 버퍼가 안 달릴 수는 없다. 무조건 다 달려있음
버퍼드 리더, 버퍼드 라이터 ㅇㅋ?
2. 서버 만들기
요청 받는 애(서버)를 먼저 만든다
양 끝단에 소켓을 만듬. try-catch 하기
우리는 포트번호를 10000으로 지정했다.
소켓을 두 개 만들었다. 보통 소켓을 두 개 이상 만든다
2-1. 소켓이 2개인 이유
소켓이 1개라면, 중간에 다른 클라이언트가 서버에 접속할 수가 없다! 서버의 소켓을 이미 첫번째 클라이언트가 독점해서 사용하고 있기 때문! (데이터가 섞이기 때문에 하나의 소켓에 2개의 컴퓨터가 연결될 수는 없다.)
[ 첫번째 소켓 ]
ServerSocket serverSocket = new ServerSocket(10000); 10000번 포트를 사용하는 서버 소켓을 생성. 이 포트는 클라이언트가 서버에 접속할 때 사용될 포트 번호 해당 라인에서 소켓(클라이언트의 연결 요청이 오면 생성되는 소켓)이 생성되지 않는다면, 연결 요청을 무한정 기다린다. -> 이런 현상을 '블로킹'이라고 함 (ServerSocket 클래스의 생성자에서 지정된 포트 번호 (여기서는 10000)를 통해 클라이언트의 연결 요청을 수신할 수 없기 때문)
블로킹이란?
= 프로그램이 특정 작업을 처리하는 동안 다른 작업을 할 수 없는 상태
즉, 클라이언트의 연결을 기다리는 상황에서 블로킹이 일어나면, 다른 작업을 하지 않고 계속해서 연결을 기다려야 한다. 프로그램이 멈춘 것처럼 보일 수 있다.
= 특정 작업이 완료될 때까지 스레드가 멈추는 상태
블로킹 작업은 주로 I/O 작업이나 동기화 작업에서 발생
클라이언트 연결 안됐죠? 연결을 기다리고 있죠? 대기중이죠?
[ 두번째 소켓 ] → 연결 요청만을 받는 소켓
Socket socket = serverSocket.accept(); 클라이언트의 연결을 기다리고, 클라이언트가 연결되면 해당 클라이언트와 통신할 수 있는 소켓(Socket)을 반환한다. (= 클라이언트의 연결을 수락하는 코드) accept() 메서드는 ServerSocket의 인스턴스 메서드로, 서버 소켓에서 클라이언트의 연결을 수락하고, 클라이언트와 통신하기 위한 소켓을 반환하는 역할 즉, 연결이 되면 aceept()가 됨.(완료됨) 그럼 이 서버 소켓과의 연결을 끊는 것
accept() 메서드는 ServerSocket의 인스턴스 메서드
인스턴스 메서드는 해당 클래스의 객체를 생성한 후에만 호출할 수 있다.
클래스 이름 자체로는 메서드를 호출할 수 없다. (그래서 변수명으로 호출을?)
[ 2번째 소켓 설명 ]
이 소켓은 '연결을 해주는 용도'로만 사용한다.
서버 소켓은..... 오작교 같은 거구나.
소켓 개수 : 클라이언트 개수 + 1
클라이언트가 100명이면 소켓의 개수는 101개. 즉, 스레드도 101개 필요
* 소켓은 전부 포트를 가지고 있다.
* 소켓이 한 개 밖에 없으면 하나의 요청만 받을 수 있기 때문에 이렇게 만든다.
3. 클라이언트 만들기
클라이언트를 만들기 위해서는 소켓을 직접 생성하고, 서버와의 연결을 수립해야 한다. 이를 위해서는 IP 주소와 포트 번호를 매개변수로 사용하여 목표 서버에 연결해야 한다.
4. 버퍼 달기
Scanner 있네? 입력 받는 거 있네? 그럼... 버퍼 달아야겠지?
package ex17.oneway; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStreamWriter; import java.net.Socket; import java.util.Scanner; public class Client { public static void main(String[] args) { try { //클라이언트용 소켓 만들고 Socket socket = new Socket("127.0.0.1", 10000); //11번 라인 소켓과 연결 Scanner sc = new Scanner(System.in); String msg = sc.nextLine(); //BufferedWriter 만들고 BufferedWriter bw = new BufferedWriter( new OutputStreamWriter(socket.getOutputStream(), "UTF-8") //소켓에 BufferWriter 쓸거고, 그 쓰는건 UTF-8로 할거다 ); //쓰고 bw.write(msg + "\n"); bw.flush(); } catch (IOException e) { e.printStackTrace(); //에러를 자세하게 볼 수 있음 } } }
글자를 쓸 때 몇 바이트로 문자를 교환할 건지 OutputStreamWriter에 UTF-8 이라고 적어야 한다 그래야지 소켓이 읽어들어올 때, 한글이 안깨진다. (세그먼트로 쪼갤때 한글은 3바이튼데 1바이트로 끊어읽는 것을 방지함.) 몇 바이트인지 알아야지 어? 애는 한글인데? 하고 3바이트로 쪼개줌 +) 이모지 - 4바이트
getOutputStream() 메서드 = 해당 소켓과 연결된 클라이언트와 데이터를 주고받을 수 있는 출력 스트림을 반환
socket.getOutputStream()은 socket에 있는 OutputStream을 가져온다는 말
오류 잡기 TIP (+printStackTrace())
개발할 때 printStackTrace() 꼭 다 넣어주기. 에러 추적하기 쉬움 피범벅들 > printStackTrace()
java.net.ConnectException: Connection refused: connect //제일 윗 부분부터 읽어봐라. 왜 커넥션이 거부 당했을까..? 이해 안되면 다음줄로 at java.base/sun.nio.ch.Net.connect0(Native Method) at java.base/sun.nio.ch.Net.connect(Net.java:589) at java.base/sun.nio.ch.Net.connect(Net.java:578) at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:583) at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327) at java.base/java.net.Socket.connect(Socket.java:751) at java.base/java.net.Socket.connect(Socket.java:686) at java.base/java.net.Socket.<init>(Socket.java:555) at java.base/java.net.Socket.<init>(Socket.java:324) at ex17.oneway.Client.main(Client.java:9)
팁!
Net, NioSocketImpl, Socket 이것들….
내가 만든 라이브러리들,,아니잖아. 그러니까, 내가 만든게 잘못됐구나ㅎㅎ… 할수있음
제일 밑에 ㅠㅠ 잘못된 내 코드 발견
전체 코드
[ Client ]
package ex17.oneway; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStreamWriter; import java.net.Socket; public class Client { public static void main(String[] args) { // localhost = 127.0.0.1 (루프백) try { //소켓만들고 Socket socket = new Socket("127.0.0.1", 10000); // Scanner sc = new Scanner(System.in); // String msg = sc.nextLine(); //버퍼드라이트만들고 // 가나다라abc BufferedWriter bw = new BufferedWriter( new OutputStreamWriter(socket.getOutputStream(), "UTF-8") //소켓에 BufferWriter 쓸거고, 그 쓰는건 UTF-8로 할거다 ); //쓰고 bw.write("msg1" + "\n"); bw.write("msg2" + "\n"); bw.flush(); } catch (IOException e) { e.printStackTrace(); } } }
[ Server ]
package ex17.oneway; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.ServerSocket; import java.net.Socket; public class Server { public static void main(String[] args) { try { ServerSocket serverSocket = new ServerSocket(10000);ㅇ Socket socket = serverSocket.accept(); //리스너 (누가 나한테 통신 요청하는지를 리스닝함) System.out.println("클라이언트 연결됨"); BufferedReader br = new BufferedReader( new InputStreamReader(socket.getInputStream(), "UTF-8") ); while (true) { //while 돌려서 써라 String msg = br.readLine(); if (msg == null) break; System.out.println(msg); } } catch (IOException e) { throw new RuntimeException(e); } } }
[ 해당 코드를 while로 변경한 것 ] String msg = br.readLine(); //while 돌려서 써라 System.out.println(msg);
[ 실행하는 법 ]
실행할 때
1. Server 파일을 커런트 파일로 먼저 실행하고 (꼭 서버 먼저 열어야 됨)
2. Client 파일을 커런트 파일로 실행하기
결과
만약, \n 없는 상태로 작동하면 Server에서 와라라락 에러 flush 없는 상태로 작성해도 와라라락 에러남
Share article