sonumb

클라이언트 연결 정보 알아내기 본문

개발자 이야기/Network

클라이언트 연결 정보 알아내기

sonumb 2021. 8. 26. 17:06

1.개요

클라이언트가 어느 주소로 접속했는지 또한 어느 NIC로 접속했는지 서버측에서 알고 싶을 때, 아래와 같은 방법으로 이 정보를 획득한다.

  • IP 주소획득은 getsockname()으로,
  • NIC 이름은 getifaddrs()로

아래는 이 함수들을 이용해 실제 주소를 획득하는 예제다.

ref 정보

https://stackoverflow.com/questions/848040/how-can-i-get-the-interface-name-index-associated-with-a-tcp-socket

아래는 IPv4 이며, IPv6용이 있는지 확인해봐야 한다.

2.소스

서버는 클라이언트 접속후 단순히 클라이언트 접속 NIC의 IP와 이름을 출력하며 이를 반복한다.

server.c

#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <ifaddrs.h>

int main(int argc, char* argv[])
{
  int svr_sock;
  int clnt_sock;
  int ret = 0;
  int endconn = 0;

  char buf[512];

  struct sockaddr_in serv_addr;
  struct sockaddr_in clint_addr;
  socklen_t clnt_addr_size;

  struct sockaddr_in addr[1];
  socklen_t          addrlen;
  struct ifaddrs   * ifaddr = NULL;
  struct ifaddrs   * ifa  = NULL;

  if(argc != 2)
    {
      printf("%s <port>\n", argv[0]);
      exit(1);
    }
  svr_sock = socket(PF_INET, SOCK_STREAM,0); //1번
  if(svr_sock == -1)
    printf("socket error\n");

  memset(&serv_addr, 0, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  serv_addr.sin_port = htons(atoi(argv[1]));

  if(bind(svr_sock,
         (struct sockaddr*)&serv_addr,
         sizeof(serv_addr)) == -1) //2번
    printf("bind error\n");

  if(listen(svr_sock,5)==-1) //3번
    printf("listen error\n");

  clnt_addr_size = sizeof(clint_addr);

label_try_accept:
  clnt_sock = accept(svr_sock,
                     (struct sockaddr*)&clint_addr,
                     &clnt_addr_size); //4번
  if(clnt_sock == -1)
    {
      printf("accept error\n");
      exit(1);
    }

  ret = getsockname( clnt_sock,
                    (struct sockaddr *)addr,
                    &addrlen );
  if( ret != 0 )
    {
      printf("accept error:%s\n", strerror(errno) );
      exit(1);
    }

  getifaddrs( &ifaddr );

  for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next )
    {
      if (ifa->ifa_addr)
        {
          if (AF_INET == ifa->ifa_addr->sa_family)
            {
              struct sockaddr_in* inaddr = (struct sockaddr_in*)ifa->ifa_addr;
              if (inaddr->sin_addr.s_addr == addr->sin_addr.s_addr)
                {
                  if (ifa->ifa_name != NULL)
                    {
                      // Found it
                      strncpy(buf, inet_ntoa(addr->sin_addr), 512);
                      printf("sock's addr(%s), nic(%s)\n",
                               buf, ifa->ifa_name );
                    }
                }
            }
        }
    }

  freeifaddrs(ifaddr);

  do {
    ret = recv(clnt_sock, buf, 1, 0 );
    switch( ret )
      {
      case 1:
        fputc((int)buf[0], stdout);
        fflush(stdout);
        break;
      case 0: /* end-of-connection */
        goto label_try_accept;
        break;
      case -1:
        fprintf( stderr, "ERROR: %s\n", strerror(errno) );
        endconn = 1;
        break;
      default:
        abort();
      }
  } while ( endconn == 0 );

  close(svr_sock); //6번
  close(clnt_sock);
  return 0;
}

client.c

#include<stdio.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<unistd.h>
#include<string.h>

int main(int argc, char* argv[])
{
  int my_sock;
  struct sockaddr_in serv_addr;
  int str_len;
  if(argc != 3)
    {
      printf("%s <IP> <PORT>\n", argv[0]);
      exit(1);
    }
  my_sock = socket(PF_INET,SOCK_STREAM,0); //1번
  if(my_sock == -1)
    printf("socket error \n");
  memset(&serv_addr,0,sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr.s_addr =
               inet_addr(argv[1]);
  serv_addr.sin_port=htons(atoi(argv[2]));

  if(connect(my_sock,
            (struct sockaddr*)&serv_addr,
            sizeof(serv_addr))==-1) //2번
    printf("connect error\n");

  close(my_sock); //4번
  return 0;
}

3.실행 결과

server

$ ./server 6600
sock's addr(192.168.19.135), nic(ens33)
sock's addr(127.0.0.1), nic(lo)

client

$ ./client 192.168.19.135 6600  
$ ./client 127.0.0.1 6600  
$  
반응형