C로 소켓 프로그래밍

C로 소켓 프로그래밍

소켓 프로그래밍 네트워크의 두 노드를 연결하여 서로 통신하는 방법입니다. 한 소켓(노드)은 IP의 특정 포트를 수신하는 반면 다른 소켓은 연결을 형성하기 위해 다른 소켓에 연결됩니다. 클라이언트가 서버에 접근하는 동안 서버는 리스너 소켓을 형성합니다.
소켓 프로그래밍은 인스턴트 메시징 애플리케이션 바이너리 스트리밍 및 문서 협업 온라인 스트리밍 플랫폼 등에 널리 사용됩니다.

이 C 프로그램에서는 클라이언트/서버 모델을 보여주기 위해 서버와 클라이언트 간에 하나의 hello 메시지를 교환합니다.

서버.c

C
   #include         #include         #include         #include         #include         #include         #define PORT 8080   int     main  (  int     argc       char     const  *     argv  [])   {      int     server_fd       new_socket  ;      ssize_t     valread  ;      struct     sockaddr_in     address  ;      int     opt     =     1  ;      socklen_t     addrlen     =     sizeof  (  address  );      char     buffer  [  1024  ]     =     {     0     };      char  *     hello     =     'Hello from server'  ;      // Creating socket file descriptor      if     ((  server_fd     =     socket  (  AF_INET       SOCK_STREAM       0  ))      <     0  )     {      perror  (  'socket failed'  );      exit  (  EXIT_FAILURE  );      }      // Forcefully attaching socket to the port 8080      if     (  setsockopt  (  server_fd       SOL_SOCKET        SO_REUSEADDR     |     SO_REUSEPORT       &  opt        sizeof  (  opt  )))     {      perror  (  'setsockopt'  );      exit  (  EXIT_FAILURE  );      }      address  .  sin_family     =     AF_INET  ;      address  .  sin_addr  .  s_addr     =     INADDR_ANY  ;      address  .  sin_port     =     htons  (  PORT  );      // Forcefully attaching socket to the port 8080      if     (  bind  (  server_fd       (  struct     sockaddr  *  )  &  address        sizeof  (  address  ))       <     0  )     {      perror  (  'bind failed'  );      exit  (  EXIT_FAILURE  );      }      if     (  listen  (  server_fd       3  )      <     0  )     {      perror  (  'listen'  );      exit  (  EXIT_FAILURE  );      }      if     ((  new_socket      =     accept  (  server_fd       (  struct     sockaddr  *  )  &  address        &  addrlen  ))       <     0  )     {      perror  (  'accept'  );      exit  (  EXIT_FAILURE  );      }          // subtract 1 for the null      // terminator at the end      valread     =     read  (  new_socket       buffer        1024     -     1  );         printf  (  '%s  n  '       buffer  );      send  (  new_socket       hello       strlen  (  hello  )     0  );      printf  (  'Hello message sent  n  '  );      // closing the connected socket      close  (  new_socket  );          // closing the listening socket      close  (  server_fd  );      return     0  ;   }   

클라이언트.c

C
   #include          #include         #include         #include         #include         #define PORT 8080   int     main  (  int     argc       char     const  *     argv  [])   {      int     status       valread       client_fd  ;      struct     sockaddr_in     serv_addr  ;      char  *     hello     =     'Hello from client'  ;      char     buffer  [  1024  ]     =     {     0     };      if     ((  client_fd     =     socket  (  AF_INET       SOCK_STREAM       0  ))      <     0  )     {      printf  (  '  n   Socket creation error   n  '  );      return     -1  ;      }      serv_addr  .  sin_family     =     AF_INET  ;      serv_addr  .  sin_port     =     htons  (  PORT  );      // Convert IPv4 and IPv6 addresses from text to binary      // form      if     (  inet_pton  (  AF_INET       '127.0.0.1'       &  serv_addr  .  sin_addr  )       <=     0  )     {      printf  (      '  n  Invalid address/ Address not supported   n  '  );      return     -1  ;      }      if     ((  status      =     connect  (  client_fd       (  struct     sockaddr  *  )  &  serv_addr        sizeof  (  serv_addr  )))       <     0  )     {      printf  (  '  n  Connection Failed   n  '  );      return     -1  ;      }          // subtract 1 for the null      // terminator at the end      send  (  client_fd       hello       strlen  (  hello  )     0  );      printf  (  'Hello message sent  n  '  );      valread     =     read  (  client_fd       buffer        1024     -     1  );         printf  (  '%s  n  '       buffer  );      // closing the connected socket      close  (  client_fd  );      return     0  ;   }   


컴파일 중

 gcc client.c -o clientgcc server.c -o server  


산출

 Client:Hello message sentHello from serverServer:Hello from clientHello message sent  

소켓 프로그래밍의 구성 요소

1. 소켓

소켓 프로그램이 네트워크에 액세스하여 네트워크를 통해 다른 프로세스/노드와 통신하는 데 사용되는 핵심 구성 요소 중 하나입니다. 이는 단순히 통신의 종점 역할을 하는 IP 주소와 포트 번호의 조합입니다.
예: 192.168.1.1:8080 여기서 콜론으로 구분된 두 부분은 IP 주소( 192.168.1.1 ) 그리고 포트 번호( 8080 ).

소켓 유형:

  • TCP 소켓(스트림 소켓): 안정적인 연결 기반 통신을 제공합니다(예: TCP 프로토콜 ).
  • UDP 소켓(데이터그램 소켓): 연결 없는 통신을 더 빠르게 제공하지만 신뢰할 수 없습니다(예: UDP 프로토콜 ).

2. 클라이언트-서버 모델

그만큼 클라이언트-서버 모델 클라이언트와 서버가 서로 상호 작용하여 정보나 서비스를 교환하는 소켓 프로그래밍에 사용되는 아키텍처를 말합니다. 이 아키텍처를 통해 클라이언트는 서비스 요청을 보내고 서버는 해당 서비스 요청을 처리하고 응답을 보낼 수 있습니다.

서버 및 클라이언트 모델의 상태 다이어그램

C로 소켓 프로그래밍소켓의 서버 및 클라이언트 모델에 대한 상태 다이어그램

C의 소켓 프로그래밍은 네트워크 통신을 처리하는 강력한 방법입니다.

서버 측 프로세스 생성

서버는 다음 단계를 통해 생성됩니다.

1. 소켓 생성

이 단계에는 소켓() 함수를 사용하여 소켓을 생성하는 작업이 포함됩니다.

매개변수:

  • sockfd: 소켓 설명자 정수(파일 핸들과 같은)
  • 도메인: 정수는 통신 도메인을 지정합니다. 동일한 호스트의 프로세스 간 통신을 위해 POSIX 표준에 정의된 AF_LOCAL을 사용합니다. IPV4로 연결된 서로 다른 호스트의 프로세스 간 통신을 위해 IPV6으로 연결된 프로세스에 대해 AF_INET 및 AF_I NET 6을 사용합니다.
  • 유형: 통신 유형
    SOCK_STREAM: TCP(안정적인 연결 지향)
    SOCK_DGRAM: UDP(신뢰할 수 없는 연결 없음)
  • 규약: IP(인터넷 프로토콜)의 프로토콜 값은 0입니다. 이는 패킷의 IP 헤더에 있는 프로토콜 필드에 나타나는 숫자와 동일합니다.(자세한 내용은 man 프로토콜 참조)
C
   sockfd     =     socket  (  domain       type       protocol  )   

2. 소켓 옵션 설정

이는 파일 설명자 sockfd가 참조하는 소켓에 대한 옵션을 조작하는 데 도움이 됩니다. 이는 완전히 선택 사항이지만 주소와 포트를 재사용하는 데 도움이 됩니다. 이미 사용 중인 주소와 같은 오류를 방지합니다.

C
   setsockopt  (  sockfd       level       optname       optval       socklen_t     optlen  );   

3. 바인딩

소켓을 생성한 후 바인딩() 함수는 소켓을 addr(사용자 정의 데이터 구조)에 지정된 주소와 포트 번호에 바인딩합니다. 예제 코드에서는 서버를 로컬 호스트에 바인딩하므로 INADDR_ANY를 사용하여 IP 주소를 지정합니다.

C++
   bind  (  sockfd       sockaddr     *  addr       socklen_t     addrlen  );   

매개변수:

  • 양말 : 소켓() 함수를 사용하여 생성된 소켓 파일 설명자입니다.
  • 주소 : 소켓을 바인딩하기 위한 IP 주소와 포트 번호를 포함하는 구조체 sockaddr에 대한 포인터입니다.
  • 추가 : addr 구조체의 길이.

4. 들어보세요

이 단계에서 서버는 서버 소켓을 수동 모드로 설정하여 클라이언트가 연결을 위해 서버에 접근할 때까지 기다리는 Listen() 함수를 사용합니다. 백로그는 sockfd에 대해 보류 중인 연결 대기열이 커질 수 있는 최대 길이를 정의합니다. 대기열이 가득 찼을 때 연결 요청이 도착하면 클라이언트는 ECONNREFUSED 표시와 함께 오류를 수신할 수 있습니다.

C
   listen  (  sockfd       backlog  );   

매개변수 :

  • 양말 : 소켓() 함수를 사용하여 생성된 소켓 파일 설명자입니다.
  • 백로그 : 서버가 연결 수락을 기다리는 동안 보류 중인 연결을 보유하는 대기열의 크기를 나타내는 숫자입니다.

5. 수락

이 단계에서 서버는 청취 소켓에 대한 보류 중인 연결 대기열에서 첫 번째 연결 요청을 추출합니다. sockfd는 다음을 사용하여 새로운 연결된 소켓을 생성합니다. 수용하다() 함수를 실행하고 해당 소켓을 참조하는 새 파일 설명자를 반환합니다. 이 시점에서 클라이언트와 서버 사이에 연결이 설정되고 데이터를 전송할 준비가 됩니다.

C
   new_socket  =     accept  (  sockfd       sockaddr     *  addr       socklen_t     *  addrlen  );   

매개변수:

  • 양말 : 소켓()과 바인드()에 의해 반환된 소켓 파일 설명자입니다.
  • 주소 : 클라이언트의 IP 주소와 포트 번호를 보유할 구조체 sockaddr에 대한 포인터입니다.
  • 추가 : 주소 구조의 길이를 지정하는 변수에 대한 포인터입니다.

6. 보내기/받기

이 단계에서 서버는 클라이언트로부터 데이터를 보내거나 받을 수 있습니다.

보내다(): 클라이언트에 데이터를 보내려면

C
   send  (  sockfd       *  buf       len       flags  );   

매개변수:

  • 양말 : 소켓() 함수에 의해 반환된 소켓 파일 설명자입니다.
  • 버프 : 전송할 데이터가 포함된 버퍼에 대한 포인터입니다.
  • 오직 : 전송할 데이터의 바이트 수입니다.
  • 깃발 : 데이터 전송 방법에 대한 다양한 옵션을 지정하는 정수는 일반적으로 기본 동작에 0이 사용됩니다.

받다() : 클라이언트로부터 데이터를 수신합니다.

C
   recv  (     sockfd       *  buf       len       flags  );   

매개변수:

  • 양말 : 소켓() 함수에 의해 반환된 소켓 파일 설명자입니다.
  • 버프 : 저장할 데이터가 포함된 버퍼에 대한 포인터입니다.
  • 오직 : 전송할 데이터의 바이트 수입니다.
  • 깃발 : 데이터 전송 방법에 대한 다양한 옵션을 지정하는 정수는 일반적으로 기본 동작에 0이 사용됩니다.

6. 닫기

정보 교환이 완료된 후 서버는 close() 함수를 사용하여 소켓을 닫고 시스템 리소스를 해제합니다.

C
   close  (  fd  );   

매개변수:

  • fd: 소켓의 파일 설명자.

클라이언트 측 프로세스 생성

클라이언트 측 프로세스를 생성하려면 다음 단계를 따르십시오.

1. 소켓 연결

이 단계에는 서버의 소켓 생성과 동일한 방식으로 수행되는 소켓 생성이 포함됩니다.

2. 연결

connect() 시스템 호출은 파일 설명자 sockfd가 참조하는 소켓을 addr이 지정한 주소에 연결합니다. 서버의 주소와 포트는 addr에 지정됩니다.

C++
   connect  (  sockfd       sockaddr     *  addr       socklen_t     addrlen  );   

매개변수

  • 양말 : 소켓() 함수에 의해 반환된 소켓 파일 설명자입니다.
  • 주소 : 서버의 IP 주소와 포트 번호를 포함하는 struct sockaddr에 대한 포인터입니다.
  • 추가 : 주소의 크기.

3. 보내기/받기

이 단계에서 클라이언트는 서버가 클라이언트로부터 데이터를 보내고 받는 방법과 유사한 send() 및 receive() 함수를 사용하여 수행되는 서버로부터 데이터를 보내거나 받을 수 있습니다.

4. 닫기

정보 교환이 완료되면 클라이언트도 서버와 동일한 방식으로 close() 함수를 사용하여 생성된 소켓을 닫고 시스템 리소스를 해제해야 합니다.

소켓 프로그래밍의 일반적인 문제 및 수정 사항

  • 연결 실패: 연결 실패를 방지하려면 클라이언트가 올바른 연결을 시도하고 있는지 확인해야 합니다. IP 주소 및 포트 .
  • 포트 바인딩 오류: 이러한 오류는 다른 애플리케이션에서 포트를 이미 사용 중일 때 발생합니다. 이 시나리오에서는 해당 포트에 대한 바인딩이 실패합니다. 다른 포트를 사용해 보거나 해당 포트를 사용하는 이전 애플리케이션을 닫으세요.
  • 차단 소켓: 기본적으로 소켓은 차단됩니다. 이는 클라이언트 연결이나 데이터가 없으면 accept() 또는 recv()와 같은 호출이 무기한 대기한다는 의미입니다. 필요한 경우 소켓을 비차단 모드로 설정할 수 있습니다.
퀴즈 만들기