본 프로젝트의 개요와 프로젝트를 진행하기 위한 개념 확립
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