네트워크 - 프로그램(1)
#Java/adv2/Network
/네트워크 프로그램1 - 예제 , /네트워크 프로그램1 - 분석
- Client
- 소켓(socket)
Socket socket = new Socket("localhost", PORT);- 서버쪽의 주소, PORT 지정
- 내부에서
InetAddress를 사용. IP주소를 통해 TCP 접속을 시도한다 (3-way handshake) - 연결이 성공적으로 완료되면
Socket객체를 반환한다. - 남는 포트 하나를 로컬 포트로 자동 할당 해줌(클라이언트쪽 포트)
- Socket 객체를 통해 서버와 통신할 수 있다.
- 네트워크 스트림
- 클라이언트-서버간 통신은 Socket이 제공하는 스트림을 사용한다.
socket.getInputStream()socket.getOutputStream()- 보조 스트림과 같이 사용하면 다루기 편하다.
DataInputStream input = new DataInputStream(socket.getInputStream())DataOutputStream output =new DataOutputStream(socket.getOutputStream())
- 서버에게 문자 보내기
output.writeUTF(string);
- 서버로부터 문자 받기
String received = input.readUTF();- 문자를 받을 때 까지 블로킹된다.
- 자원 정리를 꼭 해줘야 한다.
input.close();output.close();socket.close();
- 소켓(socket)
- Server
- 서버 소켓 열기(일반 Socket이랑 다름)
ServerSocket serverSocket = new ServerSocket(PORT);- 서버는 특정 포트를 열어둬야 클라이언트가 그 포트를 지정해서 접속할 수 있다.
- 지정한 포트를 사용해서 서버 소켓을 생성하면, 클라이언트는 해당 포트로 서버에 연결할 수 있다.
- 클라이언트 접속 기다리기
Socket socket = serverSocket.accept();- 접속 과정
- 서버가 12345 포트로 서버 소켓을 열어둔다.
- 클라이언트는 이제 12345 포트로 서버에 접속할 수 있다. 클라이언트가 12345 포트에 연결을 시도한다.
- 이때 OS 계층에서 TCP 3 way handshake가 발생하고, TCP 연결이 완료된다.
- TCP 연결이 완료되면 서버는 OS backlog queue라는 곳에 클라이언트와 서버의 TCP 연결 정보를 보관한다.
- 이 연결 정보를 보면 클라이언트의 IP, PORT, 서버의 IP, PORT 정보가 모두 들어있다.
serverSocket.accept()메서드를 호출하면 backlog queue에서 TCP 연결 정보를 조회하여,Socket객체를 만들어서 반환한다.- 먄약 TCP 연결 정보가 없다면, 연결 정보가 생성될 때 까지 대기한다. (블로킹)
- 사용한 TCP 연결 정보는 backlog queue에서 제거된다.
- 서버 입장에서는 리스닝포트가 localport가 되고, 클라이언트가 사용한 임시 포트가 port가 된다.
- 그 외의 사용법은 Client와 동일한 구조
- 자원 정리할 때 input, output, socket을 닫고 마지막에 serverSocket도 닫아줘야 한다.
- 서버 소켓 열기(일반 Socket이랑 다름)
- 발생할 수 있는 예외
- 서버를 시작하지 않고 클라이언트를 실행하면
java.net.ConnectException: Connection refused - 포트를 다른 프로세스가 사용하고 있다면
java.net.BindException: Address already in use
- 서버를 시작하지 않고 클라이언트를 실행하면
- /DNS 탐색
InetAddress localhost = InetAddress.getByName("localhost");InetAddress클래스를 사용하면 호스트 이름으로 대상 IP를 찾을 수 있다.- 자바는
InetAddress.getByName("호스트명")메서드를 사용해서 해당하는 IP 주소를 조회합니다. - 이 과정에서 시스템의 호스트 파일을 먼저 확인한다.
/etc/hosts(리눅스, mac)C:\Windows\System32\drivers\etc\hosts(윈도우,Windows)
- 호스트 파일에 정의되어 있지 않다면, DNS 서버에 요청해서 IP 주소를 얻는다.
- 자바는
- 1번 예제의 문제점
- 메시지를 하나만 주고 받으면 클라이언트와 서버가 모두 종료된다.
/네트워크 프로그램2 - 예제 , /네트워크 프로그램2 - 분석
- 클라이언트가 “exit”을 보내야만 클라이언트와 서버가 종료되도록 개선
- 클라이언트와 서버가 메시지를 주고 받는 부분만
while로 반복 - 문제점: 서버는 하나의 클라이언트가 아니라, 여러 클라이언트의 연결을 처리할 수 있어야 한다.
- 새로 연결한
ClientV2-2는 소켓 연결은 되지만, 메시지를 전송해도 서버로 부터 아무런 응답이 오지 않는다. - 새로 연결한 클라이언트도 3-way ~~ 거치고 OS backlog queue에 담겨서 서버와 TCP 연결은 되었다.
- 다만 클라이언트에만 socket 객체가 생성되고, 아직 서버에 새로 접속한 쪽의 Socket 객체가 생성되지 않은 것이다.
- 이 상태에서 클라이언트는 서버에 메시지를 보낼 수는 있다.
- 클라이언트
- 애플리케이션 OS TCP 송신 버퍼 → 클라이언트 네트워크 카드
- 클라이언트가 보낸 메시지가 서버에 도착했을 때, 서버
- 서버 네트워크 카드 OS TCP 수신 버퍼 → 애플리케이션
- 서버 애플리케이션에서 아직 읽지 않았기 때문에, 서버 OS의 TCP 수신 버퍼에서 대기하게 된다. OS에 있는 소켓에서 메시지를 inputstream으로 빨아 당겨야 처리할 수 있는 것이다.
- 클라이언트
- 여기서 핵심적인 내용이 있는데, 소켓 객체 없이 서버 소켓만으로도 TCP 연결은 완료된다는 점이다.
- 여기서 핵심적인 내용이 있는데, 소켓 객체 없이 서버 소켓만으로도 TCP 연결은 완료된다는 점이다.
- V2 구조에서는 새로운 클라이언트가 접속했을 때 서버의
main스레드는accept()메서드를 절대로 호출할 수 없다.- 2개의 블로킹 작업: 별도의 스레드로 처리해야 한다.
accept(): 클라이언트와 서버의 연결을 처리하기 위해 대기readXxx(): 클라이언트의 메시지를 받아서 처리하기 위해 대기- 각각의 블로킹 작업은 별도의 스레드에서 처리해야 한다. 그렇지 않으면 다른 블로킹 메서드 때문에 계속 대기할 수 있다.
- 2개의 블로킹 작업: 별도의 스레드로 처리해야 한다.
- 새로 연결한
/네트워크 프로그램3: 여러 클라이언트가 동시에 접속할 수 있는 서버로 개선
- 서버의
main스레드는 서버 소켓을 생성하고, 서버 소켓의accept()를 반복해서 호출해야 함- 새로운 연결이 있을 때 마다
Socket을 반환한다. - 반환된
Socket를 기반으로Runnable을 구현한Session이라는 별도의 객체를 만들고, 새로운 스레드에서 이 객체를 실행한다. Session을 담당하는 스레드는 자신의 소켓이 연결된 클라이언트와 메시지를 반복해서 주고 받는 역할을 담당한다.
- 새로운 연결이 있을 때 마다
Session- Runnable 구현, 별도의 스레드에서 실행됨
private final Socket socket;- socket에서 스트림 가져와서 통신하고, 종료 시 input, output, socket을 닫아준다.
ServerserverSocket.accept();를 while문 안에 넣자.- socket이 반환되면 새로운 세션과, 스레드를 만들고
thread.start()호출
- 문제점
- 클라이언트의 연결을 직접 종료하면 클라이언트 프로세스가 종료되면서, 클라이언트와 서버의 TCP 연결도 함께 종료된다. 이때 서버에서
readUTF()로 클라이언트가 메시지를 읽으려고 하면EOFException이 발생한다. close()하는 코드가 try문 안에 있어,EOFException발생시 catch문으로 점프를 뛰어 자원을 닫지 못하게 된다.- 자바 외부의 자원은 자동으로 GC가 되는게 아니므로 꼭 정리를 해줘야 한다.
- 클라이언트의 연결을 직접 종료하면 클라이언트 프로세스가 종료되면서, 클라이언트와 서버의 TCP 연결도 함께 종료된다. 이때 서버에서
- 리소스 호출 등에서 발생하는 예외를 위한
CallException, 리소스를 닫는 중 발생하는 예외를 위한CloseException- 리소스의
callEx()호출 시CallException발생, 리소스의closeEx()호출 시CloseException발생 - resource1, resource2가 있다고 해보자.
- 리소스의
resource1.call();resource2.callEx();resource2.closeEx();resource1.closeEx();resource2.callEx()를 호출하면서 예외가 발생했다. 예외 때문에 자원 정리 코드가 정상 호출되지 않았다.
/자원 정리2 : 예외가 발생해도 자원이 정리되도록 개선
- resource1과 resource2의 scope를 위해 try 밖에서 선언하고, finally에서 close를 해주자.
- 자원을 생성하다가 예외가 발생할 수도 있다. close 전에 if문으로 null이 아닌 경우에만 닫아주자.
- 문제점:
resource2.closeEx()resource1.closeEx()를 호출하는 과정- resource1의 자원을 닫지 못함 (다른 자원을 닫을 수 없는 문제 발생)
- 더 큰 문제는, callEx()에서 예외가 터지고, closeEx()에서 또 예외가 터지면서, CloseException을 새로 던지게 된다. 이 때, callException 가 아닌 closeException만 던져진다. callException이 사라져버림
- 핵심 예외가 바뀌는 문제가 발생한 것이다. 핵심 예외가 사라져버림. 자원을 닫는 건 핵심 예외가 아님.
/자원 정리3 : 자원 정리중에 발생하는 예외를 처리
- finally 내에서
closeEx()를try - catch로 한 번 더 예외처리를 해준다.closeEx()에서 발생한 예외는 버림으로써,CallException을 살릴 수 있다.- 자원 정리 시점에 발생한 예외는 당장 더 처리할 수 있는 부분이 없으므로, 로그만 남겨서 개발자가 인지하도록 해주자.
- 아쉬운 부분
resource변수를 선언하면서 동시에 할당할 수 없음(try,finally코드 블록과 변수 스코프가 다른 문제)catch이후에finally호출, 자원 정리가 조금 늦어진다.- 개발자가 실수로
close()를 호출하지 않을 가능성,close()호출 순서를 실수할 가능성 존재
/자원 정리4 : try-with-resources 로 개선
- Resource 객체가
AutoCloseable을 구현하도록 하고,close()를 override 해주자. try (자원 선언과 동시에 할당)하고, catch 문에서는 단순히CallException을 던지도록 해주자.CallException이 발생하면,try-with-resources로 인해close()를 자동 호출하게 된다.close()내에서 발생하는 예외는 어떻게 처리될까?try-with-resource는 close 중에 발생한 예외는 핵심 예외가 아니라고 생각하고 예외가 원래 발생되었던 CallException에 closeException을 넣어준다.(Suppressed) 결국에는 CallException이 던져진다.
try-with-resources장점- 리소스 누수 방지, 코드 간결성 및 가독성 향상, 스코프 범위 한정, 조금 더 빠른 자원 해제, 자원 정리 순서
- 부가 예외 포함
- 부가 예외는 핵심 예외안에
Suppressed로 담아서 반환한다. - 개발자는 자원 정리 중에 발생한 부가 예외를
e.getSuppressed()를 통해 활용할 수 있다.
- 부가 예외는 핵심 예외안에
try-with-resources는 항상 사용할 수 있는게 아니다. 어떤 경우에는 지저분하게 처리해야 한다
'Java > IO, Network' 카테고리의 다른 글
| [Java] 52. 채팅 프로그램(feat. Command Pattern) (0) | 2025.07.01 |
|---|---|
| [Java] 51. 네트워크 - 프로그램(2) (0) | 2025.07.01 |
| [Java] 49. File, Files (0) | 2025.07.01 |
| [Java] 48. 자바 I/O 활용 (0) | 2025.07.01 |
| [Java] 47. 자바 IO 기본(2) (0) | 2025.07.01 |