네트워크 프로그래밍
- 다른 장비와 데이터를 주고 받는 작업을 네트워킹(networking)이라고 한다.
- 네트워킹은 OSI 7 layer로 구분되어 있다.
- OSI 7 layer 중 자바에서 활용하는 대표적인 레이어들은 무엇이 있을까?
- Application layer의 HTTP(Hypertext Transfer Protocol), FTP(File Transfer Protocol), Telnet은 모두 TCP 통신을 한다.
- TCP 통신을 한다면 Transport layer에서의 처리를 자바 API 사용 시 알아서 처리해준다.
- Transport layer의 TCP(Transmission Controll Protocol)는 "연결 기반 프로토콜"로 상대방이 데이터를 받았는지 확실히 보장할 수 있지만 UDP(User Datagram Protocol)는 다른 장비가 데이터를 제대로 받았는지에 대해서 보장을 하지 못한다.
- TCP는 데이터 전송에 대한 보장을 받는 대신 처리 절차가 복잡하고 비싸고 느리며 무겁다.
- 데이터 유실이 발생해도 상관 없는 경우 UDP를 사용해 주고 받을 수 있다.
- Transport layer에서 사용하는 것 중 하나인 Port는 일반적인 웹 애플리케이션에서 80이라는 포트를 사용한다.
- 다른 번호를 사용하고 싶을 경우 주소 뒤 콜론+번호(:99) 형태로 사용하면 된다.
- SSL 통신 시 443 포트를 사용한다.
- 정해져 있는 포트는 되도록 다른 용도로 사용하지 않는 것이 좋으며 0 ~ 1023 까지의 번호는 사용하는 것이 제한되어 있다. ( 0~1023 제외하고 16비트인 65,535까지 임의로 사용 가능)
Socket 클래스
- TCP 통신을 자바에서 수행 시 java.net.Socket 클래스를 사용한다.
- Socket 클래스는 서버든 클라이언트든 원격에 있는 장비와의 연결 상태를 보관하고 있다.
- Socket 클래스는 데이터를 보내는 쪽(보통 클라이언트)에서 객체를 생성해 사용한다.
- 데이터를 받는 쪽(보통 서버)에서 클라이언트 요청을 받으면 요청에 대한 Socket 객체를 생성 후 데이터를 처리한다.
- 서버에서는 ServerSocket이라는 클래스를 사용해 데이터를 받는다.
- Socket 클래스는 new 키워드로 생성자를 만들 필요 없이 ServerSocket 클래스에서 제공하는 메소드에서 클라이언트 요청이 생기면 Socket 객체를 생성해 전달해준다.
- ServerSocket 생성자
- 매개 변수 backlog는 큐의 개수라고 생각하면 되며 ServerSocket 객체가 바빠서 연결 요청을 처리 못하고 대기시킬 때 최대 대기 개수를 지정한다. 미 지정 시 기본 값은 50이다.
- 애플리케이션의 접속이 원활하지 않은 경우 backlog 개수를 증가시키는 것도 하나의 방법이다.
- 매개 변수 InetAddress 클래스의 bindAddr 객체는 특정 주소에서만 접근이 가능하도록 할 때 지정한다.
- 디폴트 생성자를 제외한 생성자로 객체 생성 시 생성하자마자 연결을 대기할 수 있는 상태가 된다.
- 디폴트 생성자는 객체 생성 후 별도로 연결 작업을 통해 연결을 대기할 수 있는 상태로 만들어줘야 한다.
- ServerSocket 클래스의 메소드
- Socket accept() : 객체 생성 후 사용자 요청을 대기하는 메소드
- void close() : 소켓 연결이 끝난 이후 소켓을 닫는 메소드로 close() 처리를 하지 않고 JVM이 계속 동작중이라면 해당 포트는 동작하는 서버나 PC에서 다른 프로그램이 사용할 수 없다.
- 데이터를 보내는 클라이언트에서는 Socket 객체를 직접 생성해야한다.
- Socket 클래스 생성자 중 host와 port를 지정하는 생성자가 가장 편하다.
- 다른 생성자의 경우 별도의 용도가 있는 Socket 객체를 생성하는 용도
- Socket 객체도 close() 메소드로 소켓을 사용 후 닫아줘야 한다.
- 일부 생성자만 객체 생성 시 객체 생성과 동시에 지정된 서버로 접속을 한다.
- 데이터를 받기 위해서 Socket 클래스의 getInputStream() 메소드로 InputStream 객체를 받아 데이터를 받아오면 되고 데이터를 전송하기 위해서는 getOutputStream() 메소드로 OutputStream 객체를 받아 데이터를 전달하면 된다.
- 매개 변수 backlog는 큐의 개수라고 생각하면 되며 ServerSocket 객체가 바빠서 연결 요청을 처리 못하고 대기시킬 때 최대 대기 개수를 지정한다. 미 지정 시 기본 값은 50이다.
// TCP 통신에서 데이터를 받는 서버 소켓
public void startServer() {
ServerSocket server = null;
Socket client = null;
try {
//다른 프로그램에서 서버로 접근 시 9999 포트를 사용해야한다.
server = new ServerSocket(9999);
while (true) {
System.out.println("Server:Waiting for request.");
//다른 원격 호출을 대기하는 상태로 연결 완료 시 client 변수에 Socket 객체를 리턴한다.
client = server.accept();
System.out.println("Server:Accepted");
//데이터를 받기 위해 Socket 클래스의 getInputStream() 메소드로 객체를 받아온다.
InputStream stream = client.getInputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(stream));
String data = null;
StringBuilder receivedData = new StringBuilder();
while ((data = in.readLine()) != null) {
receivedData.append(data);
}
System.out.println("Received data:" + receivedData);
in.close();
stream.close();
if (receivedData != null && "EXIT".equals(receivedData.toString())) {
System.out.println("Stop SocketServer");
break;
}
// 나머지 부분 생략
- new ServerSocket(9999); 를 통해 bind(), listen() 내부 호출한다.
- bind()는 해당하는 소켓에 주소(ip, port)를 할당하는 역할
- listen()은 서버 소켓으로 들어오는 요청을 기다리는 역할
- server.accept(); 를 통해 클라이언트로부터의 연결 요청을 수락한다.
- getInputStream()으로 받아온 InputStream 객체를 통해 데이터를 수신한다.
// TCP 통신에서 데이터를 전송하는 클라이언트 소켓
public void sendSocketSample() {
for (int loop = 0; loop < 3; loop++) {
sendSocketData("I liked java at " + new Date());
}
sendSocketData("EXIT");
}
public void sendSocketData(String data) {
Socket socket = null;
try {
System.out.println("Client:Connecting");
// 같은 IP의 9999 포트로 접속하는 소켓 생성
socket = new Socket("127.0.0.1", 9999);
System.out.println("Client:Connect status=" + socket.isConnected());
Thread.sleep(1000);
// 데이터 전송을 위해 Socket 클래스의 getOutputStream() 메소드로 객체를 받아온다.
OutputStream stream = socket.getOutputStream();
BufferedOutputStream out = new BufferedOutputStream(stream);
byte[] bytes = data.getBytes();
out.write(bytes);
System.out.println("Client:Sent data");
//나머지 부분 생략
- new Socket("127.0.0.1",9999) 를 통해 해당 주소를 가진 ServerSocket에게 연결 요청
- getOutputStream()으로 받아온 OutputStream 객체를 통해 데이터를 전송한다.
Datagram 관련 클래스
- Datagram 관련 클래스 : UDP 통신 시 사용하는 클래스
- UDP는 TCP와 달리 데이터 전달에 대한 보장을 받을 수 없다.
- 데이터 유실이 있어도 상관없을 경우에만 사용하는 것이 좋다.
- 클래스 하나에서 전, 수신의 역할을 모두 수행할 수 있다.
- 데이터 송수신 시 스트림을 사용하지 않고 DatagramPacket 클래스를 사용한다.
DatagramSocket 클래스
- DatagramSocket 클래스의 메소드
- void receive(DatagramPacket packet) : 메소드 호출시 요청을 대기하고 데이터를 받았을 때 packet 객체에 데이터를 저장한다.
- void send(DatagramPacket packet) : packet 객체에 있는 데이터를 전송한다.
- 데이터 전송 시 send() 메소드를 사용하고 수신 시 receive() 메소드를 사용한다.
DatagramPacket 클래스
- DatagramPacket 클래스 생성자
- 여러 생성자 중 하나만 데이터 수신을 위한 생성자이며 나머지는 모두 데이터 전송을 위한 생성자다.
- 매개 변수 byte 배열은 전송되는 데이터를 의미한다.
- 매개 변수 offset은 전송되는 byte 배열의 첫 위치를 의미한다.
- 매개 변수 length는 전송되는 데이터의 크기를 의미한다.
- DatagramPacket 클래스의 메소드
- byte[] getData() : 전송받은 데이터를 byte 배열로 리턴한다.
- int getLength() : 전송받은 데이터의 길이를 리턴한다.
// UDP 통신에서 데이터를 수신 받는 Datagram 서버 소켓
public void startServer() {
DatagramSocket server = null;
try {
server = new DatagramSocket(9999);
int bufferLength = 256;
byte[] buffer = new byte[bufferLength];
DatagramPacket packet = new DatagramPacket(buffer, bufferLength);
while (true) {
System.out.println("Server:Waiting for request.");
server.receive(packet);
int dataLength = packet.getLength();
System.out.println("Server:received. Data length=" + dataLength);
String data = new String(packet.getData(), 0, dataLength);
// 이후 내용 생략
- new DatagramSocket(9999); 를 통해 포트번호를 지정해 객체를 생성한다.
- new DatagramPacket(buffer, bufferLength); 를 통해 데이터를 받기 위한 DatagramPacket 객체를 생성한다.
- byte 배열과 배열의 크기를 지정해준다.
- server.receive(packet); 을 통해 데이터를 받기 위해 대기하다가 데이터가 넘어오면 packet 객체에 데이터를 담는다.
- String data = new String(packet.getData(), 0, dataLength);
- packet 객체를 통해 byte 배열로 넘어온 데이터를 String 생성자를 통해 문자열로 변경한다.
// UDP 통신에서 데이터를 전송하는 Datagram 클라이언트 소켓
public void sendDatagramData(String data) {
try {
DatagramSocket client = new DatagramSocket();
InetAddress address = InetAddress.getByName("127.0.0.1");
byte[] buffer = data.getBytes();
DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length, address, 9999);
client.send(packet);
// 이후 내용 생략
- InetAddress.getByName("127.0.0.1"); 을 통해 데이터를 받을 서버의 IP를 지정한다.
- new DatagramPacket(buffer, 0, buffer.length, address, 9999);
- 데이터를 전송하기 위해 서버 주소를 지정해 DatagramPacket 객체를 생성한다.
- 서버 IP와 포트번호등 주소를 지정하면 데이터를 수신하기 위한 객체가 아닌 전송하기 위한 객체다.
- client.send(packet); 을 통해 packet 객체의 데이터를 전송한다.
TCP / UDP 프로그램의 차이를 애플리케이션에 적용하면?
- TCP와 다르게 UDP는 데이터 송수신을 보장해주지 않는다고 했다.
- 그렇기에 프로그램을 작성해서 UDP로 데이터를 전송을 할 때 서버가 준비가 되지 않아도 클라이언트는 예외를 발생시키지 않고 그냥 수행한다.
- TCP의 경우 서버가 준비되어 있지 않으면 클라이언트에서 ConnectException 예외를 발생시킨다.
간단 내용 정리
1. TCP 통신과 UDP 통신의 차이는 무엇인가요?
TCP는 데이터 전송에 대한 보장을 받을 수 있지만 UDP 통신은 데이터 전송에 대한 보장을 받을 수 없다.
2. 네트워크 통신시 포트 번호를 지정하는 이유는 무엇인가요?
지정된 용도에 맞게 서로 다른 데이터를 처리해주기 위해서
3. ServerSocket 클래스의 용도는 무엇인가요?
TCP로 데이터를 통신하기 위해서 사용한다.
4. ServerSocket 클래스의 accept() 메소드의 용도는 무엇인가요?
데이터를 전송해줄 클라이언트의 연결 요청이 올 때까지 대기하다가 연결 요청 시 데이터를 받아서 전달해 줄 Socket 객체를 생성해 전달해준다.
5. Socket 클래스의 용도는 무엇인가요?
데이터를 받아서 처리하거나 데이터를 전송할 때 사용한다.
6. DatagramSocket 클래스의 용도는 무엇인가요?
UDP로 데이터를 통신하기 위해서 사용한다.
7. DatagramPacket 클래스의 용도는 무엇인가요?
UDP로 데이터를 전송하거나 수신할 때 사용한다.
출처: 이상민, <자바의 신 VOL.2> 로드북
'Java > 자바의 신 VOL.2' 카테고리의 다른 글
기말 점검 및 실습 문제 풀이 (1) | 2024.07.06 |
---|---|
11. 이제 주요 API도 알아봤으니 정리해봅시다 (2) | 2024.07.05 |
09. Serializable과 NIO도 살펴봅시다 (0) | 2024.07.03 |
08. 파일에 있는 것을 읽고 쓰려면 아이오를 알아야죠 (1) | 2024.07.03 |
07. 쓰레드는 개발자라면 알아두는 것이 좋아요 (1) | 2024.07.03 |