TCP/IP 통신에서는 통신할 대상 서버를 찾을 때 호스트 이름이 아니라, IP 주소가 필요하다, 예를 들면 google은 다음과 같이 DNS탐색을 할 수 있다.
public class InetAddressMain {
public static void main(String[] args) throws UnknownHostException {
InetAddress localhost = InetAddress.getByName("localhost");
System.out.println(localhost);
InetAddress google = InetAddress.getByName("google.com");
System.out.println(google);
}
}
여기서 Inet이란 인터넷이다.
localhost/127.0.0.1
google.com/142.250.197.142 //계속 바뀌긴 한다.
InetAddress.getByName("호스트명") 메서드를 사용해서 해당하는 IP 주소를 조회할 수 있다.
DataInputStream input = new DataInputStream(socket.getInputStream());
DataOutputStream output = new DataOutputStream(socket.getOutputStream());
Socket 은 서버와 연결되어 있는 연결점이라고 생각하면 된다. Socket을 통해서 서버와 통신할 수 있다.
public class Server {
private static final int PORT = 12345;
public static void main(String[] args) throws IOException {
log("서버 시작");
ServerSocket serverSocket = new ServerSocket(PORT);
log("서버 소켓 시작 - 리스닝 포트: " + PORT);
Socket socket = serverSocket.accept();
log("소켓 연결: " + socket);
DataInputStream input = new DataInputStream(socket.getInputStream());
DataOutputStream output = new DataOutputStream(socket.getOutputStream());
//Client로 부터 문자 받기
String received = input.readUTF();
log("client -> server: " + received);
// 클라이언트에게 문자 보내기
String toSend = received + " World!";
output.writeUTF(toSend);
log("client <- server: " + toSend);
// 자원 정리
log("연결 종료: " + socket);
input.close();
output.close();
socket.close();
serverSocket.close();
}
}
public class Client {
private static final int PORT = 12345; //이건 서버 PORT
public static void main(String[] args) throws IOException {
log("클라이언트 시작");
Socket socket = new Socket("localhost",PORT); //CLIENT PORT는 자동으로 만들어짐
DataInputStream input = new DataInputStream(socket.getInputStream()); //외부 데이터 받을때
DataOutputStream output = new DataOutputStream(socket.getOutputStream()); //외부 데이터 보낼때
log("소켓 연결: "+ socket);
//서버에 문자 보내기
String toSend = "Hello";
output.writeUTF(toSend);
log("client -> server: " + toSend);
//서버로부터 문자 받기
String received = input.readUTF();
log("client <- server: " + received);
//자원 정리
log("연결 종료: "+socket);
input.close();
output.close();
socket.close();
}
}
서버 소켓
서버는 특정 포트를 열어두어야 한다. 그래야 클라이언트가 해당 포트를 지정해서 접속할 수 있다. ServerSocket을 지정한 포트로 만들어야 한다.
- 서버가 12345 포트로 서버 소켓을 열어둔다. 클라이언트는 이제 12345 포트로 서버에 접속할 수 있다.
- 클라이언트가 12345 포트에 연결을 시도한다.
- 이때 OS 계층에서 TCP 3 way handshake가 발생하고, TCP 연결이 완료된다.
- TCP 연결이 완료되면 서버는 OS backlog queue라는 곳에 클라이언트와 서버의 TCP 연결 정보를 보관한다.
- 이 연결 정보를 보면 클라이언트의 IP, PORT, 서버의 IP, PORT 정보가 모두 들어있다
클라이언트와 랜덤 포트: TCP 연결시에는 클라이언트 서버 모두 IP, 포트 정보가 필요하다. 예제에서 사용된 IP 포트는 다음과 같다.
Client: localhost(127.0.0.1), 50000(포트 랜덤 생성) -> 자신의 포트를 지정한 적이 없다.
Server : localhost(127.0.0.1), 12345
Socket socket = serverSocket.accept();
서버에 있는 socket 이 필요함. (ServerSocket X -> 클라이언트가 서버에 접속할 수 있고 backlogQueue에서 정보 꺼낼수만 있게함 )
accept() 를 호출하면 backlog queue에서 TCP 연결 정보를 조회한다. 먄약 TCP 연결 정보가 없다면, 연결 정보가 생성될 때 까지 대기한다. (블로킹)
꺼낼 것이 있다면 해당 정보를 기반으로 Socket 객체를 생성
사용한 TCP연결정보는 Backlog Queue에서 제거된다.
그리고 TCP로 연결되어 있고 Stream을 통해 메세지를 주고 받을 수 있다.
Server 에 둘 이상의 클라이언트가 작동하지 않는 이유는 accept와 readUTF가 blocking 이기 때문에 두개를 별도의 Thread로 만들어서 해결해야 한다.
public class Session implements Runnable {
private final Socket socket;
public SessionV3(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
//run 이라 예외 X
try {
DataInputStream input = new DataInputStream(socket.getInputStream());
DataOutputStream output = new DataOutputStream(socket.getOutputStream());
while (true) {
// 클라이언트로부터 문자 받기
String received = input.readUTF(); // 블로킹
log("client -> server: " + received);
if (received.equals("exit")) {
break;
}
// 클라이언트에게 문자 보내기
String toSend = received + " World!";
output.writeUTF(toSend);
log("client <- server: " + toSend);
}
// 자원 정리
log("연결 종료: " + socket);
input.close();
output.close();
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public class Server {
private static final int PORT = 12345;
public static void main(String[] args) throws IOException {
log("서버 시작");
ServerSocket serverSocket = new ServerSocket(PORT);
log("서버 소켓 시작 - 리스닝 포트: " + PORT);
while (true) {
Socket socket = serverSocket.accept(); // 블로킹
log("소켓 연결: " + socket);
//session을 별도의 thread에서 사용한다.
SessionV3 session = new SessionV3(socket);
Thread thread = new Thread(session);
thread.start();
}
}
}
이런식으로 session을 통해 thread를 사용해서 여러개의 client가 통신되게끔 한다.
자원 반납
//Autocloseable 사용해서 구현함
public class Resource implements AutoCloseable {
private String name;
public ResourceV2(String name) {
this.name = name;
}
public void call() {
System.out.println(name + " call");
}
public void callEx() throws CallException {
System.out.println(name + " callEx");
throw new CallException(name + " ex");
}
@Override
public void close() throws CloseException {
System.out.println(name + " close");
throw new CloseException(name + " ex");
}
}
public class ResourceCloseMain {
public static void main(String[] args) {
try {
logic();
} catch (CallException e) {
System.out.println("CallException 예외 처리");
Throwable[] suppressed = e.getSuppressed();
for (Throwable throwable : suppressed) {
System.out.println("suppressedEx = " + throwable);
}
e.printStackTrace();
} catch (CloseException e) {
System.out.println("CloseException 예외 처리");
e.printStackTrace();
}
}
private static void logic() throws CallException, CloseException {
try (ResourceV2 resource1 = new ResourceV2("resource1");
ResourceV2 resource2 = new ResourceV2("resource2")) {
resource1.call();
resource2.callEx(); // CallException;
} catch (CallException e) {
System.out.println("ex: " + e);
throw e; // CallException;
}
}
}
Try with resources 장점
- 리소스 누수 방지 : 모든 리소스가 제대로 닫히도록 보장한다. 실수로 finally 블록을 적지 않거나, finally블럭 안에서 자원 해제 코드를 누락하는 문제들을 예방할 수 있다.
- 코드 간결성 및 가독성 향상 : 명시적인 close() 호출이 필요 없어 코드가 더 간결하고 읽기 쉬워진다.
- 스코프 범위 한정 : 예를 들어 리소스로 사용되는 resource1,2 변수의 스코프가 try 블럭 안으로 한정된다. 따라서 코드 유지보수가 더 쉬워진다.
- 조금 더 빠른 자원 해제 : 기존에는 try catch finally로 catch 이후에 자원을 반납했다. Try with resources 구분은 try 블럭이 끝나면 즉시 close() 를 호출한다.
- 자원 정리 순서 : 먼저 선언한 자원을 나중에 정리한다.
'컴퓨터 네트워크' 카테고리의 다른 글
네트워크 exception (0) | 2024.12.24 |
---|---|
Network program - 2 (0) | 2024.12.24 |
네트워크 - 기본 이론 (1) | 2024.12.24 |
InputStream, OutputStream (1) | 2024.12.22 |
컴퓨터 네트워크 4장- 라우터 내부 (0) | 2024.05.18 |