2022. 12. 4. 22:22ㆍCS/Network
우리가 흔히 말하는 서버와 클라이언트. 그 둘의 모델에 대해 알아보고, 소켓통신에 대해서도 알아보자.
클라이언트와 서버는 간단하게 말하면 서비스를 요청하는 쪽과 서비스를 제공하는 쪽을 의미한다.
통신은 항상 클라이언트의 요청과 서버의 응답으로 구성된다.
클라이언트
서비스의 요청자를 의미한다. 서버와 통신하는 모든 프로그램이 될 수 있으며, 서버에 요청을 보내 응답을 받는다.
서비스를 사용하는 사용자, 기계 혹은 프로그램이 클라이언트가 될 수 있다.
서버
서비스를 제공하는 컴퓨터. 큰 용량과 성능을 가지고 있으며 클라이언트와 API 통신을 한다.
요즘 대용량 서비스는 microservice 를 활용해 서버를 띄우는 경우가 많다. (ex 도커, 쿠버네티스) 이와같은 배포는 서비스의 수정, 확장성을 용이하게 하는 장점이 있다.
소켓통신
소켓(Socket)은 프로세스가 네트워크를 주고받게 할 수 있게 만든다.
프로세스간 데이터 통신을 위해서는 반드시 소켓을 열어 데이터를 쓰거나 읽어들어야 한다.
소켓 : 프로토콜 + IP주소 + PortNumber 로 정의된다.
- 프로토콜
http 라는 말을 많이 들어보았을것이다.
이는 Hypertext Protocol 의 줄임말로, html 파일을 어떻게 주고받을것인지에 대한 통신 규약(protocol) 이다.
시스템간에 어떻게 통신을 할 것인지 정의한 약속이라 보면 되겠다.
- IP
전세계 컴퓨터마다 고유 식별주소가 부여되는데, 이것이 IP이다. (ex. 13.123.235.64)
터미널 명령어로 ifconfig, 혹은 ipconfig 등으로 확인할 수 있다.
- 포트(port)
포트는 네트워크 상에서 통신하기 위해 호스트 내부적으로 프로세스가 할당받는 고유한 숫자이다. 한 호스트 내에서는 여러프로세스가 돌기 마련인데, 이를 식별하기 위한 값이라고 보면된다.
프로그래밍을 하는 중 스프링은 localhost:8000, 리액트는 3000, mysql 은 3306번 포트로 접속해본 경험이 있을 것이다.
이들 모두 해당 응용프로그램이 디폴트값으로 갖고 있는 포트이다.
포트는 중복 사용할 수 없으며, 해당 포트를 다른 프로세스가 차지하고 있는 경우 kill 해야 해당 포트가 사용가능해진다.
ex) www.google.co.kr:8000 의 의 url이 있다고 해보자.
클라이언트가 서버에게 이 ip addr을 가진 8080 포트에 request url을 보내줘. 라고 요청(request) 을 보낸다.
그럼 socket connection 이 맺어지게 되고 데이터를 주고 받을 수 있게 되는 것이다.
소켓통신은 기본적으로 서버와 클라이언트가 계속 연결을 유지해 실시간으로 하는 양방향 통신이다.
따라서 서버와 클라이언트가 실시간으로 데이터를 주고받는 상황이 필요할 때 사용된다.
자바에서의 소켓통신
자바애서는 소켓 통신을 위한 인터페이스가 잘 구비되어 있어 프로그램하기 편하다.
Socket 클래스를 통해 TCP 통신을, DatagramSocket 클래스로 UDP 통신을 할 수 있다.
TCP란?
- connection-oriented (연결 지향성)
- 양방향으로 바이트 스트림을 전송해 오류 없음을 보장한다.
- 송신된 순서에 중복되지 않게 수신.
- 대용량 데이터 전송에 적합
UDP란?
- connectionless (비 연결성)
- 확실한 전달 보장을 하지 않는다.
- 실시간 멀티미디어 정보를 처리하기 위해 주로 사용됨
예시)
import java.net.*;
import java.io.*;
public class DateClient {
public static void main(String[] args) throws IOException {
/* make connection to server socket*/
Socket socket = new Socket("127.0.0.1", 6013);
InputStream in = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(in));
/* read date from the socket */
String line = null;
while((line=br.readLine())!=null){
System.out.println(line);
}
socket.close();
}
}
import java.net.*;
import java.io.*;
public class DateServer {
public static void main(String[] args) throws Exception{
ServerSocket server = new ServerSocket(6013);
/* Listen for connection */
while(true){
System.out.println("I'm listening...");
Socket client = server.accept();
PrintWriter pout = new PrintWriter(client.getOutputStream(), true);
System.out.println("Now Client is connected...");
/* write the date to the socket */
pout.println(new java.util.Date().toString());
client.close();
}
}
}
DateServer
Serversocket server : 클라이언트 연결을 받아들일 준비를 한다. (대기 상태)
server.accept()를 통해 클라이언트의 연결을 수용한다.
client.getOutputStream(): outstream을 얻어와 소켓에 쓸 준비를 한다.
PrintWriter로 서버가 소켓에다가 글을 쓴다. (현재 날짜)
DateClient
inputstream 을 열어 서버로부터 소켓에 쓰인 데이터를 얻어온다.
클라이언트는 소켓의 데이터를 BufferedReader를 통해 읽어온다.