https://basaeng.tistory.com/57
[네트워크 개념] 버클리 소켓 - 1
개요소켓 통신을 개념을 정리하기 위해 아래 책의 버클리 소켓 파트를 읽은 뒤 이를 바탕으로 소개해보겠습니다.https://product.kyobobook.co.kr/detail/S000001792473?utm_source=google&utm_medium=cpc&utm_campaign=googleS
basaeng.tistory.com
이어서 작성하겠습니다
UDP 소켓
UDP 소켓에서 데이터를 보내기 위해서는 sendto() 함수를 사용한다.
https://learn.microsoft.com/ko-kr/windows/win32/api/winsock/nf-winsock-sendto
sendto 함수(winsock.h) - Win32 apps
sendto 함수(winsock.h)는 데이터를 특정 대상으로 보냅니다.
learn.microsoft.com
TCP의 경우 연결을 한 뒤 보내기 때문에 send 함수를 사용하고 UDP는 도착지를 명시해야하므로 sendto라는 이름의 함수를 사용한다.
int sendto(
[in] SOCKET s,
[in] const char *buf,
[in] int len,
[in] int flags,
[in] const sockaddr *to,
[in] int tolen
);
사용할 소켓 s, 보낼 데이터 buf, 길이 len, 비트 플래그 flag (보통 0을 사용함),
목적지 to, sockaddr의 길이 tolen을 지정한다.
패킷의 길이 len이 일정 이상이면 IP 계층에서 단편화가 일어난다는 것을 기억해야 한다.
데이터를 받을 때는 recvfrom()을 사용한다.
https://learn.microsoft.com/ko-kr/windows/win32/api/winsock/nf-winsock-recvfrom
recvfrom 함수(winsock.h) - Win32 apps
recvfrom 함수는 데이터그램을 수신하고 원본 주소를 저장합니다.
learn.microsoft.com
int recvfrom(
[in] SOCKET s,
[out] char *buf,
[in] int len,
[in] int flags,
[out] sockaddr *from,
[in, out, optional] int *fromlen
);
역시 연결이 없으므로 데이터가 어디서 도착할지 모른다.
따라서 from은 수신을 원하는 주소가 당연히 아니고 해당 소켓으로 데이터를 받았을 때 상대방이 누구인지를 식별하기 위한 정보를 저장하기 위한 구조체의 포인터이다.
TCP 소켓
TCP 소켓은 연결지향이기 때문에 서버와 클라이언트 간 연결이 선행되어야 한다.
TCP는 연결을 하기 위해 3-way handshake을 한다.
서버가 listen하고 클라이언트가 connect 하는 것이므로,
클라이언트가 connect를 호출하면 3-way hand shake가 시작된다.
listen()
https://learn.microsoft.com/ko-kr/windows/win32/api/winsock2/nf-winsock2-listen
listen 함수(winsock2.h) - Win32 apps
수신 대기 함수는 들어오는 연결을 수신 대기하는 상태에 소켓을 배치합니다.
learn.microsoft.com
int WSAAPI listen(
[in] SOCKET s,
[in] int backlog
);
서버는 앞에서 bind한 소켓(listen용도)을 인자로 사용한다. 그리고 backlog의 크기를 지정한다.
backlog는 클라이언트가 connect를 호출하고 SYN을 받은 순간에 들어간다.
만약 가득찼다면 그 이상의 연결은 받을 수 없다.
accept()
https://learn.microsoft.com/ko-kr/windows/win32/api/winsock2/nf-winsock2-accept
accept 함수(winsock2.h) - Win32 apps
accept 함수는 소켓에서 들어오는 연결 시도를 허용합니다.
learn.microsoft.com
SOCKET WSAAPI accept(
[in] SOCKET s,
[out] sockaddr *addr,
[in, out] int *addrlen
);
accept는 listen에서 채운 backlog큐를 인출하는 명령이다.
인자 s는 listen하고 있는 소켓으로 해당 소켓에 대한 backlog큐를 인출한다.
그리고 addr은 새로운 연결이 들어오는 해당 클라이언트의 주소를 채워준다.
return하는 소켓은 연결된 클라이언트와 통신하는 독립적인 소켓이 만들어져 반환된 것이다.
listen소켓은 listen용도로만 사용되며 앞으로는 return된 소켓으로 통신하면 된다.
connect()
https://learn.microsoft.com/ko-kr/windows/win32/api/winsock2/nf-winsock2-connect
connect 함수(winsock2.h) - Win32 apps
connect 함수는 지정된 소켓에 대한 연결을 설정합니다.
learn.microsoft.com
int WSAAPI connect(
[in] SOCKET s,
[in] const sockaddr *name,
[in] int namelen
);
connect함수를 통해 클라이언트가 서버에 연결을 요청한다.
s는 클라이언트의 소켓이며 bind하지 않았었다면 사용할 수 있는 port를 할당받는다.
name, namelen은 연결하고자 하는 서버의 주소, 길이이다.
send()
https://learn.microsoft.com/ko-kr/windows/win32/api/winsock2/nf-winsock2-send
send 함수(winsock2.h) - Win32 apps
연결된 소켓에 데이터를 보냅니다. (보내기)
learn.microsoft.com
TCP에서는 send, recv이전에 이미 서버, 클라 간 연결이 되어있기 때문에 목적지를 지정할 필요는 없다.
int WSAAPI send(
[in] SOCKET s,
[in] const char *buf,
[in] int len,
[in] int flags
);
보낼 때 사용할을 소켓 s를 지정한다.
보낼 데이터는 buf에 담아서 보낸다.
len은 전송할 데이터의 바이트 수이다.
TCP의 경우 send는 실제로 보낸다는 느낌 보다는 TCP의 전송 버퍼에 값을 밀어넣는다는 느낌이다.
실제로는 데이터가 작다면 Nagle Algorithm으로 모아서 보낼 수 있으며, 데이터가 크다면 TCP Segmentation을 통해서 MSS크기로 잘라보낸다.
recv()
https://learn.microsoft.com/ko-kr/windows/win32/api/winsock2/nf-winsock2-recv
recv 함수(winsock2.h) - Win32 apps
recv 함수(winsock2.h)는 연결된 소켓 또는 바인딩된 연결 없는 소켓에서 데이터를 받습니다.
learn.microsoft.com
int WSAAPI recv(
[in] SOCKET s,
[out] char *buf,
[in] int len,
[in] int flags
);
recv에 사용할 소켓, 버퍼, 길이를 지정한다.
이또한 send처럼 수신 버퍼에서 값을 가져오는 것이다.
따라서 버퍼의 길이를 지정한다고 해당 길이만큼 가져오는 것을 보장하지 않는다.
블로킹 IO와 논블로킹 IO
기본적인 소켓의 함수들은 블로킹 방식이다. 해당 함수가 동작을 마치기 전까지는 return하지 않는다.
메인 스레드에서 이러한 방식을 채택한다면 프레임 레이트를 유지할 수 없다.
이러한 문제를 해결하기 위한 3가지 방법이 있다.
멀티스레딩
첫번째 방법은 멀티스레딩이다. 블로킹 가능성이 있는 함수를 다른 스레드로 분리시킨다면, 해당 스레드가 블록되더라도 메인 스레드는 정상적으로 동작하기 때문에, 문제가 없다.
하지만 여기서 문제는 recv, send를 다른 스레드에 영향이 없게 하려면 accept를 할 때마다 새로운 쓰레드를 만들어 사용한다는 얘기가 된다.
클라이언트가 많아지면 context switching해야할 스레드가 너무 많아지게되고, 기본적으로 스레드마다 할당되어야 하는 스택의 크기 때문에 메모리도 모자를 것이다.
추가적으로 만약 메인스레드가 데이터를 송수신 중인 소켓에 데이터를 보낸다면 블로킹 될 가능성을 그대로 가지게 된다.
논블로킹 I/O
두번째 방법은 논블로킹 함수를 이용하는 것이다.
소켓 자체를 논블로킹모드로 바꾸고(ioctl socket) 이를 사용한다.
https://learn.microsoft.com/ko-kr/windows/win32/api/winsock/nf-winsock-ioctlsocket
ioctlsocket 함수(winsock.h) - Win32 apps
ioctlsocket 함수(winsock.h)는 소켓의 I/O 모드를 제어합니다.
learn.microsoft.com
int ioctlsocket(
[in] SOCKET s,
[in] long cmd,
[in, out] u_long *argp
);
s는 모드를 변경할 소켓
cmd는 소켓 파라미터 (FIONBIO)
argp는 파라미터 설정 값(argp !=0 논블로킹, argp == 0 블로킹)
소켓의 모드를 바꿨다면 이제는 함수는 블로킹이 되는 상황이면 블로킹 하지않고 return한다.
이때 return되는 값은 -1(SOCKET_ERROR)이며 WSAGetLastError()로 확인하면 WSAEWOULDBLOCK(10035)가 된다.
select()
논블로킹 소켓도 너무 많아지면 순회하면서 모두 확인하는 것은 효율이 떨어질 수 있다.
select()를 활용하면 여러 소켓을 한꺼번에 확인하고 그중 하나라도 준비되면 대응할 수 있다.
https://learn.microsoft.com/ko-kr/windows/win32/api/winsock2/nf-winsock2-select
select 함수(winsock2.h) - Win32 apps
select 함수는 필요한 경우 동기 I/O를 수행하기 위해 대기하는 하나 이상의 소켓의 상태 결정합니다.
learn.microsoft.com
int WSAAPI select(
[in] int nfds,
[in, out] fd_set *readfds,
[in, out] fd_set *writefds,
[in, out] fd_set *exceptfds,
[in] const timeval *timeout
);
select는 읽기, 쓰기, 오류 가능성이 있는 소켓에 대한 집합인 fd_set을 받고
가능한 것이 있다면 (혹은 timeout이 지나면) return한다.
return value는 가능한 소켓 핸들 수 이며, 오류인 경우는 SOCKET_ERROR를 반환한다.
readfds, writefds, exceptfds에는 각각 읽기, 쓰기, 오류 가능성이 있는 fd_set의 주소를 넘긴다.
typedef struct fd_set {
u_int fd_count;
SOCKET fd_array[FD_SETSIZE];
} fd_set, FD_SET, *PFD_SET, *LPFD_SET;
이는 fd_set 구조체로
FD_ZERO로 초기화
FD_SET으로 집합에 소켓 추가
FD_ISSET으로 select return 후에 Set에 특정 소켓이 포함되었는지 확인할 수 있다.
소켓 부가 옵션
setsockopt()를 통해 소켓에 옵션을 지정할 수 있다.
https://learn.microsoft.com/ko-kr/windows/win32/api/winsock2/nf-winsock2-setsockopt
setsockopt 함수(winsock2.h) - Win32 apps
setsockopt 함수(winsock2.h)는 모든 형식의 소켓과 연결된 소켓 옵션의 현재 값을 모든 상태로 설정합니다.
learn.microsoft.com
int WSAAPI setsockopt(
[in] SOCKET s,
[in] int level,
[in] int optname,
[in] const char *optval,
[in] int optlen
);
level, optname쌍을 통해서 옵션의 종류를 지정할 수 있다.
자세한 옵션들은 링크를 통해 확인할 수 있다.
'CS > 네트워크' 카테고리의 다른 글
| [네트워크 개념 복습] 소켓 알아보기 (0) | 2025.09.30 |
|---|---|
| [네트워크 개념 복습] OSI 7계층 (0) | 2025.09.24 |
| TCP흐름 Wireshark로 확인해보기 (4) | 2025.07.24 |
| [네트워크 개념] 버클리 소켓 - 1 (0) | 2025.07.15 |
| 네트워크 원리 - 웹 브라우저 (2) | 2025.07.11 |