dev-miri
[computer network] 네트워크 프로그래밍과 소켓 (1) 본문
1. 네트워크 프로그래밍과 소켓의 이해
네트워크 프로그래밍이란?
- 소켓을 기반으로 프로그래밍을 하기 때문에 소켓 프로그래밍이라고도 함
- 네트워크로 연결된 둘 이상의 컴퓨터 사이에서의 데이터 송수신 프로그램의 작성
소켓에 대한 간단한 이해
- 네트워크(인터넷)의 연결 도구
- 운영체제에 의해 제공이 되는 소프트웨어적인 장치
- 소켓은 프로그래머에게 데이터 송수신에 대한 물리적, 소프트웨어적 세세한 내용을 신경 쓰지 않게 한다
소켓은 어떤 과정을 통해 생성되는가?
TCP 소켓은 전화기에 비유될 수 있다!
전화 받는 소켓의 생성(Listening Socket)
1. 전화기 장만하기
소켓은 socekt 함수의 호출을 통해서 생성된다.
소켓의 생성은 전화기의 장만에 비유할 수 있다.
#include <sys/socket.h>
int socket(int domain, int type, int protocol)
//성공 시 파일 디스크립터, 실패 시 -1 반환
2. 전화번호의 부여
전화기에 전화번호가 부여되듯이 소켓에도 주소 정보가 할당된다.
소켓의 주소 정보는 IP와 PORT번호로 구성 된다.
#include <sys/socekt.h>
int bind(int sockfd, struct sockaddr *myaddr, socklen_t addrlen);
//성공 시 0, 실패 시 -1 반환
3. 전화기의 연결
연결 요청이 가능한 상태의 소켓 : 걸려오는 전화를 받을 수 있는 상태
일반 소켓 - 전화 걸 수 있음
listen 함수 호출 후 소켓 - 걸려오는 전화를 받을 수 있게 됨! -> 서버 소켓으로 바꾸어줌
#include <sys/socket.h>
int listen(int sockfd, int backlog)
//성공 시 0, 실패 시 -1 반환
4. 수화기를 드는 상황
연결 요청의 수락 : 연결 요청이 수락되어야 데이터의 송수신이 가능하다
수락 된 이후에 데이터의 송수신은 양방향으로 가능하다
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//성공 시 파일 디스크립터, 실패 시 -1 반환
첫 번째 매개변수인 sockfd는 처음 상대방의 요청을 받은 소켓이다.
accept 함수는 종료 후 새로운 소켓을 리턴한다.
전화 거는 소켓의 구현(Client)
연결을 요청하는 소켓의 구현
- 전화를 거는 상황에 비유할 수 있다
- 리스닝 소켓과 달리 구현 과정이 매우 간단하다
- ‘소켓의 생성’과 ‘연결의 요청’으로 구분된다
#include <sys/socket.h>
int connect(int sockfd, struct sockaddr *serv_addr, socklen_t addrlen);
//성공 시 0, 실패 시 -1 반환
Connect 함수로 상대방 서버에 접속한다.
2번째 매개변수 sockaddr은 상대방의 소켓 주소(웹 서버의 포트번호, ip주소)등을 의미한다.
2. 소켓의 프로토콜과 그에 따른 데이터 전송 특성
프로토콜이란?
- 개념적으로 약속의 의미를 담고 있다
- 컴퓨터 상호간의 데이터 송수신에 필요한 통신규약
- 소켓을 생성할 때 기본적인 프로토콜을 지정해야 한다
#include <sys/socket.h>
int socket(int domain. int type, int protocol);
->성공시 파일 디스크립터, 실패 시 -1 반환
- 매개변수 domain, type, protocol이 모두 프로토콜 정보와 관련이 있다
- domain : 소켓이 사용할 프로토콜 체계(protocol family) 정보 전달
- type : 소켓의 데이터 전송방식에 대한 정보 전달
- protocol : 두 컴퓨터간 통신에 사용되는 프로토콜 정보 전달
프로토콜 체계(Protocol Family)
- 프로토콜도 그 종류에 따라 부류가 나뉘는데, 그 부류를 가리켜 프로토콜 체계라 한다.
- 프로토콜 체계 PF_INET은 IPv4 인터넷 프로토콜 체계를 의미한다.
소켓의 타입
- 데이터 전송 방식을 의미함
- 소켓이 생성될 때 소켓의 타입도 결정되어야 한다
- 프로토콜 체계 PF_INET의 대표적인 소켓 타입 둘
- 연결 지향형 소켓 타입 TCP
- 비 연결 지향형 소켓 타입 UDP
프로토콜의 최종선택
-IPv4 인터넷 프로토콜 체계에서 동작하는 연결지향형 데이터 전송 소켓
int tcp_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
//tcp_socket : fd값
//PF_INET : IPv4
//SOCK_STREAM, IPPROTO_TCP : TCP 의미
-IPv4 인터넷 프로토콜 체계에서 동작하는 비 연결지향형 데이터 전송 소켓
int udp_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)
3. 주소체계와 데이터 정렬
인터넷 주소란? (IP주소)
- 인터넷상에서 컴퓨터를 구분하는 목적으로 사용되는 주소
- 4바이트 주소체계인 IPv4와 16바이트 주소체계인 IPv6가 존재한다
- 대부분 IPv4 사용
- 소켓을 생성할 때 기본적인 프로토콜을 지정해야 한다
- 네트워크 주소와 호스트 주소로 나뉜다. 네트워크 주소를 이용해서 네트워크를 찾고, 호스트 주소를 이용해서 호스트를 구분한다.
소켓의 구분에 활용되는 PORT 번호
- application에 부여되는 번호
- port 번호 - socket 1대 1 매핑
- port 번호는 16비트로 표현. 따라서 그 값은 0이상 65535 이하
- 0~1023은 잘 알려진 PORT(well-known PORT)라 해서 이미 용도가 결정되어있다
- 1024~ : 우리가 사용
4. 주소 정보의 표현
IPv4 기반의 주소표현을 위한 구조체
struct sockaddr_in
{
sa_family_t sin_family; //주소체계(IPv4 or IPv6)
uint16_t sin_port; //PORT번호
struct in_addr sin_addr; //32비트 IP 주소
char sin_zero[8]; //사용되지 않음
};
구조체 sockaddr_in의 멤버에 대한 분석
- 멤버 sin_family : 주소체계 정보 저장
- AF_INTE : IPv4 인터넷 프로토콜에 적용하는 주소체계(대부분 사용)
- 멤버 sin_port
- 16비트 PORT 번호 저장
- 네트워크 바이트 순서로 저장
- 멤버 sin_addr
- 32비트 IP주소정보 저장
- 네트워크 바이트 순서로 저장
- 멤버 sin_addr의 구조체 자료형 in_addr 사실상 32비트 정수 자료형
- 멤버 sin_zero
- 특별한 의미를 지니지 않는 멤버
- 반드시 0으로 채워야 한다
- clinet : server의 ip, port를 구조체에 넣음 → 구조체를 connect 함수에 전달(서버에 연결 요청 함수)
- server : 자기 ip, port를 구조체에 넣음
struct in_addr
{
in_addr_t s_addr;
}; //32비트 IPv4 인터넷 주소
//in_addr_t : 32비트 unsigned 정수
구조체 sockaddr_in의 활용의 예
struct sockaddr_in serv_addr;
.......
if(bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))==-1)
error_handling("bind() error");
- 구조체 변수 sockaddr_in은 bind 함수의 인자로 전달되는데, 매개변수 형이 sockaddr이므로 형 변환을 해야한다.
- bind : socket에 ip와 port번호 할당하는 서버에서 쓰는 함수
- 구조체 sockaddr_in 에 ip와 port 번호를 넣어서 bind 함수에서 사용
struct sockaddr
{
sa_family_t sin_family; //주소체계(address family)
char sa_data[14]; //주소정보
};
- 구조체 sockaddr은 다양한 주소체계의 주소정보를 담을 수 있도록 정의되었다. 그래서 IPv4의 주소정보를 담기가 불편하다.
- 이에 동일한 바이트 열을 구성하는 구조체 sockaddr_in이 정의되었으며, 이를 이용해서 쉽게 IPv4의 주소정보를 담을 수 있다.
5. 네트워크 바이트 순서와 인터넷 주소 변환
CPU에 따라 달라지는 정수의 표현
: cpu에 따라서 상위 바이트를 하위 메모리 주소에 저장하기도 하고, 상위 바이트를 상위 메모리 주소에 저장하기도 한다. 즉, CPU마다 데이터를 표현 및 해석하는 방식이 다르다
바이트 순서(Order)와 네트워크 바이트 순서
-빅 엔디안(Big Endian) : 상위 바이트 값을 작은 번지수에 저장
-리틀 엔디안(Little Endian/intel 계열) : 상위 바이트 값을 큰 번지수에 저장
- 호스트 바이트 순서 : CPU별 데이터 저장방식을 의미함
- 네트워크 바이트 순서
- 통일된 데이터 송수신 기준을 의미
- 빅 엔디안이 기준!
- 정수 전달 시 문제가 발생할 수 있으므로 빅 엔디안으로 통일
unsigned short htons(unsigned short);
unsigned short ntohs(unsigned short);
unsigned long htonl(unsigned long);
unsigned long ntohl(unsigned long);
- short : 16bit - 포트번호
- long : 32bit - IP주소
- h는 호스트 바이트 순서를 위미
- n은 네트워크 바이트 순서를 의미
- s는 자료형 short를 의미
- l은 자료형 long을 의미
6. 인터넷 주소의 초기화와 할당
-문자열 정보를 네트워크 바이트 순서의 정수로 변환(inet_addr)
#include <arpa/inet.h>
in_addr_t inet_addr(const char * string);
->성공 시 빅 엔디안으로 변환된 32비트 정수 값,
실패 시 INADDR_NODE 반환
“211.214.107.99”와 같이 점이찍힌 10진수로 표현된 문자열을 전달하면, 해당 문자열 정보를 참조해서 IP 주소 정보를 32비트 정수형으로 반환!
inet_aton
#include <arpa/inet.h>
int inet_aton(const char * string, struct in_addr * addr);
-> 성공 시 1(true), 실패 시 0(false) 반환
- askii to network
- string IP 주소 → 32bit 정수형 주소
- string : 변환할 IP주소 정보를 담고 있는 문자열의 주소 값 전달
- addr : 변환된 정보를 저장할 in_addr 구조체 변수의 주소값 전달
- 기능상으로 inet_addr 함수와 동일하다. 다만 in_addr형 구조체 변수에 변환의 결과가 저장된다는 점에서 차이를 보인다.
inet_ntoa
#include <arpa/inet.h>
char * inet_ntoa(struct in_addr adr);
-> 성공 시 변환된 문자열의 주소 값, 실패 시 -1 반환
- network to askii
- inet_aton 함수의 반대기능 제공
- 네트워크 바이트 순서로 정렬된 정수형 IP주소 정보를 우리가 눈으로 쉽게 인식할 수 있는 문자열의 형태로 변환
인터넷 주소의 초기화
- 서버에서 주소정보를 설정하는 이유
- IP 211.217.168.13, PORT 9190으로 들어오는 데이터는 내게로 다 보내라!
- 클라이언트에서 주소정보를 설정하는 이유
- IP 211.217.168.13, PORT 9190으로 연결을 해라!