인터넷 구성요소

  • 호스트, 라우터, 통신 프로토콜

패킷 전송 과정

  • 프로그램 TCP IP 이더넷

TCP 와 UDP

TCP

  • 연결 설정 후 통신 가능
  • 일대일 통신
  • 데이터 경계 구분x (바이트 스트림 서비스)

UDP

  • 연결 설정 없이 통신가능
  • 일대일, 일대다 상관없음
  • 데이터 경계 구분

소켓생성

socket(IP 타입, TCP|UDP, 0);
  • ip 타입
    AF_INET: IPv4
    AF_INET6: IPv6
  • TCP: SOCK_STREAM, UDP: SOCK_DGRAM

IPv6 사용시: AF_INET6, sockaddr_in6

TCP 소켓 처리 과정

서버

  1. WSAStartup(): 소켓 초기화
  2. socket(): 서버 소켓 생성
  3. bind(): 소켓 바인딩 (소켓의 지역 IP 주소와 지역 포트 번호를 결정)
  4. listen(): 클라이언트 연결대기, 클라이언트 얼마나 받을건지 결정
  5. accept(): 클라이언트 접속 수락, 클라이언트 통신을 위한 새로운 소켓 생성
  6. send(): 메시지 보내기 && recv(): 데이터 받기

클라이언트

  1. WSAStartup(): 소켓 초기화
  2. socket(): 클라이언트 소켓 생성
  3. connect(): 서버와 연결 설정, 연결을 위한 패킷 교환
  4. send(): 메시지 보내기 && recv(): 데이터 받기

UDP 소켓 처리과정

서버

  1. WSAStartup(): 소켓 초기화
  2. socket(): 서버 소켓 생성
  3. bind(): 소켓 바인딩 (소켓의 지역 IP 주소와 지역 포트 번호를 결정)
  4. sendto(): 메시지 보내기 && recvfrom(): 데이터 받기

클라이언트

  1. WSAStartup(): 소켓 초기화
  2. socket(): 클라이언트 소켓 생성
  3. sendto(): 메시지 보내기 && recvfrom(): 데이터 받기

TCP 서버 - 클라이언트 동작 원리

  1. 서버는 소켓을 생성, 클라이언트 대기
  2. 클라이언트가 서버에 접속, 연결설정을 위한 패킷 교환 (3-way handshaking 방식)
  3. 서버는 클라이언트와 데이터 송, 수신을 위한 새로운 소켓 생성
  4. 기존에 생성한 소켓은 다른 클라이언트 접속대기

UDP 서버 - 클라이언트 동작 원리

  1. 서버는 소캣 생성, 클라이언트가 데이터 보내길 기다림
  2. 클라이언트는 연결설정 없이 서버와 곧바로 데이터 주고 받음, 이때 서버는 소켓 한 게만 사용
  3. 아무 클라이언트가 접속해서 서버와 통신할 수 있다

TCP 메시지 경계 구분

  1. 항상 고정길이로 보냄

    주고받을 데이터의 길이 변동폭이 크지 않을 때 적합

  2. 가변 데이터를 보내고 특별한 표식(EOR)을 붙임

    생성 데이터의 길이를 미리 알 수 없을 때 적합

  3. 고정 길이 보내고 가변길이 보냄, 수신자는 고정길이 읽고, 뒤에 가변길이 알아냄

    생성 데이터의 길이를 미리 알고 있는 상황에서 구현이 쉽고 처리 효율성이 높음

  4. 모든 가변 데이터 보내고 연결종료

    한쪽에서 일방적으로 가변 길이 데이터를 보낼 때 적합

UDP 데이터 전송

  • 유니캐스팅

    1:1 데이터 전송

  • 브로드캐스팅(IPv4)

    특정 네트워크에 속한 모든 객체

  • 멀티캐스팅

    동일한 그룹에 속한 객체

  • 애니캐스팅(IPv6)

    동일한 그룹에 속한 객체 중 가장 가까운 객체
    그리고 그 객체가 나머지 객체들에게 전송

브로드케스팅

  • 송신자가 보낸 데이터 하나를 다수의 수신자가 받는 방식
  • 소켓 생성 이후 setsockopt() 함수 인자로 SO_BRODCAST 를 넘겨 브로드케스팅 허용

브로드케스트 주소 종류

  • 네트워크 브로드 케스트
    [ip].[ip].[255].[255]
  • 서브넷 브로드케스트
    [ip].[ip].[ip].255
  • 지역 브로드케스트
    255.255.255.255

바이트 정렬

메모리에 데이터 저장때 바이트 배치 순서
ex) 0x12345678

빅엔디언: 0x12, 0x34, 0x56, 0x78
리틀 엔디언: 0x78, 0x56, 0x34, 0x12
  • 바이트 정렬을 고려하는 경우
    1. IP 주소에 문제가 발생하는 상황
    2. 포트 번호에 문제가 발생하는 상황
    3. 응용 프로그램 데이터에서 문제가 발생하는 상황

멀티 쓰레딩

교착 상태 해결

  • 데이터 송수신 부분 잘 설계

    빠른 구현, 모든 경우 해결책x

  • 소켓에 타입아웃 적용

    구현 쉬움, 성능 떨어짐

  • 넌블로킹 소켓 사용하기

    조건 만족하지 않아도 소켓 함수 즉시 리턴으로 교착 해결, 구현복잡 && 자원낭비

  • 입출력 모델 사용

    넌블로킹 단점 보안, 구현복잡

생성

쓰레드 함수

DWORD WINAPI 함수명(LPVOID param){ }

쓰레드 API

CreateThread(NULL, 0, 함수명, 보낼데이터주소, 0, NULL): 쓰레드 생성

종료

  • 함수 안 return
  • 쓰레드 함수 내 ExitThread() 호출
  • 다른 쓰레드가 TerminateThread() 호출

종료 대기

종료 대기를 위해 쓰레드 함수를 HANDLE에 메핑

HANDLE hThread = CreateThread();
  • 한개: WaitForSingleObject(HANDLE 쓰레드, int 대기시간=INFINITE)
  • 여러개: WaitForMultipleObjects(int 대기할개수, HANDLE *쓰레드주소, TRUE, 대기시간=INFINITE)

실행 제어

  • 일시 정지: SuspendThread(HANDLE 쓰레드) || Sleep(HANDLE 쓰레드)
  • 재실행: ResumeThread(HANDLE 쓰레드)
  • 종료: CloseHandle(HANDLE 쓰레드)

동기화

  • [임계영역, 뮤텍스, 이벤트, 세마포어, 대기가능 타이머]

임계영역

  • 두 개 이상의 스레드가 공유 자원에 접근할 때, 오직 한 스레드만 접근을 허용해야 하는 경우에 사용
//쓰레드 입장
CRITICAL_SECTION cs; //전역변수 선언
 
DWORD WINAPI thread() {
	EnterCriticalSection(&cs);
	// 공유 자원 접근
	// 해당 영역이 건드는 모든 변수는 LeaveCriticalSection 를 호출하기 전까지 다른 쓰레드가 건들면 대기시켜 버림
	LeaveCriticalSection(&cs);
}
 
// 메인 입장
main() {
	EnterCriticalSection(&cs);
	// 공유 자원 접근
	// 해당 영역이 건드는 모든 변수는 DeleteCriticalSection 를 호출하기 전까지 다른 쓰레드가 건들면 대기시켜 버림
	DeleteCriticalSection(&cs);
}

이벤트

  • 사건이 발생하기 전 다른 쓰레드에게 알리는 기법
  • 생성
    CreateEvent(NULL, FALSE, FALSE, NULL): HANDLE
  • 이벤트 발생
    SetEvent(HANDLE 이벤트)
  • 이벤트 종료
    ResetEvent(HANDLE 이벤트)

용어정리

  • 소켓: 전송 계층과 응용 프로그램 사이의 인터페이스 역할을 하며 떨어져 있는 두 호스트를 연결함 (전송 프로토콜, ip, 포트번호로 정의)
  • 패킷: IP주소, 포트 번호, 오류코드 + 데이터

함수 정리

  • WSACleanup() 소켓 끝 마무리
  • closesocket(SOCKET): 소켓 종료
  • gethostbyname(Char * 도메인): 호스트 이름 ip주소
  • gethostbyaddr(char *ip주소, int 길이, int ip타입): ip 주소 호스트 이름
  • inet_ntop(int ip타입, Char *반환할 데이터, void *담을주소): 바이트 ip 주소 실제 ip 주소
  • inet_pton(int ip타입, void *변환값주소, 버퍼, 길이): 실제ip 주소 바이트 ip 주소
  • (자료형*)malloc(sizeof(자료형)): 동적 메모리 할당
    (p[0], p[1]…) 이렇게 할당하는 순간 해당 하는 만큼 메모리를 할당해서 무한대로 쓸 수 있음

바이트 정렬함수

  • htons(): 2바이트 호스트 바이트 네트워크 바이트(빅엔디안 으로 변환)
  • htonl(): 4바이트 호스트 바이트 네트워크 바이트(빅엔디안 으로 변환)

    프로그램이 소켓에 넘겨주기 전

  • ntohs(): 2바이트 네트워크 바이트 호스트 바이트
  • ntohl(): 4바이트 네트워크 바이트 호스트 바이트

    소켓이 전송한 결과를 받아볼 때

클라이언트는 hton* 로 전송하고 서버는 ntoh* 로 받으면 됨