What is the difference shutdown and close?

소켓에서 더 이상 읽을 데이터가 없거나 쓸 데이터가 없다면 shotdown 호출을 사용합니다. 나중의 write나 read를 위해 소켓을 shutdown하면 이 정보는 연결의 반대 쪽 끝에 전송됩니다. 예를 들어 서버 쪽에서 추가로 쓰기 위해 소켓을 shutdown한다면 block된 read 호출이 0을 반환해 더 이상 읽을 바이트가 없다는 것을 예측할 수 있게 해줍니다.

 

프로세스가 더 이상 소켓 file descriptor가 필요하지 않다면 close를 사용합니다.

 

만약 소켓 file decriptor를 만든 후에 fork를 하게 되면 모든 프로세스는 소켓 리소스를 재사용 하기 전에 close해주어야 합니다. 만약 close가 아닌 shutdown을 하게 되면 추가로 read를 했을 경우 file decriptor가 아닌 소켓을 변경했기 때문에 모든 프로세스가 영향을 받습니다.

 

잘 짜여진 코드는 close를 호출하기 전에 shutdown을 호출합니다.

 

When I re-run my server code it doesn't work! Why?

기본적으로 소켓이 닫힌 다음에 포트는 time-out 상태에 들어갑니다. 이 time-out 동안 소켓은 재사용 될 수 없습니다. 

이러한 동작은 포트에 바인딩 하기 전에 소켓 옵션을 REUSEPORT로 셋팅하여 막을 수 있습니다.

    int optval = 1;
    setsockopt(sock_fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));

    bind(sock_fd, ...);

Can a TCP client bind to a particular port?

클라이언트에서도 특정한 포트로 바인드하여 사용할 수 있씁니다. 사실 나가는 TCP 연결은 자동적으로 클라이언트에서 사용되지 않는 포트에 바인드됩니다. 대부분의 경우 클라이언트에서 포트를 명시적으로 세팅하는 일이 필요하지 않습니다. 왜냐하면 시스템이 적합한 인터페이스에 맞는 사용중이지 않은 포트를 할당해주기 때문입니다(예를 들면 현재 연결이 WiFi 연결이라면 무선 카드). 하지만 특정한 이더넷 카드를 선택할 필요가 있거나 방화벽이 나가는 연결을 특정한 범위의 포트만 허락해주었을 경우 포트를 지정하는 것이 유용할 수 있습니다.

 

I built a simple TCP client or server but my process sometimes just quits! Why?

만약 프로세스가 TCP 연결의 반대편에서 shutdown된 소켓에 write한다면 그 프로세스는 SIGPIPE 시그널을 보냅니다. 이전에 pipe에서 다뤘듯이 SIGPIPE의 기본 동작은 프로세스를 종료하는 것입니다. 이러한 문제가 발생했을 때 해결하는 방법은 SIGPIPE를 무시하거나 스스로의 signal handler를 구현하는 것입니다.

 

void handle_sigpipe(int signal) {
  char mesg[1000];
  sprintf(mesg, "\n****** SIGPIPE  - no one is listening :-( ******\n");
  write(1, mesg, strlen(mesg));
}

그리고 signal을 사용해 signal handler를 등록합니다.(또는 sigaction이나 pthread_sigmask등을 사용합니다.)

signal(SIGPIPE,handle_sigpipe)

 

Who connected to my server?

accept 시스템 콜은 sockaddr 구조체를 통해 원격 클라이언트에 대한 정보를 선택적으로 제공합니다. 각각의 프로토콜들은 다양한 종류의 struct sockaddr을 가지고 있으며 이 구조체는 다양한 크기를 가지고 있습니다. 사용하기 가장 단순한 구조체는 가능한 모든 종류의 sockaddr을 표현하기에 충분한 크기를 가진 sockaddr_storage입니다. C는 어떠한 상속 모델을 가지고 있지 않습니다. 그러므로 명시적으로 기본 유형 구조체를 cast해서 구조체를 사용해야 합니다.

 	struct sockaddr_storage clientaddr;
    socklen_t clientaddrsize = sizeof(clientaddr);
   	int client_id = accept(passive_socket, (struct sockaddr *) &clientaddr, &clientaddrsize);

 

이미 살펴봤듯이 getaddrinfo를 통해 addrinfo 엔트리의 linked list를 만들 수 있습니다. 만약 소켓 테이터를 IP와 포트 주소로 바꾸길 원한다면 어떻게 할까요? 이 때 getnameinfo를 사용해 local이나 remote의 소켓 정보를 도메인 이름이나 숫자 IP로 바꿀 수 있습니다. 이와 비슷하게 포트 번호도 service name을 통해 알 수 있습니다. 아래의 예는 클라이언트 IP 주소와 포트 번호를 요청하는 예입니다.

 

    socklen_t clientaddrsize = sizeof(clientaddr);
    int client_id = accept(sock_id, (struct sockaddr *) &clientaddr, &clientaddrsize);
    char host[256], port[256];
    getnameinfo((struct sockaddr *) &clientaddr,
          clientaddrsize, host, sizeof(host), port, sizeof(port),
          NI_NUMERICHOST | NI_NUMERICSERV);

 

getnameinfo Example: What's my IP address?

현재 기기 IP 주소의 linked list를 얻기 위해서 getifaddrs를 사용하면 IPv4와 IPv6주소의 링크드 리스트를 반환받을 수 있습니다. 각각의 엔트리를 조사할 수 있고 getnameinfo를 통해 호스트의 IP주소를 얻을 수도 있습니다. ifaddrs 구조체는 family를 포함하지만 구조체의 크기는 가지고 있지 않습니다. 그러므로 직접 family에 기반한 구조체의 크기를 결정해주어야 합니다.

(family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)

완성된 코드는 다음과 같습니다.

    int required_family = AF_INET; // Change to AF_INET6 for IPv6
    struct ifaddrs *myaddrs, *ifa;
    getifaddrs(&myaddrs);
    char host[256], port[256];
    for (ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next) {
        int family = ifa->ifa_addr->sa_family;
        if (family == required_family && ifa->ifa_addr) {
            if (0 == getnameinfo(ifa->ifa_addr,
                                (family == AF_INET) ? sizeof(struct sockaddr_in) :
                                sizeof(struct sockaddr_in6),
                                host, sizeof(host), port, sizeof(port)
                                 , NI_NUMERICHOST | NI_NUMERICSERV  ))
                puts(host);
            }
        }

What's my machine's IP address(shell version)

ipconfig를 사용하면 쉘에서 내 기기의 IP 주소를 알 수 있습니다. 하지만 각각의 인터페이스 대한 너무 많은 결과가 나올 수 있으므로 output을 grep하여 적절히 filtering할 필요가 있습니다.

ifconfig | grep inet

Example output:
	inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1 
	inet 127.0.0.1 netmask 0xff000000 
	inet6 ::1 prefixlen 128 
	inet6 fe80::7256:81ff:fe9a:9141%en1 prefixlen 64 scopeid 0x5 
	inet 192.168.1.100 netmask 0xffffff00 broadcast 192.168.1.255
Posted by 몰랑&봉봉
,