티스토리 뷰

190708-1
1일차
 

교육을 받으면서 노트필기 했던 내용을 날것 그대로 업로드합니다. 


TCP/IP 프로토콜로 
 
윈도우즈에서 제공하는 네트워크 프로그래밍 방식을 공부한다.
 
Windows Socket : 현재 비동기, 넌 블로킹을 위한 2.2가 주력.
 
● 네트워크는 end system이라고 부르는 컴퓨터 또는 하드웨어가 2개 이상 연결되어 있는 것을 말한다. 컴퓨터 사이에 통신가능하도록 2개이상의 장치가 연결된 것이다. 
 
● 프로로토콜 : end system과 (라우터간 등)통신을 위한 약속
 
● 컴터는 모두 1대 1로 연결되있다. 기본적으로는 1대 1만 통신이 가능하다. 네트워크가 1대 1로 맞물려 이는 것을 로컬 네트워크라고한다.
 
● 출발위치 목적지를 실어서 데이터를 보낸다.
 
● 라우터, 게이트웨이
 
● OSI 7계층, TCP/IP 계층
 
애플리케이션
FTP,HTTP,TELNET,SMTP,MIME,SNMP...
전송
TCP/IP
인터넷
IP
네트워크 액세스
네크워크,디바이스 드라이버
지금은 무조건 TCP/IP를 거의 쓴다. TCP레벨에서하는 것은 os가 다 해주고 그 윗단계를 우리가 받는 것이다. 
 
● TCP vs UDP
 
● 데이터 경계를 구분하지 않음 : 편지처럼 한통 두통 이렇게 쓰지 않는다는 것이다. 
 
● 1대 1 통신 (TCP) : 연결이 끊기기 전까지 서로 알고있다. 
 
● 패킷 : 실제 한번에 전동되는 단위
 
● 서브넷 마스크가 씌여진 것중에 자기 것만 받아들여서 읽는다. 이후 프로세스는 포트번호로 구별한다. 
 
● 소켓 : 통신을 위한 메모리 블럭(단위) / 통신을 하기위한 라이브러리 또한 소켓(라이브러리)이라고한다. 핸들로 소켓을 컨트롤한다.
 
● 두 컴퓨터가 통신한 다는 것은 소켓과 소켓이 연결되어 있다고 한다.
// 소켓 생성
SOCKET sock = socket(...);
...
recv(sock, ...) // 받기
send(sock, ...) // 보내기
 
● 대부분의 소켓 함수는 성공하면 0을 반환한다.
 
● TCP/IP 다중 처리 서버 (기본적인 구조)
 
● 소켓 데이터 구조
 
ws2_32.lib
 
#include <winsock2.h>
#include <stdio.h>
 
int main(int argc, char* argv[])
{
    WSADATA wsa;
    
    if(WSAStartup(MAKEWORD(2, 2), &wsa) != 0) //WSAStartup 로드 및 버전 설정
        return -1;
 
    // 원래의 IP 주소 출력
    char *ipaddr = "230.200.12.5";
    printf("IP 주소 = %s\n", ipaddr);
 
    // inet_addr() 함수 연습 //문자열 주소 주고 4바이트 정수 이루어진 변환 (리틀엔디안)
    printf("IP 주소(변환 후) = 0x%x\n", inet_addr(ipaddr));
 
    // inet_ntoa() 함수 연습
    IN_ADDR temp;
    temp.s_addr = inet_addr(ipaddr); // 4바이트정수주고 -> 문자열로 변환
    printf("IP 주소(변환 후) = %s\n", inet_ntoa(temp));
 
    WSACleanup(); // 2개가 꼭! , WSACleanup : dll을 마무리 하는 작업
    return 0;
}
 
WSAStartup : 윈도우즈 소켓 어싱크로나이즈 (비동기)
WSA 가 붙었다고 해서 모두다 비동기함수가 아니다. 그 시기에 만들어져서 그렇게 된 것이다.
 
WSAStartup
MAKEWORD(2, 2) : 인아웃 파라미터, 버전 2.2 (뒤에서부터)
(주버전.부버전.빌드버전.수정버전)
 
&wsa : 아웃 파라미터
 
 
"65.66.67.68" : 리틀 엔디안으로 저장한다.
 

구조체 멤버 정렬
#include <stdio.h>
//#pragma pack(1)
 
typedef struct _msg
{
    char c;
    int n;
    double d;
} MSG;
 
void main()
{
    MSG msg={0};
 
    printf("size: %d\n", sizeof(msg)); }
 16byte 출력. 패딩바이트. char는 1byte이다. 그러나 cpu가 읽어 갈 때는 32bit(또는64bit) 만큼 읽어간다. 즉 4byte를 가져가서 읽게된다. (3byte는 패딩 바이트)
 읽는 주소가 정해져 있어서 패딩바이트가 없으면 여러번 읽는 비효율이 발생할 수 있다.
 
#pragma pack(1) // 별로좋은건아님. 컴파일러 최적화를 하지않는다. (패딩바이트를 삽입하지 않는다.)
 
#pragma 비정형화된 매크로를 지정할 때 사용한다. (표준화 되지않은 것.)
 

 
바이트 정렬
클라이언트가 서버에 접속할 때, 최소 ip 와 프로토콜을 전달하게된다.
 
두 가지 저장방식이 있다. (두개 다 장점이 있다.)
(정수를 보관할 때)
41,42,43,44 : 리틀 엔디안 (끝 값을 작은 쪽 주소에 보관)
44,43,42,41 : 빅 엔디안 (끝 값을 큰쪽 주소에 보관)
 
종단 시스템간에서 A는 리틀, B는 빅을 사용할 때. 서로 해석이 다를 수 있다.
그래서 TCP/IP의 네트워크 표준을 만들어낸다. 표준은 빅엔디안이다.
 
#include <winsock2.h>
#include <stdio.h>
 
int main(int argc, char* argv[])
{
    WSADATA wsa;
    if(WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
        return -1;
 
    u_short x = 0x1234;
    u_long y = 0x12345678;
    
    u_short x2;
    u_long y2;
 
    // 호스트 바이트 -> 네트워크 바이트
    printf("호스트 바이트 -> 네트워크 바이트\n");
    printf("0x%x -> 0x%x\n", x, x2 = htons(x));
    printf("0x%x -> 0x%x\n", y, y2 = htonl(y));
 
    // 네트워크 바이트 -> 호스트 바이트
    printf("네트워크 바이트 -> 호스트 바이트\n");
    printf("0x%x -> 0x%x\n", x2, ntohs(x2));
    printf("0x%x -> 0x%x\n", y2, ntohl(y2));
 
    WSACleanup();
    return 0;
}
 
인텔 계열은 모두 리틀엔디안
 
htons(x) : host to network short
htonl(y) : host to network long
 
ntohs(x2) : network to host short(2byte)
ntohl(y2) : network to host long(4byte)

도메인이름IP변환
#include <winsock2.h>
#include <stdlib.h>
#include <stdio.h>
 
// 소켓 함수 오류 출력
void err_display(char *msg)
{
    LPVOID lpMsgBuf;
    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER|
        FORMAT_MESSAGE_FROM_SYSTEM,
        NULL, WSAGetLastError(),
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR)&lpMsgBuf, 0, NULL);
    printf("[%s] %s", msg, (LPCTSTR)lpMsgBuf);
    LocalFree(lpMsgBuf);
}
 
// 도메인 이름 -> IP 주소
BOOL GetIPAddr(char *name, IN_ADDR *addr)
{
    HOSTENT *ptr = gethostbyname(name);
    if(ptr == NULL){
        err_display("gethostbyname()");
        return FALSE;
    }
    //ptr->h_addr = > ptr->h_addr_list[0]
    memcpy(addr, ptr->h_addr, ptr->h_length);
 
    return TRUE;
}
 
// IP 주소 -> 도메인 이름
BOOL GetDomainName(IN_ADDR addr, char *name)
{
    HOSTENT *ptr = gethostbyaddr((char *)&addr,
        sizeof(addr), AF_INET);
    if(ptr == NULL){
        err_display("gethostbyaddr()");
        return FALSE;
    }
    strcpy(name, ptr->h_name);
    return TRUE;
}
 
int main(int argc, char* argv[])
{
    WSADATA wsa;
    if(WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
        return -1;
 
    // 도메인 이름 -> IP 주소
    IN_ADDR addr;
    if(GetIPAddr("www.yahoo.com", &addr)){
        // 성공이면 결과 출력
        printf("IP 주소 = %s\n", inet_ntoa(addr));
    
        // IP 주소 -> 도메인 이름
        char name[256];
        if(GetDomainName(addr, name)){
            // 성공이면 결과 출력
            printf("도메인 이름 = %s\n", name);
        }
    }
 
    WSACleanup();
    return 0;
}
 
도메인 네임 서버(DNS) : 도메인은 논리적인 영역을 뜻한다. 이름을 관리하는 서버.
 

 
오류시 _WINSOCK_DEPRECATED_NO_WARNINGS 추가
 
    // socket()
    SOCKET listen_sock = socket( //--소켓을생성 (TCP)
        AF_INET, //주소체계: 통신 영역 설정, 인터넷 영역을 사용하며 리모트 컴퓨터 사이의 통신을 사용, IPv4
        SOCK_STREAM, //프로토콜유형(타입): TCP/IP 기반 사용 (-- SOCK_DGRAM :UDP)
        0 /*IPPROTO_TCP*/ //앞 두 인자로 프로토콜 결정이 명확하면 0사용, IPPROTO_TCP, IPPROTO_UDP
    );
 
    // bind()
    SOCKADDR_IN serveraddr;
    ZeroMemory(&serveraddr, sizeof(serveraddr)); // memset() 과 똑같다. 0으로 set한다.
    serveraddr.sin_family = AF_INET; //주소체계 (인터넷 주소체계를 사용 : IP)
    serveraddr.sin_port = htons(9000); //지역포트번호
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); //지역IP 주소 (아이피 상관없이 접근가능)
    retval = bind(
        listen_sock, //-- 해당 소켓에 연결 짓는다.
        (SOCKADDR*)& serveraddr, //-- 구조체의 주소
        sizeof(serveraddr) //-- 구조체의 크기
    ); //--속성부여, 연결
바인딩 : 소켓에 속성을 부여하는 작업
 
    // listen()
    retval = listen(
        listen_sock,
        SOMAXCONN //접속대기 큐의 크기
    ); // TCP 상태를 LISTENING 변경
    if (retval == SOCKET_ERROR) err_quit("listen()");
클라이언트가 접속가능한 대기큐가 생성된다.
 
listen()이 성공한 소켓을 대기소켓(listen 소켓)이라고 한다.
 
대기소켓은 소켓이 들어왔는지 확인만 가능하다.
 
// accept()
addrlen = sizeof(clientaddr);
client_sock = accept(
    listen_sock, //대기 소켓
    (SOCKADDR*)& clientaddr, //클라이언트의 정보 out param
    &addrlen //주소구조체형식의크기, in(크기지정), out(초기화한크기반환) param
); //통신소켓 생성: 원격 IP, 원격 포트 결정
accept() : 접속 대기큐데 들어온 클라이언트의 정보를 꺼내온다. 통신가능한 통신소켓을 (소켓번호)반환한다.
 
inet_ntoa(), ntohs()
 
지금 하는 내용은 순차처리서버 입니다. 일반적 서버구조가 아니다.
 
// 데이터 받기
retval = recv(
    client_sock, //통신소켓핸들
    buf, //받을 애플리케이션 버퍼
    BUFSIZE, //수신 버퍼의 최대 크기
    0 //대부분 0 or MSG_PEEK와 MSG_OOB를 사용 가능
);
오류 발생시 -1
종료시 0
또는 실제 받은 바이트 수 반환
 
// closesocket()
closesocket(client_sock);
 

 
클라이언트
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
 
 
// connect()
SOCKADDR_IN serveraddr;
ZeroMemory(&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(9000); // 대기소켓의 포트번호
serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 예약주소 루프백주소
retval = connect( // 접속요청
    sock, //소켓핸들 (-- connect 성공시 연결된다.)
    (SOCKADDR*)& serveraddr, //접속 서버 주소값 (서버정보)
    sizeof(serveraddr) //주소값 크기
); // 서버에 접속 요청(성공하면 자동으로 지역포트, 지역주소를 할당)
 
// 데이터 보내기
retval = send(
    sock, //통신소켓핸들
    buf, // 보낼 애플리케이션 버퍼
    strlen(buf), //보낼 데이터 크기
    0 //대부분 0 or MSG_DONTROUTE나 MSG_OOB를 사용 가능
); // 실제 보낸 바이트수 반환

예전 강의
오전 9:11 2018-03-27
[TUTO]
[명시적 dll사용]
HMODULE hDll = LoadLibrary(_T("SPDLL2.dll"));
를 사용해서 dll을 불러올 수 있다.
 
시스템 프로그래밍의 맛만봤다. 앞으로 os를 가지고 놀일이 별로없을것.
 
8bit컴 - 아두이노
16bit컴 -
32bit컴 - 라즈베리 갈릴에이 esp8216
64bit컴 - 요즘PC
 
8~32bit 임베디드 프로그래밍.
하드웨어에 직접장착해 쓰는 것을 펌웨어라고함.
앞으로는 대부분 기기에 os가 장착될거.
 
하드웨어: c,c++
그 외:파이썬 C#, JAVA
 
[[네트워크 프로그래밍]]
네트워크는 두개 이상의 연결.
 
네트워크상 연결되는 요소 하나하나를 end-system(통신가능장비)이라 한다.
end-system이 두 대이상 연결됬을 경우 네트워크.
네트워크는 연결자체를 뜻해서 네트워크프로그래밍이랑은 다른뜻이다.
1대1로 연결되있는걸 로컬네트워크라고부른다.
로컬네트워크가 각각 또 연결되어있는데 수없이 연결된 로컬네트워크를 네트워크시스템이라고한다. 이게 전세계적으로 연결되있는건 인터넷.(망)
 
게이트웨이 역할하는게 라우터(분배기). 라우터는 데이터가 어디로 가야할지를 알고있다.
 
로컬안에 많은 라우터(라우터역할)가 있고 라우터서로연결되어있다. 그중 최종 라우터가 마지막으로 밖으로 보내는 라우터. 라우터가 논리적으로 많이 있고.. 컴퓨터는 1개의 라우터를 갖는다고하지만 말이다. 최종라우터를 게이트웨이. 게이트웨이에 동작시키고 있는 연결상태를 동작하는 소프트웨어를.->게이트웨어(소프트웨어)
 
모든 곳에는 IP가있다.
IPv4
4B.4B.4B.4B
A군 B군 C군(클래스 A,B,C)
군에는 네트워크ID , 호스트ID
네트워크ID:최종라우터인 게이트웨이의 번호라고 생각하면됨.
호스트ID: 라우터(게이트웨이)를 찾아가고 각각의 호스트번호로 가서 사용.
    게이트웨이까지 이동을 했다가 실제 컴퓨터로 찾아갈 때.
 
프로토콜에는 내출발 ip와 도착 ip정보가 있다.
라우터가 데이터가 어디로 갈지를 정한다.
어디서 출발했고 어디로 가야하지를 알고있기때문에 라우터들끼리 보내고보내고 할 수있는 거다. 그후 도착ip까지 도착하게된다면... 호스트ip를 까보고 최종 목적지까지 도달하게 되는 것이다.
 
소프트웨어는 모두 하드웨어로 구현이 가능하다.
네트워크에 관련된 소프트웨어는 os가 가지고있는데...
8bit컴엔 os가 없는데 어떻게 네트워크 프로그래밍이 가능하냐.
(예를들어서..)ㄴ
TCP라는 칩에다가 소프트웨어를 하드웨어로 박아놓앗기 때문에 가능하다.
칩만있으면 라이브러리 함수를 불러쓰듯이 칩만있으면 동작이 가능하다.
소프트웨어는 전기적인 신호이기때문에 하드웨어로 똑같이 만들어 낼 수 있다.
 
[IP주소변환]
ws2_32.lib;(스텁코드)
32bit형식의 라이브러리. 2000년 초까지 만들어지고 업데이트가 안됨.
얘를 포함하면 dll이 링크되어 사용할 수 있다.
 
2.x소켓은 비동기를 추가하였다.
 
wsa:윈도우 소켓 어싱크
 
WSAStartup:네트워크 속성.
 
WSAStartup(): 0이면 정상종료
 
사용시 3단계....
1. 라이브러리 추가
2. #include <winsock2.h>
3. WSAStartup(),WSACleanup()
 
윈도우즈는 네트워크 라이브러리가 소켓이라고한다. 뭔가의 접점(소켓)
일반적으로 전화기를 소켓이라고 부른다..
윈도우의 네트워크 라이브러리를 소켓 라이브러리라고한다.
또 뭔가뭔가 통신하기위한수단 이라고 보면된다.
소켓1.통신수단 자체를의미(논리적인 장치)2.라이브러리를 의미한다.
 
네트워크 통신을 할라면 IP address가 필요하다..
현재많이쓰는 v4, 요즘은 v6로 바뀌고 있다.
수업코드에는 v4로 되어있다.
1byte.1b.1b.1b
 
inet_addr() : 주소를 주면 16진수로 변경해준다.(문자열을 4바이트로 변환)
 
inet_ntoa() : 4바이트정보를 문자열로 변경해준다.
a:아스키
 
[구조체멤버정렬]
바이트정렬.
데이트를 쓸때 4바이트씩 익꺼나 4바이트씩 쓴다.
따라서 5바이트를 읽을라면 4바이트읽고또 4바이트 읽어야함...
좀 불필요. 그래서 패딩바이트(안쓰는바이트)를 넣어주어서 4바이트맞춘다.
 
네트워크로 데이터를 주고받을 때는 절대로 구조체 직접 보내지 않는다.
컴파일러가 최적화를 하기 때문에. 뭐 몰라도 쓸일없다.
 
[바이트정렬.]
표준이 2개가 있다.
리틀엔디안.
빅엔디안.
 
네트워크에서는 보통 문자,바이트단위로 만들어서 전달한다.
네트워크표준은 빅엔디안이다.
 
[소켓옵션]
setsockopt()
 
서버가 죽고나서 리소스에서 한동안 포트번호가 사용된다..
그럼 새로연결시 이미 사용중이라뜨....
그걸 다시 사용하고자할 때 옵션을 설정할 수 있다.
리소스가 부족할 수 있을 때 옵션을 사용할 수 있다.
 
결론적으로는 소켓의 옵션도 바꿔서 사용할 수 있다.
 
도메인네임:문자열을 아이피 네임과 메핑시켜놓은 것.
도메인:논리적으로  그룹, 영역,경계 라는 뜻
 
도메인네임서버 : 도메인네임테이블을 가지고있는 서버.
    도메인네임치면 도메인네임서버에서 IP를 가져와서 접속하는거다.
 
[TCP]
유니코드를 멀티바이트로
 
void err_quit(char *msg)
 
NULL, WSAGetLastError()
네트워크에서 사용하는 함수는 다 시스템레벨 함수다.
그래서 대부분 시스템에서 쓰던 함수를 사용한다.
옛날엔 중요했지만 지금ㅇ느 중요ㅏ지않아서 빨리나간다.
 
쓰레드에서 각각의 쓰레드에서는 오류값 정수를 보관할 수 있는
메모리공간을 다 가지고 있다.. 거기다가 마지막 오류정보를 담는다.
성공하면 성공정보를 담는다. A쓰레드에서 add()를 호출한 다음에
GetLastError()를 해주면 add()함수의 오류 정보를 얻을 수 있다.
왜 이렇게했나? 이건 객체지향이 아니니깐...그냥 정수로 보관하던것.
 
객체지향에서는 그냥 오류객체를 뜨로우함
 
err_display(char *msg)
똑같은데 exit만 없음.
오류는 오륜데 덜 치명적인 오류,
 
여러가지 프로토콜이 있지만 가장 널리쓰이는게 TCP프로토콜이다.
99%가 TCP 사용한다.
 
TCP는 기본적으로 병렬(다중) 서버라는 것이있다.
순차처리서버도 있다.
 
대기소켓: 클라이언트가 접속되는지 기다리는역할
통신소켓: 실제 클라이언트와 주고받는 역할
 
Soket(): 소켓 생성
bind(): 소켓의 속성을 설정,IP와 포트번호를 설정한다.
listen(): 대기소켓의 클라이언트가 (지정개수만큼)접속할 수 있는 큐를 생성.
accept():접속대기큐를 보고있다가 접속하면 리턴..
 
대기큐는 직접생성했지만 통신소켓은 직접생성할 수 없다.
accept()가 통신소켓을 반환해준다.(생성해준다.)
 
순차처리는 accept() 이후 통신만함..
다중서버는 이 통신소켓이 아직 완료되지 않았더라도 새로운 통신소켓을 하나 더만들어서 또 다른 클라이언트와 통신을 할 수 있게(2개이상의) 해줌.
 
대기소켓은 하나만 만들어지고 통신소켓은 수십 수천개까지 만들어질 수 있다.
 
보낼때 send()
받을때 recv()
 
서로간 정보를 알고있는게 통신소켓이라고 한다.
커넥트가 완료되면 상대방의 정보와 현재 내정보가 있어서 통신이 가능하다.
이런 소켓을 통신가능한 소켓이라 한다.
 
SOCKADDR_IN
2b:주소체계
2b:포트번호
8b:ip
 
listen() : 방금만들어둔 대기 소켓을 이제 사용한다.
 
/*통신가능한 소켓 반환*/client_sock = accept(listen_sock/*여기에 메세지가 없다면 블락.*/, (SOCKADDR *)&clientaddr/*클라이언트아이디,포트번호*/, &addrlen/*구조체사이즈*/);//대기함수
 
이해만하면안뎀.
네트워크는 네트워크가될 때 클라이언트도 돌아간다.
담에 네트워크 프로그램 하면 간단하게되어있다.
나머지는 추상화 되어있다....
그러면 이제 어디서 어려워 지냐,
바로 이해하는 것과 실제 네트워크를 위해 코드를 짜는 것은 다른 일이다.
네트워크를 돌리고있을 때 수없이 많은 방향중 하나를 타고온건데..
그와중에 이벤트가 엄청 일어날 수 있고 어떠한 처리를 할 수 있는지 생각을 할 수 있어야한다. 사람이 제어불능? ㄴㄴ 그래도 사람이 제어는 할 수 있다.
 
 
Server
 
_WINSOCK_DEPRECATED_NO_WARNINGS;
C/C++전처리기 추가
 
바이트수 멀티바이트 문자집합
C# 2005 키보드
F6 누르면 빌드.
 
★★
서버는 통신을 위해 대기소켓을 준비해 놓아야한다.
 
엑세스 허용하면 대기소켓이 만들어진것.
 
소켓 프로토콜, 그외 정보(리모트정보 등)를 최소한 포함해야한다.
 
클라이언트에서 처음에 소켓만들면 아직 리모트정보는 안채워져있음.
프로토콜,로컬정보는 있는데말이다. 수락하면 리모트정보가 생긴다.
서버의 로컬은 클라의 리모트
클라의 로컬은 서버의 리모트.
 
cmd창-netstat/s
TCP    0.0.0.0:9000           
DESKTOP-LLBMOPM:0      LISTENING (대기소켓)
 
TCP    127.0.0.1:9000         
DESKTOP-LLBMOPM:59906  ESTABLISHED (통신소켓)
 
  TCP    127.0.0.1:59906        
DESKTOP-LLBMOPM:9000   ESTABLISHED (통신소켓)
 
총 3개의 소켓을 가진다. 대기소켓, 서버의통신소켓, 클라의통신소켓
 
포트번호. 프로세스를 구분하는번호라고 생각하면된다. 어떤 프로세스와 소통을 진행할건지 판단.
위에 클라의 포트 59906, 서버의 포트 9000
각각 리모트 로컬이 엇갈려서 저장이 되어있는 것이다.
 
위 정보중 하나만달라도 전혀 다른 소켓이다.
포트번호는 사용하지 않는 포츠번호가 랜덤으로 정해진다.
대기소켓. 서버의 통신소켓의 포트번호는 같다라고 생각하면된다.(windows)
 
클라. 커넥트가 성공해서 반환하면 그때부터 통신소켓이된다.
그때부터 리시브 센드가 가능.
 
어플->os send버퍼에 넣는 것을 센드(os레벨)
저장됬다고하면 send버퍼에 있는걸 제거한다.
 
recv는
recv버퍼.를 어플리케이션 버퍼에 저장.
 
[STUDY]
127.0.0.1 은 로컬 컴퓨터의 IP주소이다. 한대의 컴퓨터 내에서 서버와 클라이언트 프로그램을 모두 실행시키는 경우에는 이러한 전달방식을 택한다.
 
리눅스 : 파일 디스크럽터(파일 핸들)
리눅스에서는 소캣도 파일에포함.
윈도우소켓(윈솤)은 상당부분 BSD 계열 유닉스 소켓을 참고해서 설게되었다.
 
[리눅스와 윈도우 기반으로 동시에 공부해야하는이유]
상당수의 프로젝트에서는 서버를 리눅스 계열의 운영체제 기반으로 설계한다.
그러나 반대로 클라이언트 프로그램의 경우에는 윈도우 기반 개발이 절대적이다.
 
[윈도우 소켓기 반개발 설정]
-헤더 winsock2.h 를 포함
-ws2_32.lib 라이브러리를 링크시킴.
-WSAStartup() : 윈솤버전 알리고, 해당버전을 지원하는 라이브러리의 초기화 작업을 진행해야한다.
 
WSAStartup(MAKEWORD(2, 2), &wsa)
wsa로 초기화된 라이브러리 정보가 채워진다.
socket() : 소켓을 생성 성공시 소켓핸들반환, 실패시 INVALID_SOCKET
bind() : 대기 소켓의 로컬 IP주소, 로컬 PORT번호 할당.  성공시 0 반환, 실패시 SOCKET_ERROR
listen() : 소켓이 클라 프로그램의 연결요청(접속)을 받아들일수 있는 상태가 되게함. 클라의 정보를 저장하는 접속 대기 큐가 만들어진다. 성공시 0 반환, 실패시 SOCKET_ERROR
accept(): 클라 프로그램에서의 연결요청을 수락할 때 호출. 연결 요청 있을 때까지 block.  성공시 새로운 통신소켓핸들
connect(): 클라 프로그램에서 소켓 기반으로 연결요청을 할때 호출. 성공시0
closesocket(): 소켓을 닫을때 사용. 성공시 0.
 
리눅스에서는 소켓을 파일취급하여 파일디스크럽터(핸들)==소켓디스크럽터지만,
윈도우에서는 소켓을 따로 분리하여 소켓핸들이 있고 파일핸들이있어 파일과 분리한다.
 
send() : 애플리케이션 버퍼 -> TCP 프로토콜 송신버퍼. 성공시 전송된 바이트 수 반환 실패시 SOCKET_ERROR
recv() : 성공시 수신한 바이트 수 반환(EOF전송시 0),
         실패시 SOCKET_ERROR반환
 
[프로토콜]
컴퓨터 상호관의 대화에 필요한 통신규약
socket(int domain,int type,int protocol);
첫번쨰인자:프로토콜체계 (IPv4,IPv6 등)
두번째인자:    전송방식. 프로토콜체계가 정해졌다고해서 데이터의 전송방식까지 결정된     것이아니다 즉. PF_INET(IPv4)에도 전송 방식이 두가지 이상 존재    
    한 다는 것이다.
    두번째인자로 SOCK_STREAM(연결지향형소켓) SOCK_DGRAM(비연
    결 지향형소켓)
세번째인자:하나의프로토콜체계안에 데이터전송방식이 동일한 프로토콜이 둘이상 존재
    하는 경우 구체적인 설정을 위해 필요한 옵션
 
1.연결지향형 소켓
(공장라인 1줄 양옆 2명 사탕 전달.. 마트 계산대..)
-중간에 데이터가 소멸되지 않고 목적지로 전송된다.
-전송 순서대로 데이터가 수신된다.
-전송되는 데이터의 경계(Boundary) 가 존재하지 않는다.
여기서 데이터의 경계가 존재하지 않ㄴ음은. "사탕 100개가 여러번에 걸쳐서 보내졌다. 그러나 받는 사람은 사탕 100개가 쌓인 다음에 이를 한번에 봉지에 담아 갔다." → "데이터를 전송하는 컴퓨터가 세 번의 write 함수호출을 통해서 총 100바이트를 전송하였다. 그런데 수신하는 컴퓨터는 한 번의 read함수 호출을 통해서 100바이트 전부를 수신하였다."
 
데이터를 송수신하는 소켓은 내부적으로 버퍼(buffer), 쉽게 말해서 바이트 배열을 지니고 있다. 그리고 소켓을 통해 전송되는 데이터는 일단 이 배열에 저장딘다. 때문에 데이터가 수신되었다고 해서 바로 read함수를 호출해야 하는 것은 아니다. 이배열의 용량을 초과하지 않는 한, 데이터가 채워진 후에 한 번의 read함수호출을 통해서 데이터 전부를 읽어들일 수도 있고, 반대로 한번의 write함수 호출로 전 송된 데이터 전부를 여러번의 read함수를 통해서 읽어들일 수도 있따. 즉 read함수의 호출 횟수와 write함수의 호출 횟수는 연결 지향형 소켓의 경우 큰 의미를 갖지못한다. 때문에 연결지향형 소켓은 데이터의 경계가 존재하지 않는다고 말하는 것이다.
그럼 안 읽어서 버퍼가 다 차면 어떻게되나? 데이터가 소멸하나? 아니다.데이터는 소멸하지 않는다. 왜냐면 데이터를 전송하는 영역의 소켓이 더이상 데이터를 전송하지 않기 떄문이다. 즉 연결지향형 소켓은 자신과 연결된 상대 소켓의 상태를 파악해가면서 데이터를 전송한다. 혹시나 데이터가 제대로 전송되지 않으면, 데이터를 재 전송하기까지 한다. 따라서 연결지향형 소켓의 데이터 손실은 특별한 경우가 아니면 발생하지 않는다.
 
연결지향형 소켓
-소켓 대 소켓의 연결은 반드시 1대1이어야한다.
-신뢰성 있는 순차적인 바이트 기반의 연결지향 데이터 전송 방식의 소켓
 
 
 
[비연결지향형 소켓(SOCK_DGRAM)]
(오토바이 퀴 배달 송수신 방식....)
-전송된 순서에 상관없이 가장 빠른 전송을 지향한다.
-전송된 데이터는 손실의 우려가 있고, 파손의 우려가있다.
-전송되는 데이터의 경계(Boundary)가 존재한다.
-한번에 전송할 수 있는 데이터의 크기가 제한된다.
 
최대한빨리 달림.
손실/파손 우려.
싣을수 있는 무게 제한.(많은양보낼시 두번 이상에 걸쳐)
또 택배특성상 나누어서 보내면 나눈만큼 나와서 다 받아야된다.
또 택배를 한개 시켰는데 3개로 나누어 받을 수 없다.(데이터 경계가 존재)
 
-전송속도는 빠름
-데이터 비손실, 비파손을 보장하지 않음
-데이터 크기 제한 존재
-데이터의 경계존재
"신뢰성과 순차적 데이터 전송을 보장하지 않는, 고속의 데이터 전송을 목적으로 하는 소켓"
 
 
[]
연결지향형 소켓은 데이터의 경계가 없기 때문에 서버에서 send를 한번했어도 클라이언트가 2번이상의 recv를 통해 데이터를 받을 수 있다.
예를 들어 서버에서 "Hello"를 send()했다. 그럼 클라이언트의 while문에서 recv()를 1바이트씩 읽게한다면 총 6번 읽어들일 수 있는것이다.
 
[소켓에 할당되는 IP주소와 PORT번호]
IP는 Internet Protocol의 약자로 인터넷 상에서 데이터를 송수신할 목적으로 컴퓨터에게 부여하는 값을 의미한다.
반면 PORT번호는 컴퓨터에게 부여하는 값이 아닌, 프로그램 상에서 생성되는 소켓을 구분ㅎ기 위해 소켓에 부여되는 번호를 뜻한다.
[인터넷주소 Internet Address]
IPv4 (4바이트 주소)
IPv6 (16바이트 주소체계)
IPv4 기준의 4바이트 IP주소는 네트워크 주소와 호스트(컴퓨터)주소로 나뉘며, 주소의 형태에 따라서 A,B,C,D,E 클래스로 분류가된다.
 
네트워크 주소(ID)란 네트워크의 구분을 위한 IP주소의 일부를 말한다.
 
203.211.217.202로 데이터를 전송한다면,
203.211.217은 SEMI.COM이라면 202는 그 네트워크에 속한 어떤 사람의 PC이다. 전송과정에서 203.211.217로 그 네트워크의 라우터로 데이터를 전송하게되면 그 라우터가 202를 참고해서 알맞은 호스트에 데이터를 전송해준다.
 
[클래스 별 네트워크 주소와 호스트 주소의 경계]
A클래스 첫번째 바이트 범위 0~127... 등
 
[NIC]
네트워크 인터페이스 카드. 데이터 송수신장치.가 컴퓨터 마다 있는데. IP는 데이터를 NIC를 통해 컴퓨터 내부로 전송하는데 사용된다. 그러나 컴퓨터 내부로 전송된 데이터를 소켓이 적절히 분배하는 작업은 운영체제가 담당한다. 이 떄 운영체제는 PORT번호를 활용한다. 즉 NIC를 통해서 수신된ㄷ ㅔ이터 안에는 PORT번호가 새겨져있다.
이렇듯 포트번호는 하나의 운영체제내에서 소켓을 구분하는목적. 중복될 수 없다.
할당가능 포트 0~65535. 0~1023까지는 Well-Known PORT라서 제외하고 우리가 할당.
 
[주소정보표현]
struct sockaddr_in{
    1. 주소체계
    2. 16비트 TCP/UDP PORT번호(네트워크바이트순서로)
    3. 32비트 IP주소(네트워크바이트순서로)
    4. 사용되지 않음.
};
 
[네트워크 바이트순서와 인터넷 주소변환]
CPU에 따라 4바이트 정수 1을 다르게 저장한다.
1.00000000 00000000 00000000 00000001
2.00000001 00000000 00000000 00000000
1번방식 2번방식으로 각각 다를 수 있다. 즉 데이터의 해석방식이 다른것.
네트워크는 빅엔디안.
CPU의 데이터 저장 방식을 '호스트 바이트 순서(host byte order)' 라고한다.
인텔계열은 주로 리틀 엔디안 방식으로 데이터를 저장한다.
두 컴터사이 전송시 차이가 있을 수 있기 때문에 네트워크를 빅엔디안으로 쓰기로 약속했다. 그 후 컴터에서 해석시 맞는 엔디안방식으로 데이터를 읽는다.
 
변환 시 s가붙는 건 short기 떄매 port번호 변환에 사용
변환 시 l이붙는건 long이기 때매 ip주소 변환에 사용.
 
[서버 소켓 생성시 IP번호가 필요한이유]
IP주소는컴터에 장착된 NIC(랜카드)개수 만큼 부여 가능하다. 그리고 이러한 경우에는 서버 소켓이라 할지라도 어느 IP주소로(NIC로) 수신할지 결정해야한다.
떄문에 서버 소켓의 초기화 과정에서 IP주소 정보를 요구하는것. 그러나 NIC가 하나라면 주저없이 INADDR_ANY를 이용하는 것이 편리하다.
 
 
 
 
[TCP와 UDP에 대한 이해]
인터넷 프로토콜 기반 소켓의 경우, 데이터 전송방법에 따라서 TCP소켓과 UDP소켓으로 나뉘고 , 특히 TCP 소켓의 경우 연결을 지향하기 때문에 '스트림 기반 소켓' 이라고도 이야기한다.
TCP는 Transmission Control Protocol의 약자로써 '데이터 전송과정의 컨트롤' 이라는 뜻을 담고 있다. 때문에 TCP소켓의 정확한 이해를 위해서는 컨트롤의 방법과 범위에 대해 살펴보는 것이 도움이된다.
 
IP의 상위계층에서 호스트대 호스트의 데이터 송수신 방식을 약속하는 것이 TCP 그리고 UDP이며, TCP는 확인 절차를 걸쳐서 신뢰성 없는 IP에 신뢰성을 부여한 프로토콜라 할 수 있따.
 
[연결요청 대기상태로의 진입]
listen 함수 호출을 통해서 '연결 요청 대기 상태'로 만든다.
그리고 listen 함수가 호출 되어야 클라이언트가 연결 요청을 할 수 있는 상태가 된다. 즉, listen함수가 호출되어야 클라이언트는 연결요청을 위해서 connect함수를 호출 할 수 있다.
listen함수를 호출하면 문지기역할을 하는 서버소켓이 만들어지고, listen함수의 두번째인자로 전달되는 정수의 크기에 해당하는 대기실이 만들어진다. 이 대기실흥 가리켜 연결요청 대기큐라고 하며, 서버 소켓과 연결요청 대기큐가  완전히 준비되어서 클라이언트의 연결요청을 받아 들일 수 있는 상태를 가리켜 '연결요청대기상태'라고한다.
 
[클라이언트의 연결요청 수락]
listen함수 호출이후 클라의 연결요청이 들어왔다면, 들어온 순서대로 연결 요청을 수락해야한다. 요청이 들어온다면, accept가 통신 소캣을 만들어준다.
accept함수는 '연결요청 대기큐'에서 대기중인 클라이언트의 연결요청을 수락하는 기능의 함수이다. 따라서 성공시 내부적으로 입출력에 사용될 소켓을 생성하고 그 소켓의 핸들을 반환한다.  중요한 점은 소켓이 자동으로 생성되어, 연결요청을 한 클라이언트 소켓에 연결까지 이뤄진다는 점이다.
대기큐가 비어있으면 대기큐가 찰 때까지 반환하지 않는다.(클라이언트의 요청이 들어올때까지 반환하지 않는다.)
 
 
[connect]
클라이언트에 의해 connect함수가 호출되면 다음 둘 중한가지 상황이 되어야 함수가 반환된다.
-서버에 의해 연결요청이 접수되었다.(연결요청정보가 서버의 연결요청대기큐에 등록된 상황을 의미한다.)
-네트워크 단절 등 오류상황이 발생해서 연결요청이 중단되었다.
 
클라이언트 소켓의 주소정보는??
connect함수가 호출할때
운영체제에서(커널에서)
IP는 컴퓨터에 할당된 IP로, PORT는 임의로 선택해서 부여한다.
댓글
댓글쓰기 폼