getaddinfo를 호출하면 ip와 port번호를 가지고 온다. socket 만들고 bind를 통해서 만들어진 소켓을 가지고 listen한다. listen은 서버가 클라이언트로 부터 오는 정보를 받겠다는 것이다. 여기에서 client가 connection request를 하게 되면 accept를 함수를 통해서 그 connection을 establish한다. 클라이언트와 메세지를 주고 받을 수 있게 된다.
client
getaddrinfo를 통해 ip와 port번호를 가지고와 socket을 만들고 connect으로 connection request를 보낸다. 그럼 서버에서는 connection request를 커널안에 있는 tcp mannager에서 connection을 queueing한다. 거기에서 accept해서 client와 서버의 connection이 build된다.
이 상황에서 rio_readlineb로 버퍼리드로 무언가 오기를 기다린다. 클라이언트는 write한다. 서버쪽으로 메세지가 날아가 서버에서 그 데이터를 읽고 서버에서는 그 데이터를 소켓을 통해서 writen으로 쓰기를 하고 서버는 다시 realineb로 간다.
client는 writen하고 서버에서 데이터가 오기를 기다리다가 화면에 쓰고 다시 writen으로 온다.
close를 하면 결국 서버단에서도 닫게 된다.
만약에 다른 client의 요청이 드어오면 다른 client는 계속 기다려야 한다.
서버 소켓은 open_listenfd를 반환하고, 클라이언트는 open_clientfd를 반환한다.
소켓이라는 함수를 써서 socket desciptor을 만든다. domain, type, protocol을 파라미터로 넣는다.
AF_INET은 ipv4를 나타내는 매크로 값, SOCK_STREAM은 소켓이 결국은 connection은 end point라는 것을 알려준다.
Protocol specific! Best practice is to use getaddrinfo to generate the parameters automatically, so that code is protocol independent.
서버가 bind 함수를 통해서 커널에 알려주는 것이다. server socket은 socket descriptor을 가지고 bind하겠다.
서버쪽 프로세스는 sockfd를 이용해서 데이터를 읽을 수도 있고 connection에 도착하는 모든 byte를 읽을 수도 있다.
sockfd를 가지고 쓰기를 할 수도 있다. connection의 endpoint인 addr를 이용해서 데이터를 전송하게 된다.
Best practice is to use getaddrinfo to supply the
arguments addr and addrlen.
커널이 socket function의 descriptor를 active socket이라고 부르는데 이 active socket이 listen함수를 불러서 이 descriptor은 서버에 의해서 사용될 것이라고 명시를 하는 것이다.
A server calls the listen function to tell the kernel that a descriptor will be used by a server rather than a client:
Converts sockfd from an active socket to a listening
socket that can accept connection requests from clients.
sockfd를 listening socket으로 쓰겠다고 변환을 하는 것이다. 그래서 이 socket으로 오는 모든 connection들은 accept이라는 함수를 통해서 connection build를 하기 위한 과정으로 넘어간다.
backlog는 outstanding한 connection request를 받을 수 있는 최대갯수를 지정할 수 있다. 그래서 connection request를 모두 queueing한다.
서버는 클라이언트의 connection를 기다리다가 accept이라는 함수를 호출하여서 connection이 만들어지는 것이다.
server는 connection을 기다리고 있다가(listenfd로) client의 socket address(addr)와 size of the socket address(addrlen)을 채워준다.
그러면 return값이 connected descriptor이다. 그리고 connected descriptor을 가지고 client는 Unix I/O routine들을 호출하게 된다.
client는 connection request를 보낸다.
서버에 connect를 만들거라고 요청하는 것이다.
Best practice is to use getaddrinfo to supply the arguments addr and addrlen.
clientfd라는 소켓을 만들고 listendfd도 만들자. 서버는 accpet이라는 함수에서 connection request가 오기를 기다리고 있는다. listenfd를 계속 보고 있으면서 기다리고 있다. connection request가 오는 순간 tcp mannager에 있는 request queue에 들어간다.
accept함수를 통해서 뭔가 들어와 있으니 dequeue를 해서 connect한다.
connection request가 온 다음 build를 하게 될때 accept이라는 함수를 통해서 connfd와 clientfd를 connection establish를 수행한다. 그래서 client는 clientfd로 읽고 쓰기를 하고 server는 connfd를 통해서 읽고 쓰기를 한다.
왜 listenfd와 connfd를 구분하는가? listenfd로 그 다음 client가 request를 보낼때 listenfd를 통해서 queueing이 된다. 그 다음오면 또 queue가 되고 계속해서 queueing이 된다. tcp manager in kernel에 queueing이 된다.
accept함수를 호출해야하는데 accept함수는 처음 맺은 connfd와 clientfd가 disconnect되기 전에는 accept함수를 호출하지 않는다.
그 다음 multiple client가 요청하더라도 listenfd는 바로 처리는 못하게 된다.
listending fd는 client connect request를 받기 위해서 만들어진 end point이다. server가 종료되기 전까지는 계속 살아있다.
클라이언트와 서버사이의 connection end point이다. listening은 내가 듣고 있는 거고 내가 accept하는 순간 return value가 connected descriptor이고 그 descriptor가 생성되고 매번 client로 부터 온 connection request가 와서 accept를 할때 서버에서 한 번 만들어진다. connection이 서비스를 하고 있는 동안 살아있다. 그 connection을 끊게 되면 그 fd는 없어진다.
결국에 구분하는 이유는 concurrent server을 만들기 위해서이다. 많은 요청이 왔을때 서버가 일단 받기 위해서이다.
이를 위해 child process를 fork를 띄어서 처리할 계획이다.
connection이 만들어지면 위의 파란 부분처럼 처리가 될것이다.
코드를 보면 open_clientfd라고 해서 이 함수로 들어가면 위와 같이 일단 셋팅을 하자.
그 다음 소켓함수를 이용해서 소켓을 받고 connection request를 보낸다.
open_listenfd에서는 Getaddrinfo를 호출한다.
그 다음 소켓을 만들고 바인드한다.
그 다음부터 listen을 하게 된다.
크라이언트는 open_clientfd로 connect까지 요청을 하고 read를 하고 write한다음 Rio_readlineb에서 blocking되어 서버에서 대답이 올때까지 기다린다.
서버에서는 Open_listenfd가 있고 while를 돌면서 Accept하고 echo로 클라이언트와 정보를 주고 받게 된다.
Echo server는 fd로 온 데이터를 읽고 내가 몇 바이트를 읽었다는 것을 출력하고 읽은 버퍼를 write한 다음 그 다음 요청이 오기를 기다린다.