while(1) work();
article thumbnail
반응형

개요

몇 년 전에 네트워크 강의를 수강하며 TCP와 UDP의 속도/손실률이 실제로 어느정도 되는지 궁금해졌다.

TCP/UDP 서버와 클라이언트 코드를 작성해서 비교해봤었는데, 따로 포스팅하지 않고 자료만 가지고 있었다.

 

그러다가 최근, 모 기업의 자기소개서를 작성하면서 해당 실험을 언급하게 되었고, 과거의 자료를 찾아 블로그에 포스팅한다.

 

실험 환경

TCP/UDP에 대해 각각 로컬네트워크, AWS서울리전, AWS오하이오리전에서 테스트했다.

또, 패킷의 크기가 400B, 4KB, 40KB일 때 각각 실험하였다.

 

각 환경에서 10ms간격으로 1000번 패킷을 전송하였고, 3s 간격으로 세 번 반복하였다.

(단, AWS환경에서 40KB패킷을 전송할 땐 100번만 전송하였다 ㅡ '결과 분석' 참고)

 

AWS는 t3.medium EC2 인스턴스(2vCPU, 4GB RAM, 5Gbps)를 사용하였다.

로컬 네트워크는 100Mbps의 네트워크 환경이다.

 

실험 결과

불필요한 설명보다는 실험 결과를 보여주는게 나을 것 같아, 바로 결과를 첨부한다.

 

결과 분석

[손실/손상 관련]

● TCP는 모든 패킷, 모든 환경에서 손실이 없었다. (당연함 ㅡ 오류 제어)

● TCP와 UDP 모두 모든 패킷, 모든 환경에서 손상(비트값 손상)이 없었다. (거의 당연함 ㅡ checksum)

● 패킷이 400B일 때를 제외하고 UDP에서는 손실이 발생하였다.

AWS환경에서 패킷이 40KB일 때, UDP는 약 100패킷 이후부터 수신이 불가능하였다. 따라서 100번의 패킷만 전송하였다. (AWS단에서 단시간에 UDP 패킷이 과도하게 발생할 경우 차단되는것으로 추정... 하지만 정확한 원인을 알 수 없었다.)

 

[속도 관련]

로컬/AWS서울 환경에서 패킷이 40B일 때, UDP가 TCP보다 빨랐다.

AWS오하이오 환경에서 패킷이 40B일 때, TCP가 UDP보다 빨랐다. (혼잡제어로 인한 것으로 추정)

 

모든 환경에서 패킷이 4KB일 때, TCP가 UDP보다 빨랐다. (혼잡제어로 인한 것으로 추정)

 

로컬 환경에서 패킷이 40KB일 때, TCP가 UDP보다 빨랐다.

● AWS서울/오하이오 환경에서 패킷이 40KB일 때, UDP가 TCP보다 빨랐다.

 

● AWS오하이오 환경에서 패킷 크기와 관계 없이 속도가 비슷했다.

결론

큰 이유가 없다면 TCP를 사용하는 것이 효율적일 것 같다. (정신 건강에도 좋을 것 같다.)

 

참고 자료

패킷이 400Byte일 때, TCP와 UDP의 속도 차이가 크지 않은 이유

https://stackoverflow.com/questions/51388062/why-is-my-tcp-system-faster-than-udp-one

 

Why is my TCP system faster than UDP one?

I have two (client - server - client) system. First one uses TCP and second one uses UDP. It is interesting that my TCP using system is faster than UDP using one when transferring files in size 5-6...

stackoverflow.com

실험 코드

 모든 코드는 멀티스레딩을 고려해 작성하였다.

 패킷 메시지에는 8Byte단위로 'currentTimeMillis' 값을 반복해서 저장했다. 패킷 수신 시 8Byte단위로 같은 값이 반복되는지 확인해 비트값 손상을 검출하였다.

서버측은 수신 즉시 동일한 데이터를 클라이언트로 송신했다. (SendBack)

클라이언트측은 10ms 단위로 쓰레드를 만들어 서버로 패킷을 전송하였다. (Sender)

import java.net.*;

public class UDPSender { //Send and receive

    private final DatagramSocket socket;

    private final String RECEVER_IP;
    private final int PORT;
    private final int SIZE;
    private final int REPEAT;

    private int COUNT = 0;
    private int WRONG_COUNT = 0;
    private int TIME_SUM = 0;

    private synchronized void COUNT() { COUNT++; }
    private synchronized void WRONG_COUNT() { WRONG_COUNT++; }
    private synchronized void TIME_SUM(int time) { TIME_SUM += time; }

    public static void main(String[] args) throws Exception {
        if (args.length == 0) args = new String[]{"192.168.1.33", "53135", "40000", "100"};

        new UDPSender(args[0], Integer.parseInt(args[1]), Integer.parseInt(args[2]), Integer.parseInt(args[3]));
    }

    public UDPSender(String ip, int port, int size, int repeat) throws Exception {
        this.RECEVER_IP = ip;
        this.PORT = port;
        this.SIZE = size;
        this.REPEAT = repeat;

        this.socket = new DatagramSocket();

        for (int i = 1; i <= REPEAT; i++) {
            send(i);

            Thread.sleep(10);
        }
    }

    private void send(int id) throws Exception {
        new Thread(() -> {
            try {
                byte[] buffer = createData();
                InetAddress address = InetAddress.getByName(RECEVER_IP);
                DatagramPacket packet = new DatagramPacket(buffer, buffer.length, address, PORT);

                socket.send(packet);
                System.out.println(id + " messages sent");
        
                receive(socket);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
    }

    private void receive(DatagramSocket socket) throws Exception {
        byte[] buffer = new byte[SIZE];
        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

        socket.receive(packet);

        long sendTime = validateData(packet.getData());
        long taken = System.currentTimeMillis() - sendTime;
        COUNT();

        if (sendTime == -1) {
            WRONG_COUNT();

            return;
        }

        TIME_SUM((int)taken);

        System.out.println(COUNT + " messages received (including " + WRONG_COUNT + " wrong message) / Time taken " + taken + "ms (Average " + String.format("%.3f", (double)TIME_SUM / COUNT) + "ms)");
    }

    private byte[] createData() {
        long currentTime = System.currentTimeMillis();
        byte[] buffer = new byte[SIZE];

        for (int i = 0; i < SIZE; i += 8) {
            for (int j = 0; j < 8; j++) {
                buffer[i + j] = (byte) (currentTime >> (56 - (8 * j)));
            }
        }

        return buffer;
    }

    private long validateData(byte[] data) {
        long sendTime = -1;

        for (int i = 0; i < SIZE; i += 8) {
            long time = 0;
            for (int j = 0; j < 8; j++) {
                time |= ((long) data[i + j] & 0xff) << (56 - (8 * j));
            }

            if (sendTime != -1 && time != sendTime) {
                return -1;
            }

            sendTime = time;
        }

        return sendTime;
    }

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

public class TCPSender { //Send and receive

    private final String RECEVER_IP;
    private final int PORT;
    private final int SIZE;
    private final int REPEAT;

    private int COUNT = 0;
    private int WRONG_COUNT = 0;
    private int TIME_SUM = 0;

    private synchronized void COUNT() { COUNT++; }
    private synchronized void WRONG_COUNT() { WRONG_COUNT++; }
    private synchronized void TIME_SUM(int time) { TIME_SUM += time; }

    public static void main(String[] args) throws Exception {
        if (args.length == 0) args = new String[]{"192.168.1.33", "53135", "40000", "100"};
    
        new TCPSender(args[0], Integer.parseInt(args[1]), Integer.parseInt(args[2]), Integer.parseInt(args[3]));
    }

    public TCPSender(String ip, int port, int size, int repeat) throws Exception {
        this.RECEVER_IP = ip;
        this.PORT = port;
        this.SIZE = size;
        this.REPEAT = repeat;

        for (int i = 1; i <= REPEAT; i++) {
            send(i);

            Thread.sleep(10);
        }
    }

    private void send(int id) throws Exception {
        new Thread(() -> {
            try {
                Socket socket = new Socket(RECEVER_IP, PORT);

                byte[] buffer = createData();
                OutputStream os = socket.getOutputStream();
                os.write(buffer);
                os.flush();

                System.out.println(id + " messages sent");

                receive(socket);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
    }

    private void receive(Socket socket) throws Exception {
        byte[] buffer = new byte[SIZE];

        InputStream is = socket.getInputStream();
        int left = SIZE;
        while (left > 0) {
            int read = is.read(buffer, SIZE - left, left);

            left -= read;
        }

        long sendTime = validateData(buffer);
        long taken = System.currentTimeMillis() - sendTime;
        COUNT();

        if (sendTime == -1) {
            WRONG_COUNT();

            return;
        }

        TIME_SUM((int)taken);

        System.out.println(COUNT + " messages received (including " + WRONG_COUNT + " wrong message) / Time taken " + taken + "ms (Average " + String.format("%.3f", (double)TIME_SUM / COUNT) + "ms)");
    }

    private byte[] createData() {
        long currentTime = System.currentTimeMillis();
        byte[] buffer = new byte[SIZE];

        for (int i = 0; i < SIZE; i += 8) {
            for (int j = 0; j < 8; j++) {
                buffer[i + j] = (byte) (currentTime >> (56 - (8 * j)));
            }
        }

        return buffer;
    }

    private long validateData(byte[] data) {
        long sendTime = -1;

        for (int i = 0; i < SIZE; i += 8) {
            long time = 0;
            for (int j = 0; j < 8; j++) {
                time |= ((long) data[i + j] & 0xff) << (56 - (8 * j));
            }

            if (sendTime != -1 && time != sendTime) {
                return -1;
            }

            sendTime = time;
        }

        return sendTime;
    }

}
import java.net.*;

class UDPSendBack { //Receive and send

    private final DatagramSocket socket;
    private final int SIZE;

    public static void main(String[] args) throws Exception {
        if (args.length == 0) args = new String[]{"53135", "40000"};

        new UDPSendBack(Integer.parseInt(args[0]), Integer.parseInt(args[1]));
    }

    public UDPSendBack(int port, int size) throws Exception {
        this.SIZE = size;
        this.socket = new DatagramSocket(port);

        while (true) {
            byte[] buffer = new byte[SIZE];
            DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
            socket.receive(packet);
    
            new Thread(() -> {
                DatagramPacket sendPacket = new DatagramPacket(buffer, buffer.length, packet.getAddress(), packet.getPort());

                try {
                    socket.send(sendPacket);
        
                    System.out.println("Received message and send back");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }

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

class TCPSendBack { //Receive and send

    private final ServerSocket serverSocket;
    private final int SIZE;

    public static void main(String[] args) throws Exception {
        if (args.length == 0) args = new String[]{"53135", "40000"};

        new TCPSendBack(Integer.parseInt(args[0]), Integer.parseInt(args[1]));
    }

    public TCPSendBack(int port, int size) throws Exception {
        this.SIZE = size;
        this.serverSocket = new ServerSocket(port);

        while (true) {
            Socket socket = serverSocket.accept();
    
            new Thread(() -> {
                byte[] data = new byte[SIZE];
                try {
                    InputStream is = socket.getInputStream();

                    int left = SIZE;
                    while (left > 0) {
                        int read = is.read(data, SIZE - left, left);

                        left -= read;
                    }

                    OutputStream os = socket.getOutputStream();
                    os.write(data);
                    os.flush();

                    System.out.println("Received message and send back");
                } catch (IOException e) {
                    return;
                }
            }).start();
        }
    }

}

실험 사진

 

반응형
profile

while(1) work();

@유호건

❤️댓글은 언제나 힘이 됩니다❤️ 궁금한 점이나 잘못된 내용이 있다면 댓글로 남겨주세요.

검색 태그