티스토리 뷰

시리즈/Network

TCP UDP

빅또리 2021. 2. 16. 15:38

Tcp vs Udp

출처 : https://sites.google.com/site/ciscoch9/home

 

둘 다 OSI 7 layer에서 4번째 계층인 transport layer의 프로토콜이다.

 

network layer의 IP가 IP주소, 라우팅 (라우터에 도착할 때마다 hop-by-hop으로 다음으로 거쳐야 할 주소 찾는 방식으로 경로 찾기) 등의 기술을 이용해서 목적지 인터페이스까지 찾아갈 수 있도록 하는 일을 담당한다면

 

transport layer는 양 끝단에서의 프로토콜이다.

(TCP happens in the kernel. Not in user processes!)

커널(OS)이 처리해서 해당 포트에 연결되어 있는 프로그램에 application payload를 (대표적으로 HTTP request, respone 등) 올려 보내 주는 것까지가 이 계층에서 일어나는 일이다.

 

가장 큰 차이점은

tcp는 두 호스트 간에 connection을 성립한 뒤에 데이터를 주고받는데

(3-way handshake로 시작하고, 종료시에도 서로 종료를 원한다는 메시지를 주고받는 close sequence가 있음)

udp는 그냥 편지에 주소(포트 번호) 적고 바로 메세지 동봉해서 전달하듯이 데이터를 전송하는 connectionless 프로토콜 이라는 점이다.

 

+ tcp는 데이터가 확실히 목적지에 도달하는 것을 보장(누락되면 다시 전송하도록 함), flow control (window size 조절)등의 추가적인 기능을 제공한다.

 

TCP 위에서 작동하는 프로토콜 : HTTP, HTTPs, FTP, SMTP, Telnet

UDP 위에서 작동하는 프로토콜 : DNS, DHCP, TFTP, SNMP, RIP, VoIP

 

참고 : https://www.geeksforgeeks.org/differences-between-tcp-and-udp/?ref=lbp

 

 

 

 

 

 

 

 

 

 

TCP

 

3-way handshake & closing sequence

 

 

 

객체 단위로 추상화 된 수준

 

사실 위의 tcp 과정들은 커널 단에서 일어나는 일이고 우리가 Java나 C 등으로 유저 단에서 tcp, udp등의 소켓 프로그램을 짠다는 것은 시스템 콜을 이용할 수 있게 그 언어가 제공하는 API를 사용하는 것이다.

그러므로 우리가 느끼기에는 객체 단위로 추상화된다.

 

시스템 호출 또는 시스템 콜(system call)은 운영 체제의 커널이 제공하는 서비스에 대해, 응용 프로그램의 요청에 따라 커널에 접근하기 위한 인터페이스이다 [ 위키피디아 ] 

 

 

다음의 내용은 [자바의 정석 - 남궁성 저]라는 책을 참고했다.

서버 소켓은 소켓 간의 연결만 처리하고 실제 데이터는 소켓들끼리 서로 주고받는다. 소켓들이 데이터를 주고받는 연결통로는 바로 입출력 스트림이다. [자바의 정석]

 

 

제일 간단한 버전의 코드 예시들을 보자

 

java코드

(tcp 서버)

import java.net.*;
import java.io.*;
import java.util.Date;
import java.text.SimpleDateFormat;

public class TcpIpServer {
	public static void main(String args[]) {
		ServerSocket serverSocket = null;
		
		try {
			// 서버소켓을 생성하여 7777번 포트와 결합(bind)시킨다.
			serverSocket = new ServerSocket(7777);
			System.out.println(getTime()+"서버가 준비되었습니다.");

		} catch(IOException e) {
			e.printStackTrace();
		}
		
		while(true) {
			try {
				System.out.println("연결요청을 기다립니다.");
				// 서버소켓은 클라이언트의 연결요청이 올 때까지 실행을 멈추고 계속 기다린다.
                		// 클라이언트의 연결요청이 오면 클라이언트 소켓과 통신할 새로운 소켓을 생성한다.
                
                		// ↓ ★ (블로킹 되는 지점)
				Socket socket = serverSocket.accept();
				System.out.println(getTime()+ socket.getInetAddress() + "로부터 연결요청이 들어왔습니다.");
				
				// 소켓의 출력스트림을 얻는다.
				OutputStream out = socket.getOutputStream();
				DataOutputStream dos = new DataOutputStream(out);

				// 원격 소켓(remote socket)에 데이터를 보낸다.
				dos.writeUTF("[Notice] Test Message1 from Server.");
				System.out.println("데이터를 전송했습니다.");

				// 스트림과 소켓을 닫아준다.
				dos.close();
				socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		} // while
	} // main
} // class

(tcp 클라이언트)

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

public class TcpIpClient {
	public static void main(String args[]) {
		try {
			String serverIp = "127.0.0.1";
            		//서버 소켓에 연결요청
			Socket socket = new Socket(serverIp, 7777); 

			// 소켓의 입력스트림을 얻는다.
			InputStream in = socket.getInputStream();
			DataInputStream dis = new DataInputStream(in);

			// 소켓으로 부터 받은 데이터를 출력한다.
			System.out.println("서버로부터 받은 메시지 :"+dis.readUTF());      
			System.out.println("연결을 종료합니다.");

			// 스트림과 소켓을 닫는다.
			dis.close();
			socket.close();
			System.out.println("연결이 종료되었습니다.");
		} catch(ConnectException ce) {
			ce.printStackTrace();
		} catch(IOException ie) {
			ie.printStackTrace();
		} catch(Exception e) {
			e.printStackTrace();  
		}  
	} // main
} // class

코드 출처 : [자바의 정석] 

 

 

node.js코드

(tcp 서버)

var net = require('net');

var server = net.createServer(function(socket) {
    // connection event
    console.log('클라이언트 접속');
    socket.write('Welcome to Socket Server');

    socket.on('data', function(chunk) {
        console.log('클라이언트가 보냄 : ',
        chunk.toString());
    });

    socket.on('end', function() {
        console.log('클라이언트 접속 종료');
    });
});

server.on('close', function() {
    console.log('Server closed');
});

server.listen(3000);

(tcp 클라이언트)

var net = require('net');

var ip = '127.0.0.1';
var port = 3000;

var socket = new net.Socket();
//서버에 연결 요청
socket.connect({host:ip, port:port}, function() {
   console.log('서버와 연결 성공');
  
    socket.on('data', function(chunk) {
        console.log('서버가 보냄 : ',
        chunk.toString());        
    });

    socket.on('end', function() {
        console.log('서버 연결 종료');
    });
});

코드 출처 : https://kimyc1223.github.io/2019-11-27-HoloLens004/

 

node에서 tcp는 net모듈을 이용하면 된다.

net.socket 객체는 Duplex 스트림을 구성. writable, readable 스트림을 제공

write() 메서드로 보내고, data 이벤트 핸들러로 받는다.

 

java에서의 ServerSocket과 Socket은 각 node코드의 server와 (연결 후 실행될 콜백의)socket에 대응한다고 보면 될 것 같다.

 

java는 동기적인 방식으로 한 줄씩 차례로 실행되는 형태라 서버가 클라이언트 요청을 기다리는 동안 그 줄에서 블로킹되고, 다발적으로 여러 처리를 하려면 쓰레드를 이용하는 식인데

 

node.js는 싱글 스레드, 이벤트 기반 비동기 방식. 이벤트가 호출되면 등록해둔 콜백 함수를 실행하는 방식이라 코드가 한 줄 한 줄 순서대로 연관성이 있다기보다 각 이벤트에 맞는 처리 함수를 등록해두는 형식으로 되어있다.

내가 느끼기엔 노드 코드가 더 간단하고 직관적인 것 같다.

 

 

 

 

 

 

 

 

 

 

 

 

UDP

 

java코드

UDP는 연결 지향적인 프로토콜이 아니기 때문에 TCP의 ServerSocket처럼 연결 요청을 처리할 객체는 필요 없는 듯하다.

DatagramPacket에 메시지를 담아서 DatagramSocket을 통해 통신하면 된다.

 

(udp 서버)

import java.net.*;
import java.io.*;
import java.util.Date;
import java.text.SimpleDateFormat;

public class UdpServer {
	public void start() throws IOException {
		// 포트 7777번을 사용하는 소켓을 생성한다.
		DatagramSocket socket = new DatagramSocket(7777);
		DatagramPacket inPacket, outPacket;

		byte[] inMsg = new byte[10];
		byte[] outMsg;

		while(true) {
			// 데이터를 수신하기 위한 패킷을 생성하고,
            		// 패킷을 통해 데이터를 수신(receive)한다.
			inPacket = new DatagramPacket(inMsg, inMsg.length);	
			socket.receive(inPacket);

			// 수신한 패킷으로 부터 client의 IP주소와 Port를 얻는다.
			InetAddress address = inPacket.getAddress();
			int port = inPacket.getPort();
    		
            		// 패킷을 생성해서 client에게 전송(send)한다.
            		outMsg = "Hello, I'm Server";
			outPacket = new DatagramPacket(outMsg, outMsg.length, address, port);
			socket.send(outPacket);
		}
	} // start()

	public static void main(String args[]) {
		try {
			new UdpServer().start(); // UDP서버를 실행시킨다.
		} catch (IOException e) {
			e.printStackTrace();
		}
	} // main
}

(udp 클라이언트)

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

public class UdpClient {
	public void start() throws IOException, UnknownHostException {
		DatagramSocket datagramSocket = new DatagramSocket();
		InetAddress    serverAddress  = InetAddress.getByName("127.0.0.1");

		// 데이터가 저장될 공간으로 byte배열을 생성한다.
		byte[] msg = new byte[100];

		DatagramPacket outPacket = new DatagramPacket(msg, 1, serverAddress, 7777);
		DatagramPacket inPacket  = new DatagramPacket(msg, msg.length);

		datagramSocket.send(outPacket);   // DatagramPacket을 전송한다.
		datagramSocket.receive(inPacket); // DatagramPacket을 수신한다.

		System.out.println("message from server :" + new String(inPacket.getData()));

		datagramSocket.close();
	} // start()

	public static void main(String args[]) {
		try {
			new UdpClient().start();
		} catch(Exception e) {
			e.printStackTrace();
		}
	} // main 
}

코드 출처 : [자바의 정석] 

 

 

node.js코드

(udp 서버)

var dgram = require('dgram');
var socket = dgram.createSocket('udp4');
socket.bind(3000);

socket.on('message', function(msg, rinfo) {
    console.log('메세지 도착', rinfo.address, msg.toString());
});

socket.on('close', function() {
    console.log('close event');
});

(udp 클라이언트)

var dgram = require('dgram');
var socket = dgram.createSocket('udp4');

var msg = new Buffer('Hello UDP Receiver');
socket.send(msg, 0, msg.length, 3000, '127.0.0.1',
    function(err) {
        console.log(err);
        if ( err ) {
            console.log('UDP message send error', err);
            return;
        }
        console.log('메세지 전송 성공');
        socket.close();        
    }
);

코드 출처 : https://kimyc1223.github.io/2019-11-27-HoloLens004/

 

node의 udp는 dgram모듈을 이용하면 된다.

dgram모듈의 socket객체를 사용해 send 메서드로 보내고, message 이벤트로 받는다.

댓글
공지사항
최근에 올라온 글