부호없는 32비트 수치, 사람이 보기 쉽도록 8비트 숫자(0~255) 네개로 분할하여 '192.168.1.3'과 같이 표현
16비트 부호 없는 숫자(0~65535)
하나의 호스트가 하나의 기차역이라고 생각한다면, 포트 번호는 역의 선로에 해당
하나의 역에서 여러대의 열차가 오고 갈 수 있듯이 하나의 IP주소에도 여러 회선이 연결될 수 있다.
선로에서 열차가 오는 것을 기다릭 있는 것: 특정 프로세스
접속을 기다리면서 어떤 작업을 해주는 프로세스: 서버 프로세스
서버에 접속해서 어떤 작업을 수행하는 프로세스: 클라이언트 프로세스
서버 프로세스를 그냥 서버라고 부르기도 한다. 서버프로세스가 돌아가는 머신을 서버라고 부르기도 하나 이는 클라이언트도 마찬가지
- 메일 송신할 때 사용하는 SMTP 25번 포트
- 웹 브라우저가 사용하는 HTTP 80번 포트
인터넷에서 사용하는 통신 규약(프로토콜)
현재 가장 널리 사용되고 있는 것이 IPv4
지금부터 보급이 예상되는 것이 IPv6
IPv4의 주소는 32비트
IPv6의 주소는 128비트, 그 외에 보안이나 패킷 배송 기능도 개선
(16비트 수치 8개를 16진수로 표기= 2001:0db8:1234:567:90ab:cdef:0000:0000)
IP주소는 수치에 불과하므로 사람이 다루기 어렵기 때문에 보통 IP주소 대신 호스트명을 사용
인터넷 주소창에 host domain name을 입력했을 때 해당 문자를 IP주소로 변환해주는 시스템
$ vi /etc/hosts
옛날에는 이렇게 관리 그러나 호스트 늘어날 때마다 모든 호스트를 여기에 기록해야하기 때문에 현실적이지 않았음
그래서 탄생한 것이 DNS(Domain Name System)
DNS는 호스트 명을 도메인이라고 하는 영역에 나눠서 관리함으로써 호스트명의 관리를 전세계에 분산시킴
국가 코드 최상위 도메인(Country Code Top-Level Domain, ccTLD)
(ex, .kr, .jp, .CN, .US 등.....)
일반 최상위 도메인((generic top-level domain, gTLD)
(ex, .com, .net, .org 등.....)
이어서 그 밑에 계속해서 배치되는 구조
각각의 도메인을 domain name이라고 한다.
/etc/hosts가 더는 사용되지 않는 것은 아니다. 현재도 회사나 가정 내 네트워크처럼 소수 호스트의 이름을 붙이기 위한 용도로 /etc/hosts/가 사용되고 있어, 필요에 따라 DNS와 별도로 사용되고 있다.
NIS에서도 호스트명과 IP주소를 매핑하는 구조가 적용되어 사용되기도 한다.
==> 호스트명과 IP주소를 교환해주는 존재를 리졸버라고 한다.
일반적으로 컴퓨터의 세계에서 이름으로 그 실체를 얻어내는 것을 리졸브라고 한다.
리눅스에서는 IP주소의 리졸버로 libc가 있어, 해당 설정은 /etc/nsswitch.conf에 기술된다.
#include <sys/socket.h>
#include <sys/types.h>
int socket(int domain, int type, int protocol);
domain: 어떤 영역에서 통신할 것인지에 대한 영역을 지정(AF_UNIX:프로토콜 내부에서, AF_INET:IPv4, AF_INET6:IPv6)
AF_UNIX: 유닉스 도메인 소켓으로 같은 호스트에서 프로세스 사이에 통신
type: 어떤 타입의 프로토콜을 사용할 것인지에 대해 설정(SOCK_STREAM:TCP, SOCK_DGRAM:UDP, SOCK_RAW:사용자 정의)
protocol: 어떤 프로토콜을 사용할 것인지에 대해 설정하는 것(0, IPPROTO_TCP: tcp, TPPROTO_UDP: udp)
소켓을 만들고 이에 대응하는 file descriptor을 반환
IPv4상 TCP라면 socket(PF_INET, SOCK_STREAM, 0);
성공시 fd, 실패시 -1
int connect(int sock, const struct sockaddr *addr, socklen_t addrlen);
소켓 sock에서 스트림을 꺼내서 addr로 지정한 주소의 서버에 스트림을 연결
addr: open()에서의 경로와 유사, 인터넷이라면 IP 주소와 포트 번호를 지정
addrlen: *addr의 크기를 지정
성공시 0, 실패시 -1
주의할 점
호스트 이름이 아니라 IP주소와 번호를 지정해야한다.
호스트명은 반드시 IP주소와 포트번호로 변환해야한다.
int bind(int sock, struct sockaddr *addr, socklen_t addrlen);
접속을 기다리는 주소 addr을 소켓 sock에 할당한다.(접속을 기다리는 포트번호 지정하는 역할)
성공시 0, 실패시 -1/errno
int listen(int sock, int backlog);
소켓 sock이 서버용 소켓, 즉 접속을 기다리는 소켓임을 커널에 알림
backlog: 동시에 받아들일 수 있는 커넥션의 최대 수(일단은 5정도로 지정하면 된다.)
성공시 0, 실패시 -1/errno
int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);
socket에 클라이언트가 접속하는 것을 기다리가 접속이 완료되며 ㄴ연결된 스트림의 file descriptor를 반환
성공시 fd, 실패시 -1/errno
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res);
//node의 주소 후보를 res에 기재, service와 hints로 범위를 좁힐 수 있다
//성공시 0, 실패시 에러 종류를 나타내는 0이상의 값 반환(gai_strerror()로 문자열 변환 가능)
//addrinfo의 메모리 영역은 malloc()으로 할당되므로 명시적으로 해제해야함
void freeaddrinfo(struct addrinfo *res);
const char *gai_strerror(int err);
struct addrinfo{
int ai_flags; //추가적인 옵션을 정의할 때 사용, 여러 flag를 bitwise OR-int 하여 넣는다.
int ai_family; //address family를 나타냄, AF_INET, AF_INET6, AF_UNSPEC
int ai_socktype; //socket type을 나타냄, SOCK_SREAM, SOCK_DGRAM
int ai_protocol; //IPv4와 IPv6에 대한 IPPROTO_xxx와 같은 값을 가짐
socklen_t ai_addrlen; //socket 주소인 ai_addr의 길이를 나타냄
struct sockaddr *ai_addr; //호스트의 canonical name
char *ai_canoname; //socket 주소를 나타내는 구조체 포인터
struct addrinfo *ai_next; //주소정보 구조체 addinfo는 linked list이다. 다음 데이터의 포인터
};
소켓으로 접속하면 서버가 현재 시각을 반환해줌
#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <unistd.h>
static int open_connection(char *host, char *service);
int main(int argc, char *argv[]){
int sock;
FILE *f;
char buf[1024];
//호스트명에는 최초의 실행인자 혹은 localhost를 사용
//localhost: 프로세스가 동작하고 있는 호스트
//두번째 인자는 daytime, 여기에는 포트 번호를 문자열로 전달하는 것도 가능
sock = open_connection((argc > 1 ? argv[1] : "localhost"), "daytime");
f = fdopen(sock, "r");
if(!f){
perror("fdopen(3)");
exit(1);
}
//open_connection()에서 얻은 스트림 fgets()로 한 줄씩 읽어서 출력
fgets(buf, sizeof buf, f);
fclose(f);
fputs(buf, stdout);
exit(0);
}
//TCP 접속을 담당하는 함수
//첫번째 인자 호스트명, 두번째 인자 접속할 서비스명(프로토콜명)
static int open_connection(char *host, char *service){
int sock;
struct addrinfo hints, *res, *ai;
int err;
memset(&hints, 0, sizeof(struct addrinfo));
//hints는 반환하는 IP주소의 후보를 줄이기 위해 지정
hints.ai_family = AF_UNSPEC; //Address Family를 지정하지 않은 것 IPv4나 IPv6 중 어떤것도 사용가능
hints.ai_socktype = SOCK_STREAM; //패킷이 아니라 스트림의 접속을 사용한다는 뜻, 즉 TCP
//socket()과 connect()의 인자로 getaddrinfo()에서 얻은 값을 넘겨주는 것이 전부
if((err = getaddrinfo(host, service, &hints, &res)) != 0){
fprintf(stderr, "getaddrinfo(3): %s\n", gai_strerror(err));
exit(1);
}
//소켓에 접속하는 for문
//for문을 사용하는 이유는 getaddrinfo()의 반환값이 링크드 리스트이기 때문
for(ai = res; ai; ai=ai->ai_next){
sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if(sock < 0){
continue;
}
if(connect(sock, ai->ai_addr, ai->ai_addrlen) < 0){
close(sock);
continue;
}
//success
freeaddrinfo(res);
return sock;
}
fprintf(stderr, "socket(2)/connect(2) failed\n");
freeaddrinfo(res);
exit(1);
}
$ sudo apt-get install xinetd
$ sudo /etc/init.d/xinetd start
TCP(Transmission Control Protocol): 전송을 제어하는 프로토콜(규약)
인터넷상에서 데이터를 메세지의 형태로 보내기 위해 IP와 함께 사용하는 프로토콜
일반적으로 TCP와 IP를 함께 사용하는데, IP가 데이터의 배달을 처리한다면 TCP는 패킷을 추적 및 관리하게 됩니다.
TCP는 연결형 서비스를 지원하는 프로토콜로 인터넷 환경에서 기본으로 사용합니다.
연결형 서비스로 가상 회선 방식을 제공
발신지와 수신지를 연결하여 패킷을 전송하기 위한 논리적 경로를 배정
3-way handshaking 과정을 통해 연결을 설정하고 4-way handshaking을 통해 해제
3-way handshaking 과정: 목적지와 수신지를 확실히 하여 정확한 전송을 보장하기 위해서 세션을 수립하는 과정을 의미
흐름 제어 및 혼잡 제어
높은 신뢰성 보장
UDP보다 속도가 느리다.
전이중(FULL-Duplex), 점대점(Point to Point)방식
TCP는 연속성보다 신뢰성있는 전송이 중요할 때에 사용하는 프로토콜
TCP 서버의 특징
패킷
인터넷 내에서 데이터를 보내기 위한 경로배정(라우팅)을 효율적으로 하기 위해서 데이터를 여러 개의 조각들로 나누어 전송을 하는데 이 때, 이 조각을 패킷이라고 함
- TCP에서는 패킷에 번호를 부여하여 패킷을 분실을 확인하는 처리를 하여 목적지에서 재조립한다.
CPU를 사용해서 속도가 느려지는 것이 아니고
확인후 보내고 하는 과정이 UDP에 비해서 많아서 느린것
TCP는 보낼때 패킷단위로 보내는데 받을 때 buf사이즈만큼 받기 때문에
전송이 완료되었을 때 총 받은 사이즈와 보낸사이즈 확인하는 처리해야함
UDP는 그냥 한번에 보내기 때문에 한번 보낼때 사이즈에 제한이 있음
보낼때와 받을때 크기 확인하는 처리해야함
UDP(User Datagram Protocol): 사용자 데이터그램 프로토콜(규약)
데이터를 데이터그램 방식으로 처리하는 프로토콜
데이터그램이란 독립적인 관계를 지니는 패킷이라는 뜻으로, UDP의 동작방식은 비연결형 프로토콜으로 연결을 위해 할당되는 논리적인 경로가 없다. 그렇기 때문에 각각의 패킷은 다른 경로로 전송되고, 각각의 패킷은 독립적인 관계를 지니게 되는데 이렇게 데이터를 서로 다른 경로로 독립적으로 처리하게된다.
비연결형 서비스로 데이터그램 방식을 제공
정보를 주고 받을 때 정보를 보내거나 받는다는 신호절차를 거치지 않는다
UDP 헤더의 CheckSum 필드를 통해 최소한의 오류만 검출
신뢰성이 낮다
TCP보다 속도가 빠르다
장점
UDP는 비연결형 서비스이기 때문에, 연결을 설정하고 해제하는 과정이 존재하지 않아서 서로 다른 경로로 독립적으로 처리함에도 패킷에 순서를 부여하여 재조립을 하거나 흐름 제어 도는 혼잡 제어와 같은 기능도 처리하지 않기에 TCP보다 속도가 빠르며 네트워크 부하가 적다는 장점이 있다.
단점
신뢰성있는 데이터의 전송을 보장하지 못한다.
UDP는 신뢰성보다는 연속성이 중요한 서비스(실시간 서비스(streaming))에 자주 사용
UDP 서버 특징
흐름제어
데이터를 송신하는 곳과 수신하는 곳의 데이터 처리 속도를 조절하여 수신자의 버퍼 오버플로우를 방지
ex) 송신하는 곳에서 감당이 안되게 데이터를 빠르게 많이 보내면 수신자에서 문제가 발생
혼잡제어
네트워크 내의 패킷 수가 넘치게 증가하지 않도록 방지하는 것
만약 정보의 소통량이 과다하면 패킷을 조금만 전송하여 혼잡 붕괴 현상이 일어나는 것을 막는다
프로토콜 종류 | TCP | UDP |
---|---|---|
연결방식 | 연결형 서비스 | 비연결형 서비스 |
패킷 교환 방식 | 가상 회선 방식 | 데이터그램 방식 |
전송 순서 | 전송 순서 보장 | 전송 순서가 바뀔 수 있음 |
수신 여부 확인 | 수신 여부 확인 o | 수신 여부 확인x |
통신 방식 | 1:1 통신 | 1:1 OR 1:N OR N:N 통신 |
신뢰성 | 높다 | 낮다 |
속도 | 느리다 | 빠르다 |