본 프로젝트의 개요와 프로젝트를 진행하기 위한 개념 확립
1. htons
, htonl
, ntohs
, htohl
메모리에 값을 저장하는 방식에는 빅 엔디안(큰 단위부터 적는 방식)과 리틀 엔디안(작은 단위부터 적는 방식)이 있다. 보통 네트워크 통신에서는 헤더파일 확인이 가장 쉬운 빅 엔디안 방식을 사용하고 일반 PC에서는 연산이 빠른 리틀 엔디안 방식을 사용한다.
각각의 장단점으로 인해 방식이 둘로 나뉘게 되는데 이로인해 네트워크 통신을 할 때 문제가 발생한다. 전송하는 측은 빅 엔디안으로 전송을 하는데 받는 컴퓨터 입장에서는 리틀 엔디안으로 처리를 하기 때문에 실제로 데이터를 수신하면 원래 데이터와 달라져 버리는 것이다. 이 문제를 해결하기 위해 받는 측(리틀 엔디안)에서는 전송받은 데이터(빅 엔디안)를 뒤집어서 처리를 해주어야 한다.
리틀 엔디안을 빅 엔디안으로 변경하기 위해서는 htons
(host to network short) 또는 htonl
(host to network long)을 사용하면 된다. 포트는 2byte(16bit)로 이루어져있고 IP는 4byte(32bit)로 이루어져 있기 때문에, 포트번호를 처리하는 경우는 htons
, IP주소를 처리하는 경우는 htonl
을 사용한다.
반대로 빅 엔디안에서 리틀 엔디안으로의 변형은 ntohs
(network to host short) 또는 ntohl
(network to host long)을 사용한다.
#include <arpa/inet.h>
uint16_t htons(uint16_t hostshort)
uint32_t htonl(uint32_t hostlong)
uint16_t ntohs(uint16_t netshort)
uint32_t ntohl(uint32_t netlong)
2. inet_addr
인자로 들어간 string 타입의 데이터를 빅 엔디안 형식의 정수값으로 반환해 IP주소를 저장하는 sockaddr_in
구조체의 sin_addr
에 저장
#include <arpa/inet.h>
in_addr_t inet_addr(const char* string)
string
인자를 빅 엔디안 형식(32bit 정수값)으로 반환INADDR_NONE
반환3. socket
소켓 생성
#include <sys/socket.h>
#include <sys/types.h>
int socket(int domain, int type, int protocol)
int domain
: 어떤 네트워크(protocol family / address family)에서 사용할 것인지 지정int type
: 어떤 타입의 소켓을 생성할 것인지 지정int protocol
: 소켓에서 사용할 프로토콜 지정domain | 내용 |
---|---|
PF_INET, AF_INET | IPv4 프로토콜 |
PF_INET6, AF_INET6 | IPv6 프로토콜 |
PF_LOCAL, AF_UNIX | 같은 시스템 내에서 프로세스끼리 통신 |
PF_PACKET | Low level socket을 위한 인터페이스 |
PF_IPX | IPX 노벨 프로토콜을 사용 |
type | 내용 |
---|---|
SOCK_STREAM | TCP/IP 프로토콜 |
SOCK_DGRAM | UDP/IP 프로토콜 |
SOCK_RAW | RAW 방식(TCP나 UDP 사용하지 않고 바로 IP계층 사용) |
protocol | 내용 |
---|---|
IPPROTO_TCP | TCP/IP 프로토콜 |
IPPROTO_UDP | UDP/IP 프로토콜 |
0 (IPPROTO_HOPOPTS) | 첫 번째, 두 번째 매개변수를 기준으로 자동지정 |
4. setsockopt
소켓의 옵션 설정
#include <sys/socket.h>
#include <sys/types.h>
int setsockopt(int socket, int level, int option_name, const void *option_value, socklen_t option_len)
int socket
: 옵션을 변경할 소켓의 디스크립터int level
: 변경할 옵션의 프로토콜 레벨int option_name
: 변경할 옵션의 이름const void *option_value
: 변경 결과를 저장할 버퍼의 주소 값socklen_t option_len
: option_value
로 전달된 옵션정보의 바이트 단위 크기5. getsockname
소켓의 옵션 확인
#include <sys/socket.h>
#include <sys/types.h>
int getsockopt(int socket, int level, int option_name, void *restrict option_value, socklen_t *restrict option_len)
int socket
: 옵션을 확인할 소켓의 디스크립터int level
: 확인할 옵션의 프로토콜 레벨int option_name
: 확인할 옵션의 이름void *restrict option_value
: 확인 결과를 저장할 버퍼의 주소 값socklen_t *restrict option_len
: option_value
로 전달된 주소 값의 버퍼 크기를 담고있는 변수의 주소 값프로토콜 레벨 | 옵션명 | get | set | 내용 |
---|---|---|---|---|
SOL_SOCKET (일반) | SO_SNDBUF | OK | OK | 소켓 송신 버퍼의 크기 |
SOL_SOCKET (일반) | SO_RCVBUF | OK | OK | 소켓 입력 버퍼의 크기 |
SOL_SOCKET (일반) | SO_REUSEADDR | OK | OK | 이미 사용 중인 주소나 포트에 대해서도 바인드 허용 |
SOL_SOCKET (일반) | SO_KEEPALIVE | OK | OK | keepalive 메세지 활성화 |
SOL_SOCKET (일반) | SO_TYPE | OK | X | 소켓의 타입 (ex: SOCK_STREAM) |
6. bind
소켓에 주소(서버의 정보)를 할당(묶어줌)
#include <sys/socket.h>
#include <sys/types.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
int sockfd
: 생성한 소켓 디스크립터const struct sockaddr *addr
: 소켓의 구조체 포인터(자신의 주소와 포트번호 들어있음)socklen_t addrlen
: 구조체의 길이7. listen
서버가 bind
한 뒤 어떤 클라이언트로부터 요청이 와도 수락할 수 있게 대기상태에 들어감
#include <sys/socket.h>
#include <sys/types.h>
int listen(int socket, int backlog)
int socket
: 소켓 디스크립터int backlog
: 연결 대기열의 크기 지정8. accept
서버 소켓이 클라이언트의 요청을 받아들여 연결
#include <sys/socket.h>
#include <sys/types.h>
int accept(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len)
int socket
: 소켓 디스크립터struct sockaddr *restrict address
: 클라이언트 주소 정보를 담고 있는 구조체socklen_t *restrict address_len
: 구조체의 길이9. send
소켓에서 메세지 보내기
#include <sys/socket.h>
#include <sys/types.h>
ssize_t send(int socket, const void *buffer, size_t length, int flags)
int socket
: 데이터를 보낼 대상의 소켓 디스크립터const void *buffer
: 보낼 데이터size_t length
: 데이터의 길이int flags
: 함수 호출 시 옵션을 사용하기 위한 플래그 (보통 0)10. recv
소켓에서 메세지 받기
#include <sys/socket.h>
#include <sys/types.h>
ssize_t recv(int socket, void *buffer, size_t length, int flags)
int socket
: 데이터를 받을 대상의 소켓 디스크립터void *buffer
: 받을 데이터size_t length
: 데이터의 길이int flags
: 함수 호출 시 옵션을 사용하기 위한 플래그 (보통 0)11. connect
서버에 연결 요청
#include <sys/socket.h>
#include <sys/types.h>
int connect(int socket, const struct sockaddr *address, socklen_t address_len)
int socket
: 연결하고자 하는 소켓 디스크립터const struct sockaddr *address
: 접속하고자 하는 IP와 포트 정보가 들어있는 구조체socklen_t address_len
: 구조체의 길이12. fcntl
파일속성 변경
#include <fcntl.h>
int fcntl(int fildes, int cmd, ...)
int fildes
: 속성을 조회하거나 변경할 파일 디스크립터int cmd
: 제어 동작 명령cmd | 내용 |
---|---|
F_GETFL | fd에 대한 파일 상태 속성들을 반환값으로 돌려줌 |
F_SETFL | 파일 상태 속성들을 세번째 인자로 받아 설정 |
F_GETOWN | 현재 SIGIO, SIGURG 신호를 받도록 설정된 프로세스 ID 혹은 프로세스 그룹 ID를 돌려줌 |
F_SETOWN | SIGIO와 SIGURG신호를 받도록 시정된 프로세스 ID나 프로세스 그룹 ID를 설정 |
F_DUPFD | 파일 서술자 fd를 복제, 새 파일 서술자 반환 |
F_DUPFD_CLOEXEC | 파일 서술자를 복제하고 새 파일 서술자에 관련된 FD_CLOEXEC를 설정, 새 파일 서술자 반환 |
F_GETFD | fd에 대한 파일 서술자 플래그들을 반환 |
F_SETFD | 세번째 인자로 서술자 플래그들을 설정 |
13. kqueue
새로운 kernel event queue를 생성
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
int kqueue(void)
14. kevent
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
int kevent(int kq, const struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout)
int kq
: kqueue의 디스크립터const struct kevent *changelist
: 추가하거나 바꿀 이벤트의 리스트int nchanges
: 이벤트의 개수struct kevent *eventlist
: 이벤트를 받으면 커널에서 이벤트를 넣어주는 배열int nevents
: eventlist의 kevent의 배열의 수const struct timespec *timeout
: 기다리는 시간 (NULL -> 무한대기)Reference
- https://m.blog.naver.com/wndrlf2003/70190031633
- https://m.blog.naver.com/errorsoft666/222034020641
- https://jhnyang.tistory.com/262
- https://tjdahr25.tistory.com/43
- https://reakwon.tistory.com/110
- https://www.gpgstudy.com/gpgiki/KqueueProgramming
- https://badayak.com/entry/C%EC%96%B8%EC%96%B4-%EC%86%8C%EC%BC%93-%EC%83%9D%EC%84%B1-%ED%95%A8%EC%88%98-socket