포스코DX X 비트교육센터 6기 - TCP / 비동기 프로그램 / 리눅스


비동기😊

non-blocking -> callback

응답이 크게 필요없는 경우, 비동기를 이용한다.

요즘 유행 - 비동기 (알림기능 가능

카카오톡 - 웹 소켓 기반의 비동기 채팅


인터넷: 프로그램과 프로그램의 연결 소통방법 : TCP, UDP

TCP 통신

  • 패킷 통신 : 데이터를 쪼갬으로써 다양한 경로로 연결이 가능함.
  • 특징 : 3-way handshake

image

  • 확인 과정을 통한 신뢰성 (응답확인) + 시퀀스 번호를 통해 소통이 너무 느리거나 빨라도 시퀀스를 통해 순서대로 전달이 가능하다.

통신 과정

  1. 인터넷의 데이터는 소켓 버퍼에 쌓인다.
  2. 데이터를 얼마만큼 읽어야하는지 알 수 없다.
  3. 데이터 크기 명시 필요 (\r\n)

TCP 구조

image

TCP 헤더에 담겨 있는 정보들.

  • 신뢰성이 있다. : 정보를 꼭 보내준다.
  • windowSize : 사이즈를 통해 공간이 생기면 stream을 관리함.

TCP 연결

image

TCP/IP = 3way

  • 따르릉 따르릉 : syn싱크 신호
  • 여보세요(전화받은사람) : syn싱크 + ack
  • 여보세요 : ack

image


TCP 데이터 송수신

image

  • ACK 번호 = SEQ 번호 + 전송된 바이트 크기 + 1

TCP 데이터 연결종료

image

  • 연결종료에서는 바로 뚝! 끊지 않는다. (우아한 종료)
  • 상대방이 전송할 데이터가 남아 있을 지 모르니 상호간에 연결종료에 대한 합의과정을 거친다.
  • 패킷 안에 FIN은 종료를 알리는 메시지를 뜻한다.
  • 상호간에 FIN 메시지를 한 번씩 주고 받는다 (4-Way Handshaking)
    • A : B 야, 이제 전화 끊자
    • B : 어~ 잠시만 기달려주셈
    • B : 이제 됐다, 전화 끊으삼
    • A : 끊어야지
    • TIME-WAIT 에 들어감

TCP 옵션

  • 발생 가능한 문제에 대비, 해결하기 위한 옵션이 있다.

소켓 버퍼 사이즈 (Client)

// 1. 소켓 생성
			socket = new Socket();

			// 1-1. 소켓 버퍼 사이즈 확인
			int rcvBufferSize = socket.getReceiveBufferSize();
			int sndBufferSize = socket.getSendBufferSize();
			
			System.out.println(rcvBufferSize + " : " + sndBufferSize);
			
			// 1-2. 소켓 버퍼 사이즈 변경
			socket.setReceiveBufferSize(1024*10);
			socket.setSendBufferSize(1024*10);
			
			rcvBufferSize = socket.getReceiveBufferSize();
			sndBufferSize = socket.getSendBufferSize();
			
			System.out.println(rcvBufferSize + " : " + sndBufferSize);

Time wait

image

  • 마지막 패킷이 제대로 전송이 되었는지를 확인하기 위해 필요한 것

time wait 없애기 (Server)

//1. Server Socket 생성
			serverSocket = new ServerSocket();
			
			// 1-1. FIN-WAIT -> TIME_WAIT 상태에서도 소켓 포트 할당이 가능하도록 하기 위해.
			serverSocket.setReuseAddress(true);

ack를 받지 않겠다. (Client)

//1-3. SO_NODELAY(Nagle Algorithm off)
			socket.setTcpNoDelay(true);
			

소켓에 데이터를 쓰고/읽는 타임아웃을 정한다. read에만 사용이 가능하다. (Client)

//1-4. SO_TIMEOUT
			socket.setSoTimeout(3000);
			

UDP

한 번 보내고 뜻. 없어지면, 다시 안 보냄. 몰라!

  • 비 연결 지향
  • 굳이 thread를 만들지 않아도 다중 지원
  • 속도가 빠름
package udp;

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

public class UdpEchoServer {
	public static final int PORT = 8000;
	public static final int BUFFER_SIZE = 256;

	public static void main(String[] args) {
		DatagramSocket socket = null;
		try {
			//1. 소켓 생성
			socket = new DatagramSocket(PORT);
			
			while(true) {
				//2. 데이터 수신
				DatagramPacket rcvPacket = new DatagramPacket(new byte[BUFFER_SIZE], BUFFER_SIZE );
				socket.receive(rcvPacket); // Blocking
				
				byte[] rcvData = rcvPacket.getData();
				int offset = rcvPacket.getLength();
				
				String message = new String(rcvData, 0 , offset, "utf-8");
				
				System.out.println("[UDP Echo Server] received:" + message);
				
				//3.송신
				byte[] sndData = message.getBytes("utf-8");
				DatagramPacket sndPacket = new DatagramPacket(sndData, sndData.length, rcvPacket.getAddress(), rcvPacket.getPort());
				
				socket.send(sndPacket);
			}
		} catch (SocketException e) {
			System.out.println("[UDP Echo Server] error:" + e);
		} catch (IOException e) {
			System.out.println("[UDP Echo Server] error:" + e);
		} finally {
			if (socket != null && socket.isClosed()) {
				socket.close();
			}
		}
	}
}

package udp;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.util.Scanner;

import echo.EchoServer;

public class UdpEchoClient {
	private static final String SERVER_IP = "127.0.0.1";

	public static void main(String[] args) {
		DatagramSocket socket = null;
		Scanner scanner = null;
		
		try {
			//1. 스캐너 생성
			scanner = new Scanner(System.in);
			
			//2. 소켓 생성
			socket = new DatagramSocket();
			while(true) {
				System.out.print(">");
				String line = scanner.nextLine();
				
				if("exit".equals(line)) {
					break;
				}
				
				//3. 보내기
				byte[] sndData = line.getBytes("utf-8");
				DatagramPacket sndPacket = new DatagramPacket(sndData, sndData.length, new InetSocketAddress(SERVER_IP, UdpEchoServer.PORT));
				
				socket.send(sndPacket);
				
				//4. 받아서 읽기
				DatagramPacket rcvPacket = new DatagramPacket(new byte[UdpEchoServer.BUFFER_SIZE], UdpEchoServer.BUFFER_SIZE );
				socket.receive(rcvPacket); // Blocking
				
				byte[] rcvData = rcvPacket.getData();
				int offset = rcvPacket.getLength();
				
				String message = new String(rcvData, 0 , offset, "utf-8");
				
				System.out.println("<" + message);
				
			}
		} catch (SocketException e) {
			System.out.println("[UDP Echo client] error:" + e);
		} catch (IOException e) {
			System.out.println("[UDP Echo client] error:" + e);
		} finally {
			if(scanner != null) {
				scanner.close();
			}
			if (socket != null && socket.isClosed()) {
				socket.close();
			}
		}
	}
	
	private static void log(String message) {
		System.out.println("[EchoClient] " + message);
	}	
}


Window Chatting Program

image


리눅스

  • redhat -> RHEL , CentOs12(x) -> Rocky
  • debian -> ubuntu

image

image

어댑터에 브리지로 하는 이유 : 내부 NAT를 거치지 않고 바로 네트워크 접근이 가능해진다.

image