개요
몇 년 전에 네트워크 강의를 수강하며 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
실험 코드
● 모든 코드는 멀티스레딩을 고려해 작성하였다.
● 패킷 메시지에는 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();
}
}
}
실험 사진
'언어 > JAVA' 카테고리의 다른 글
이분탐색 허벌나게 쉽게 구현(기억)하기 (0) | 2023.04.13 |
---|---|
이상하고 아름다운 JAVA 퀴즈 6 (0) | 2023.04.11 |
Permutation, Combination algorithm using bitmasking (0) | 2023.04.06 |
이상하고 아름다운 JAVA 퀴즈 5 (0) | 2023.03.13 |
이상하고 아름다운 JAVA 퀴즈 4 (0) | 2023.03.13 |