네트워크 - 프로그램(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을 닫아준다.
Server
serverSocket.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 |