TCP
연결 지향적 프로토콜
(1) 클라이언트가 연결 요청, 서버가 연결 수락하면 통신 선로 고정
(2) 통신 선로를 통해 데이터가 순차적으로 전달
ServerSocket과 Socket
자바는 java.net.ServerSocket과 java.net.Socket을 제공한다.
ServerSocket은 클라이언트의 연결 요청 대기, 연결 수락 역할을 한다.
Socket은 두 서버 간의 통신에서 데이터를 주고 받는 엔드포인트 역할을 한다.
I/O Stream
통신이 연결되었다면 엔드포인트에서 데이터가 왔다갔다 한다.
자바에서 데이터는 Stream을 통해 이동한다.
Stream은 단방향이므로 데이터 발신, 수신을 위한 Stream이 각각 필요하다.
OutputStream
발신을 위한 스트림이며 바이트 기반 출력 스트림의 최상위 클래스
write(byte[] b) 메소드로 매개값으로 주어진 바이트 배열의 모든 바이트를 출력 스트림으로 보낸다.
데이터들은 출력 스트림 내부 버퍼에 쌓여있다가 순서대로 출력된다.
flush() 메소드는 버퍼에 있는 데이터들을 모두 출력하고 버퍼를 비운다.
InputStream
수신을 위한 스트림이며 바이트 기반 입력 스트림의 최상위 클래스
read(byte[] b) 메소드로 매개값으로 주어진 바이트 배열의 길이만큼 바이트를 읽고, 배열에 저장하고, 읽은 바이트 수를 리턴한다.
더 이상 읽을 바이트가 없으면 -1을 리턴한다.
Blocking
ServerSocket의 accept() 메소드는 클라이언트가 연결 요청하기 전까지 블로킹이 된다.
위의 write(), read() 메소드 등의 I/O 작업도 블로킹이다.
블로킹이란 작업이 완료될 때까지 대기 상태이며 다른 작업을 하지 않는 방식이다.
간단한 구현
서버 코드
public class ServerExample {
public static void main(String[] args) {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(); // 클라이언트의 연결 요청을 기다리면서 연결 수락을 담당하는 ServerSocket 생성
serverSocket.bind(new InetSocketAddress("localhost", 5001)); // 클라이언트가 접속할 포트 지정 -> port binding
while(true) {
System.out.println("connect request waiting ...");
Socket socket = serverSocket.accept(); // 클라이언트 연결 수락, 클라이언트가 요청하기 전까지 스레드는 대기 상태 -> blocking
InetSocketAddress isa = (InetSocketAddress) socket.getRemoteSocketAddress();
System.out.println("connect accept " + isa.toString());
InputStream is = socket.getInputStream();
byte[] isBytes = new byte[100];
int readyByteCount = is.read(isBytes);
String isMessage = new String(isBytes, 0, readyByteCount, "utf-8");
System.out.println("data 받기 성공 " + isMessage);
OutputStream os = socket.getOutputStream();
String osMessage = "Hello Client from Server";
byte[] osBytes = osMessage.getBytes(StandardCharsets.UTF_8);
os.write(osBytes);
os.flush();
System.out.println("data 보내기 성공");
is.close();
os.close();
socket.close();
}
} catch (Exception ex) {
}
if(!serverSocket.isClosed()) {
try {
serverSocket.close(); // 클라이언트 연결 수락 요청이 필요 없으면 바인딩 해제 그래야 다른 프로그램에서 해당 포트 재사용 가능 -> unbinding
} catch (Exception ex1) {
}
}
}
}
클라이언트 코드
public class ClientExample {
public static void main(String[] args) {
// write your code here
Socket socket = null;
try {
socket = new Socket();
System.out.println("connect request");
socket.connect(new InetSocketAddress("localhost", 5001));
System.out.println("connect success");
OutputStream os = socket.getOutputStream();
String osMessage = "Hello Sever from Client";
byte[] osBytes = osMessage.getBytes(StandardCharsets.UTF_8);
os.write(osBytes);
os.flush();
System.out.println("data 보내기 성공");
InputStream is = socket.getInputStream();
byte[] isBytes = new byte[100];
int readByteCount = is.read(isBytes);
String isMessage = new String(isBytes, 0, readByteCount, "utf-8");
System.out.println("data 받기 성공 " + isMessage);
os.close();
is.close();
} catch (Exception ex) {
}
if(!socket.isClosed()) {
try {
socket.close();
} catch (IOException ex1) {
}
}
}
}
로컬 환경에서 서버와 클라이언트를 각각 실행시켜보자
연결 요청 대기, 요청, 수락, data 주고 받기가 성공되었다.
참고
신용권, 이것이 자바다
'Study > Java' 카테고리의 다른 글
[Java] 제네릭 (0) | 2023.02.01 |
---|---|
JVM 명세 - Run-Time Data Areas (0) | 2022.06.25 |
자바로 간단한 http 웹 서버 구현 (0) | 2022.06.11 |
자바 빌더 패턴 Java Builder Pattern (0) | 2022.05.31 |
[JAVA] 메인메소드 public static void main(String[] args)에 대해 (0) | 2018.09.16 |