[JAVA] TCP/UDP

JHJeong·2024년 4월 23일
0
post-custom-banner

JAVA에서 네트워크 통신을 하기 위해서는 기본으로 제공되는 클래스는 TCP 통신을 위한 Socket 클래스와 UDP 통신을 위한 Datagram 관련 클래스로 나뉜다.
또한 간단하게 웹에 접속하고 데이터를 처리하는 URL 클래스가 있긴 하지만, 섬세한 설정이 불가능하기 때문에 Apache에서 제공하는 HttpClient 클래스를 사용할 것을 권장한다.

TCP 처리를 위한 Socket

  • Socket 클래스는 데이터를 전달하고, 받기 위해서 사용하는 클래스이다.
  • ServerSocket클래스는 데이터를 수신하기 위해서 사용하는 클래스이다. 클라이언트에서 데이터를 전달하면 Socket 객체로 받아 처리하면된다.
  • 데이터를 주고 받는 것은 Socket 클래스를 통하여 Stream이나 Reader/Writer 객체로 처리하면 된다.
  • TCP는 연결 지향적 프로토콜으로, 데이터 전송 전에 서버와 클라이언트 간의 연결 설정이 필요하다. 이러한 연결 설정은 3Way Handshake 과정을 통해서 이루어지게 되고, 연결해제시에는 4Way Handshake 과정을 통해서 이루어지게된다.

아래는 TCP를 이용한 간단한 Echo Server / Client 소스이다.

import java.io.*;
import java.net.*;

public class EchoServer {
    public static void main(String[] args) throws IOException {
        int port = 3333;
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("Server is listening on port " + port);

        while (true) {
            Socket clientSocket = serverSocket.accept();
            System.out.println("New client connected");

            BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);

            String inputLine;
            while ((inputLine = in.readLine()) != null) {
                System.out.println("Received from client: " + inputLine);
                out.println(inputLine); // Echo the same line back to the client
            }

            clientSocket.close();
            System.out.println("Client connection closed");
        }
    }
}
import java.io.*;
import java.net.*;

public class EchoClient {
    public static void main(String[] args) throws IOException {
        String serverAddress = "127.0.0.1";
        int port = 3333;
        Socket socket = new Socket(serverAddress, port);
        System.out.println("Connected to server");

        PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
        BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));

        String userInput;
        while ((userInput = stdIn.readLine()) != null) {
            out.println(userInput);
            System.out.println("Server replies: " + in.readLine());
        }

        socket.close();
        System.out.println("Socket closed");
    }
}

출력 결과(EchoServer)

Server is listening on port 3333
New client connected
Received from client: hi

출력 결과(EchoClient)

Connected to server
hi
Server replies: hi

UDP를 처리하기 위한 Datagram

  • DatagramPacket 클래스는 데이터를 전달하고, 받기 위해서 사용하는 클래스이다.
  • DatagramSocket는 데이터를 수신하기 위해서 사용하는 클래스이다. 클라이언트에서 데이터를 전달하면 DatagramPacket 객체로 받아 처리하면 된다.
  • TCP는 연결할 때부터 데이터를 받는 서버가 시작되어 있어야만 데이터를 전송할 수 있지만, UDP는 데이터를 받는 서버가 시작 여부 및 데이터 송수신 가능 여부와 상관 없이 데이터를 전송할 수 있다.
  • 실시간 통신에 유리하나, 신뢰성이 낮고 순서가 보장되지 않는다는 특징이 있다.

아래는 UDP를 사용한 간단한 Echo Server / Client 소스이다.

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class EchoUDPServer {
    public static void main(String[] args) {
        try (DatagramSocket socket = new DatagramSocket(4445)) {
            byte[] buffer = new byte[1024];
            
            while (true) {
                DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
                socket.receive(packet); // 패킷 수신
                
                // 수신된 패킷에서 데이터와 클라이언트 주소, 포트 정보 추출
                String received = new String(packet.getData(), 0, packet.getLength());
                System.out.println("Received: " + received);
                
                // 동일한 내용으로 클라이언트에게 패킷 전송
                socket.send(packet);
                System.out.println("Echoed back to client");
            }
        } catch (SocketException e) {
            System.err.println("SocketException: " + e.getMessage());
        } catch (IOException e) {
            System.err.println("IOException: " + e.getMessage());
        }
    }
}
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;

public class EchoUDPClient {
    public static void main(String[] args) {
        try (DatagramSocket socket = new DatagramSocket()) {
            InetAddress address = InetAddress.getByName("localhost");
            String message = "Hello, UDP Server!";
            byte[] buffer = message.getBytes();
            
            // 서버 주소와 포트를 지정하여 패킷 생성
            DatagramPacket packet = new DatagramPacket(buffer, buffer.length, address, 4445);
            socket.send(packet); // 서버로 패킷 전송
            System.out.println("Message sent to server");
            
            // 서버로부터 응답을 받기 위해 패킷을 준비
            packet = new DatagramPacket(buffer, buffer.length);
            socket.receive(packet); // 패킷 수신
            String received = new String(packet.getData(), 0, packet.getLength());
            
            System.out.println("Echo from server: " + received);
        } catch (UnknownHostException e) {
            System.err.println("UnknownHostException: " + e.getMessage());
        } catch (IOException e) {
            System.err.println("IOException: " + e.getMessage());
        }
    }
}

출력 결과(EchoUDPServer)

Received: Hello, UDP Server!
Echoed back to client

출력 결과(EchoUDPClient)

Message sent to server
Echo from server: Hello, UDP Server!

UDP는 TCP처럼 연결지향성이 아니기때문에 EchoClient가 실행되면 미리 정해진 message를 보내고, EchoServer로부터 받아온 메시지를 출력하고 종료하도록 되어있다. 이를 보면서 UDP를 사용하는 경우 연결 설정이나 종료 같은 과정이 필요없고, 각 패킷은 독립적으로 처리된다는 것을 알 수 있다.

추가적으로 네트워크 보안을 강화하기 위해서는 데이터 전송시에 SSL/TLS등을 사용하여 암호화를 적용할 수 있고, 성능 최적화를 위해서 소켓 버퍼를 늘리거나 비동기 I/O를 사용하여 언블로킹 소켓을 사용할 수도 있다. 또한 하나의 프로세스에서 TCP 소켓을 많이 늘려 멀티스레드 환경에서 다수의 클라이언트 요청을 처리하도록 효율적으로 구성할 수도 있다.

profile
이것저것하고 싶은 개발자
post-custom-banner

0개의 댓글