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의 소켓 프로그래밍은 네트워크 통신을 처리하는 강력한 방법입니다.
서버 측 프로세스 생성
서버는 다음 단계를 통해 생성됩니다.
1. 소켓 생성
이 단계에는 소켓() 함수를 사용하여 소켓을 생성하는 작업이 포함됩니다.
매개변수:
- sockfd: 소켓 설명자 정수(파일 핸들과 같은)
- 도메인: 정수는 통신 도메인을 지정합니다. 동일한 호스트의 프로세스 간 통신을 위해 POSIX 표준에 정의된 AF_LOCAL을 사용합니다. IPV4로 연결된 서로 다른 호스트의 프로세스 간 통신을 위해 IPV6으로 연결된 프로세스에 대해 AF_INET 및 AF_I NET 6을 사용합니다.
- 유형: 통신 유형
SOCK_STREAM: TCP(안정적인 연결 지향)
SOCK_DGRAM: UDP(신뢰할 수 없는 연결 없음) - 규약: IP(인터넷 프로토콜)의 프로토콜 값은 0입니다. 이는 패킷의 IP 헤더에 있는 프로토콜 필드에 나타나는 숫자와 동일합니다.(자세한 내용은 man 프로토콜 참조)
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()와 같은 호출이 무기한 대기한다는 의미입니다. 필요한 경우 소켓을 비차단 모드로 설정할 수 있습니다.