-
[SW 정글 47일차] 소켓 (Socket) 입문기타/SW 사관학교 정글 2021. 9. 18. 03:05
오늘은 소켓에 대한 공부를 하면서 소켓 인터페이스를 접하게 되었고 클라이언트와 서버가 어떻게 소켓을 가지고 통신을 하는지에 대한 전반적인 그림을 이해할 수 있었다.
소켓 통신을 이해하면서 들었던 의문점, 개념들을 정리하려고 한다.
1. "A socket is an end point of a connection."?!
컴퓨터 시스템 11.3.3 Internet Connections 파트를 읽으면서 아래와 같은 문장을 보게 됐다.
A socket is an end point of a connection.
이 문장을 본 순간, 왜 소켓을 커넥션의 끝점이라고 하는거지? 끝점은 당연히 클라이언트와 서버 아닌가? 라는 의문을 가지게 되었고 구글링을 했다.
(현실 구글링: why socket end point)
다행히도 stackoverflow에서 내가 원하는 답을 얻을 수 있는 질문을 누군가 했다.
여러가지 대답들이 있었고 그들의 공통적인 대답은 end point는 무엇이든지 될 수 있고 TCP/IP 통신에서는 end point를 소켓으로 말할 수 있다는 것이다.
자연스럽게 TCP/IP 환경에서는 endpoint라는 큰 개념을 소켓으로 말한다고 생각했다.
2. struct sockaddr_in과 struct sockaddr
소켓 인터페이스를 구현하는데 선언되는 것중 하나가 소켓 주소 구조체이다.
소켓 통신을 위해 클라이언트와 서버에 각각 소켓을 가지고 있어야할테고 이를 소프트웨어, 코드로 표현하기 위해서는 구조체가 필요하기에 소켓 주소 구조체를 선언해줘야한다.
그런데, 소켓 주소 구조체 하나가 아닌 2개인 것이다..
왜 2개를 써야할까?
우린는 네트워크환경에서 통신을 위해서 Internet Protocol을 쓴다.
이에 따라 IP socket address structure를 만들어줘야하는데 네트워크에는 IP 이외에 다른 여러 프로토콜이 있으므로 protocol-specific socket address structure 포인터를 필요로 한다.
그렇기에 IP socket address structure만 있으면 마주치는 '문제가 어떻게 소켓 인터페이스에 포함된 함수들을 정의해서 어떠한 종류의 소켓 주소 구조체라도 받아들일 수 있도록 하는가'였다.
현재는 void * 포인터를 사용하여 문제들 해결할 수 있지만 void *포인터가 없는 시절이었기에 새로운 구조체를 만들어 applications이 protocol-specific structures를 가리키는 포인터를 새로운 구조체로 casting되도록 하는 것이다.
3. 왜 client는 bind를 안할까?
client 측을 보면 connect()가 존재하고 server측을 보면 bind()가 존재한다.
listen()과 accept()가 server측에만 존재하는 것은 이해가 되는데 bind()가 왜 client측에만 있는지는 이해가 잘 안가서 찾아보게 되었다.
먼저, connect()와 bind() system call의 차이점을 파악해보았다.
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
bind()는 자신의 local address를 소켓에 연관시켜주도록 하는 함수이다.
bind()가 server측에만 있는 이유는 이렇게 bind()를 해줌으로써 클라이언트가 서버에 연결할 때 해당 주소를 사용할 수 있기 때문이다.
더보기bind() associates the socket with its local address [that's why server side binds, so that clients can use that address to connect to server.]
connect()는 원격(server) 주소로 연결을 시켜주기 위해 사용되는 함수이다.connect()함수가 client측에 있기에 서버측과 연결을 시도할 수 있는 것이다.
더보기connect() is used to connect to a remote [server] address, that's why is client side, connect [read as: connect to server] is used.
이 정도는 그냥 감으로 알 수 있지만 왜 그런지 더 정확히 알 수 있도록 아래의 TCP/IP 연결 시에 진행되는 handshake 과정을 보면 좋을 것같다.
여기서 알아야 할 점은 bind()와 connect()는 상호호환적으로(interchangeably) 사용하는 것 즉, sever가 connect()를 하고 client()가 bind()를 하는 것은 안된다는 것이다.
(클라이언트와 서버가 같은 machine에 있어도 안된다.)
그 이유는 클라이언트와 서버는 특정한 룰들을 가지고 있는 객체이고 통신체계에서 구현된 것이기 때문이다.
하지만 아직은 왜 소켓은 클라이언트와 서버 둘 다 생성하고 bind()는 서버 측에서만 이루어지는지에 대한 궁금증은 풀리지 않았다.
직관적으로 봤을 때, client도 소켓에 자신의 IP주소와 port number를 bind시켜줘야 서버에서 그것을 알고 이 후 응답이 가능하지 않을까?
일단은 client에서 socket()로 socket을 생성하면 빈 통의 socket file descriptor가 만들어진다.
그 후에, connect()를 실행하면 앞에 bind()가 실행되지 않았으면 암묵적으로 bind("0.0.0.0", 0)이 실행된다.
여기서 zero(0)은 어떠한 것(IP 주소, 포트 번호)도 들어갈 수 있다는 의미이다.
client 측의 local address와 port number는 보통의 경우에 중요하지 않으므로 bind()가 필요없지만 서버가 클라이언트의 특정 포트 번호로 제한하고 있거나 포트 번호가 범위를 넘어선다면 client 측에서도 bind()를 해줄 필요는 있다.
추가적으로 알아두면 좋은 것은 커널이 포트번호를 할당해주기도 하고 IP address도 커널이 해준다.
물론, 우리가 직접 넣어주는 것도 가능하지만 포트번호의 경우에 우리가 어떠한 포트번호가 free한지 모르기 때문에 커널이 할당해주는 것을 사용하는 것이 좋은 선택이다.
위에서 말한 중요하지 않다는 것은 서버가 클라이언트의 주소와 포트번호를 몰라도 된다는 것을 의미하는 것은 아닌데 더 자세히 알고싶다면 TCP에 대해 공부해야한다.
https://en.wikipedia.org/wiki/Transmission_Control_Protocol
TCP segment structure와 Protocol operation 부분을 읽으면 이해가 될 것이다.
[오늘의 나는 어땠을까?]
오늘은 소켓을 공부하면서 처음에는 이해가 정말 안됐지만 계속 보면서 팀원들과 얘기를 하니까 어느정도 그림은 그려졌다.
책에서 나온 코드자체가 너무 간략하고 파고들고 싶지만 파고 들려면 C언어를 더 알아야하고 C언어를 알면 다시 네트워크를 알아야하고...
굉장히 답답했지만 차근차근 팀원들과 논의를 하고 하나씩 퍼즐이 맞춰지면서 답답함은 사라지고 신기함과 재미가 오기 시작했다.
혼자가 아닌 여러 명의 합, 내가 좋아하는 것이기에 계속해서 공부를 할 수 있는 것이라고 생각한다.
<참고 자료>
https://stackoverflow.com/questions/27014955/socket-connect-vs-bind
'기타 > SW 사관학교 정글' 카테고리의 다른 글
[SW 정글 추석 특집] Missing Semester 1일차 (0) 2021.09.19 [SW 정글 48일차] 소켓 인터페이스 구현부터 Echo 클라이언트와 서버까지 (0) 2021.09.19 [SW 정글 46일차] 네트워크 용어 익숙해지기 (0) 2021.09.17 SW 정글 6주차 회고 (0) 2021.09.16 [SW 정글 45일차] segregated list 구현과 demand-zero memory (0) 2021.09.16