[포스코DX|Java|3주] 10일차 수업
포스코DX X 비트교육센터 6기 - TCP / 비동기 프로그램 / 리눅스
비동기😊
non-blocking -> callback
응답이 크게 필요없는 경우, 비동기를 이용한다.
요즘 유행 - 비동기 (알림기능 가능
카카오톡 - 웹 소켓 기반의 비동기 채팅
인터넷: 프로그램과 프로그램의 연결 소통방법 : TCP, UDP
TCP 통신
- 패킷 통신 : 데이터를 쪼갬으로써 다양한 경로로 연결이 가능함.
- 특징 : 3-way handshake
- 확인 과정을 통한 신뢰성 (응답확인) + 시퀀스 번호를 통해 소통이 너무 느리거나 빨라도 시퀀스를 통해 순서대로 전달이 가능하다.
통신 과정
- 인터넷의 데이터는 소켓 버퍼에 쌓인다.
- 데이터를 얼마만큼 읽어야하는지 알 수 없다.
- 데이터 크기 명시 필요 (\r\n)
TCP 구조
TCP 헤더에 담겨 있는 정보들.
- 신뢰성이 있다. : 정보를 꼭 보내준다.
- windowSize : 사이즈를 통해 공간이 생기면 stream을 관리함.
TCP 연결
TCP/IP = 3way
- 따르릉 따르릉 : syn싱크 신호
- 여보세요(전화받은사람) : syn싱크 + ack
- 여보세요 : ack
TCP 데이터 송수신
- ACK 번호 = SEQ 번호 + 전송된 바이트 크기 + 1
TCP 데이터 연결종료
- 연결종료에서는 바로 뚝! 끊지 않는다. (우아한 종료)
- 상대방이 전송할 데이터가 남아 있을 지 모르니 상호간에 연결종료에 대한 합의과정을 거친다.
- 패킷 안에 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
- 마지막 패킷이 제대로 전송이 되었는지를 확인하기 위해 필요한 것
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
리눅스
- redhat -> RHEL , CentOs12(x) -> Rocky
- debian -> ubuntu
어댑터에 브리지로 하는 이유 : 내부 NAT를 거치지 않고 바로 네트워크 접근이 가능해진다.