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();

@유호건

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

검색 태그