네트워크 기본 사항: 소켓 소켓

1. 예비 지식

우정 링크: 네트워크 기본 사항 시작하기

1.1 소스 MAC 주소 및 대상 MAC 주소

MAC 주소(미디어 액세스 제어 주소, LAN 주소)는 OSI 모델의 두 번째 데이터 링크 계층에서 로컬 네트워크에 있는 장치의 물리적 주소를 식별하는 역할을 합니다.

동일한 LAN에 있는 여러 호스트의 경우 LAN으로 직접 보내는 데이터는 모든 호스트(전송 호스트 자체 포함)에서 공유되며 이는 브로드캐스팅과 동일하지만 특정 호스트만 처리합니다(모든 호스트 메시지가 수신됨). 이는 호스트가 보낸 데이터에 지정된 호스트의 MAC 주소가 포함되어 있기 때문이며, 또한 데이터의 무결성을 검증하기 위해 데이터가 발생한 호스트의 MAC 주소도 포함하여 호스트가 정보를 보낸 후 정보를 받습니다. 확인합니다.

여기서, 정보를 보내는 호스트의 MAC 주소를 발신지 MAC 주소라 하고, 정보를 받는 호스트의 MAC 주소를 목적지 MAC 주소라 한다.

1.2 소스 IP 주소 및 목적지 IP 주소

IP 주소(인터넷 프로토콜, 인터넷 프로토콜)는 OSI 모델의 세 번째 네트워크 계층에서 역할을 하며 인터넷에 연결된 장치를 고유하게 식별하는 데 사용되는 논리 주소입니다.

MAC 주소는 장치의 전역 고유성을 식별하지만 다른 네트워크에서 데이터 전송은 MAC 주소만으로 완료할 수 없습니다. 우리는 데이터 전송이 네트워크 프로토콜 스택을 통해 전송된다는 것을 알고 있습니다. 데이터가 위에서 아래로 전송될 때 헤더 정보는 프로토콜의 각 계층에 의해 캡슐화됩니다. 데이터가 아래에서 위로 전송될 때 프로토콜의 각 계층은 캡슐 해제됩니다. 응용 프로그램까지 계층은 데이터 자체를 가져옵니다. 그러나 서로 다른 네트워크는 일부 계층에서 서로 다른 프로토콜을 가질 수 있으므로 헤더의 캡슐화 및 캡슐화 해제 프로세스는 LAN만큼 대칭적이지 않으므로 서로 다른 네트워크에서 점프하려면 IP 주소와 협력해야 합니다.

1.3 MAC 주소와 IP 주소의 일치

서로 다른 네트워크에서 라우터는 "passers-by" 역할을 합니다. 사실 데이터는 전송 중에 여러 다른 네트워크를 통과할 수 있으므로 헤더 정보의 두 MAC 주소는 라우터(라우터도 하드웨어입니다) 변경 사항을 따르고 있습니다. 그러나 소스 IP 주소와 대상 IP 주소는 변경되지 않습니다. 마치 탕생이 갈 때마다 "동당나라에서 와서 서방에 가서 경전을 배우라"고 말하는 것과 같습니다. 출발지와 목적지를 바꾸지 말아야 합니다(특수한 경우 소스 IP가 바뀔 수 있습니다. 그러나 목적지 IP는 변경되지 않습니다) 그러나 당신이 만나는 친절한 사람들은 이 문장을 듣고 Tang Seng에게 다음 장소로 가는 방법을 알려줄 것입니다.

1.4 소스 포트 번호 및 대상 포트 번호

포트 번호(PORT)의 주요 기능은 컴퓨터의 특정(특히 네트워크 서비스) 프로세스가 제공하는 서비스를 나타내는 것이며, 전송 계층에서 호스트 프로세스의 고유성을 식별하는 기능을 합니다. 즉, 포트 번호는 하나의 프로세스에서만 사용할 수 있으며 프로세스는 여러 포트 번호를 사용할 수 있습니다.

포트 번호는 0~65535 범위의 16비트 부호 없는 정수입니다. 인터넷에서 포트 번호는 서로 다른 네트워크 서비스를 식별하는 데 사용됩니다. 예를 들어 웹 서버는 일반적으로 포트 번호 80을 사용하고 SMTP 서버는 포트 번호 25를 사용합니다.

프로세스 관련 지식과 결합하여 데이터 자체는 실행 중인 프로세스에 의해 처리되므로 네트워크를 통해 다른 호스트로 데이터를 전송하는 것은 처리하는 프로세스일 뿐입니다. 따라서 서로 다른 호스트에 있는 서로 다른 프로세스 간에 데이터가 전송되는 것, 즉 네트워크 수준에서의 프로세스 간 통신이라고 볼 수 있다. 포트 번호의 이름이 매우 생생하며 실제 포트(포트)도 유사합니다. 호스트의 다양한 프로세스는 출항 준비가 된 화물선과 같으며 서로 다른 번호의 위치에서 상품을 기다리고 있습니다. 상품이 준비되면 프로세스가 하나씩 처리합니다.

IP 주소는 공용 네트워크에서 호스트의 고유성을 식별하고 포트 번호는 호스트에서 프로세스의 고유성을 식별하므로 IP 주소 + 포트 번호는 네트워크의 특정 호스트에서 프로세스의 고유성을 식별합니다. IP 주소와 유사하게 포트 번호는 전송 계층에서 헤더 정보로 캡슐화됩니다.

PID와 포트 번호 모두 호스트에서 프로세스의 고유성을 나타낼 수 있으므로 네트워크 전송에 PID를 사용하지 않는 이유는 무엇입니까?

포트 번호로 식별되는 프로세스는 PID로 식별되는 프로세스의 하위 집합이며 서로 다른 범위를 식별합니다. PID는 모든 사람의 ID 카드와 같습니다.이 땅에서 우리의 고유성을 나타낼 수 있지만 종종 사용하지 않지만 교실 좌석 번호와 같이 적절하고 관리하기 쉬운 식별을 사용합니다. 학교는 학생 번호를 사용합니다. , 수험표는 대학 입시에 사용되며 신분증은 은행 등에서 사용됩니다. 물론 PID를 사용하는 것은 가능하지만 이는 모든 프로세스에서 네트워크 서비스 프로세스를 가려야 하는 부담을 증가시킬 뿐만 아니라 다른 비 네트워크 서비스 프로세스의 보안 위험도 증가시킬 것입니다. 이것은 또한 특정 유형의 요소를 나타내기 위해 단일 식별자를 사용하여 선별 비용을 절약할 수 있는 분리 접근 방식입니다.

1.5소켓

소켓(소켓)은 컴퓨터 네트워크의 소프트웨어 구조로, 컴퓨터 네트워크의 노드 간에 데이터를 송수신하는 데 사용됩니다. 소켓의 구조와 속성은 네트워크 아키텍처의 API(응용 프로그래밍 인터페이스)에 의해 정의됩니다. 애플리케이션이 네트워크에 I/O를 삽입하고 네트워크의 다른 애플리케이션과 통신할 수 있습니다. 간단히 말해서 소켓은 컴퓨터 간의 통신 방식 또는 규칙입니다.

소켓이라는 단어는 원래 소켓이나 슬롯을 가리키는 컴퓨터 네트워크에서 "소켓"으로 번역됩니다. 컴퓨터 네트워킹에서 두 프로그램 간에 연결이 설정되는 끝점을 설명하는 데 사용됩니다. 전원을 켜기 위해 전기 플러그를 콘센트에 꽂아야 하는 것처럼 두 프로그램은 연결을 설정하기 위해 "콘센트"가 필요합니다. 따라서 단어는 "소켓"으로 확장됩니다.

소켓 기능은 응용 프로그램이 TCP/IP 프로토콜 제품군과 통신하기 위한 중간 소프트웨어 추상화 계층이며 인터페이스 집합입니다. 디자인 모드에서 Socket은 실제로 Socket 인터페이스 뒤에 복잡한 TCP/IP 프로토콜 제품군을 숨기는 파사드 모드입니다.사용자에게는 단순한 인터페이스 세트가 전부입니다. 그것은 기본 복잡한 프로토콜 시스템과 실행 프로세스를 캡슐화하고 캡슐화의 결과는 SOCKET입니다. 즉, SOCKET은 우리가 통신을 위해 프로토콜을 호출하는 작업 인터페이스입니다.

소켓은 유닉스에서 유래되었으며 유닉스/리눅스의 기본 철학 중 하나는 "모든 것이 파일이다"로 "열기 열기 -> 읽기 및 쓰기 쓰기/읽기 -> 닫기 닫기" 모드로 작동할 수 있습니다. 소켓은 이 모드의 구현입니다.소켓은 특수 파일이며 일부 소켓 기능은 그것에 대한 작업(읽기/쓰기 IO, 열기, 닫기)입니다.

실제로는 다양한 정의에 신경 쓸 필요가 없으며 단순히 데이터 패킷으로 이해될 수 있으며, 이는 다양한 통신 관련 속성을 포함하는 구조입니다. 내장 라이브러리에는 함수 내에서 이 데이터 패키지의 속성을 처리하는 많은 함수가 있습니다. 소켓의 본질은 일정한 규칙(프로토콜)에 따라 구성된 파일이라는 점에 유의할 필요가 있으며, 통신의 양쪽 끝이 합의된 규칙에 따라 내부 데이터를 사용하는 한 통신 프로세스가 실현될 수 있습니다.

연결:

1.6 UCP 프로토콜 및 TCP 프로토콜

다음은 UCP 프로토콜과 TCP 프로토콜을 간략하게 소개합니다.

TCP(전송 제어 프로토콜, 전송 제어 프로토콜)는 연결 지향의 안정적인 바이트 스트림 서비스를 제공합니다. 즉, 클라이언트와 서버가 데이터를 교환하기 전에 두 당사자 간에 TCP 연결이 설정되어야 데이터가 전송될 수 있습니다. 또한 시간 초과 재전송, 중복 데이터 폐기, 데이터 검사 및 흐름 제어와 같은 기능을 제공하여 데이터가 한쪽 끝에서 다른 쪽 끝으로 전송될 수 있도록 합니다.

UDP(User Datagram Protocol, User Datagram Protocol)는 간단한 데이터그램 지향 전송 계층 프로토콜입니다. 그것은 신뢰성을 제공하지 않고 단지 응용 프로그램에 의해 IP 계층으로 전달된 데이터그램을 전송하지만 목적지에 도달할 것이라는 보장은 없습니다. UDP는 데이터그램을 전송하기 전에 클라이언트와 서버 사이에 연결을 설정할 필요가 없고 타임아웃 재전송과 같은 메커니즘이 없기 때문에 전송 속도가 매우 빠릅니다.

TCP는 간단히 말해서 전화를 거는 것과 같다 먼저 통신을 하기 위해서는 통신 채널이 필요하다.

UDP가 안정성을 제공하지 않고 계속 사용하는 이유는 무엇입니까?

UDP는 신뢰성을 제공하지는 못하지만 전송 속도가 빠르다는 장점이 있습니다. UDP는 데이터그램을 전송하기 전에 클라이언트와 서버 사이에 연결을 설정할 필요가 없고 타임아웃 재전송과 같은 메커니즘이 없기 때문에 전송 속도가 매우 빠릅니다. 이는 온라인 게임, 실시간 오디오 및 비디오 전송 등과 같이 실시간 요구 사항이 높은 일부 응용 프로그램에 매우 중요합니다. 이 경우 UDP 프로토콜을 사용하면 더 빠른 응답 속도를 제공할 수 있습니다. 일반적으로 TCP는 데이터 보안을 위해 사용되며 UDP는 특별한 시나리오(예: 라이브 방송 및 비디오)에서 사용될 수 있습니다. 우수한 통신 알고리즘에서는 TCP와 UDP가 동시에 사용되는 경우가 많으며 실제 상황에 따라 전략이 계획됩니다.

사실 여기서 "신뢰할 수 있다"는 것은 상대적이고 중립적인 단어입니다. 즉, "신뢰성"을 달성하기 위해 TCP는 많은 비용을 지불했습니다. 예를 들어 프로토콜이 더 복잡하고 유지하기 어렵기 때문에 전송 속도가 UDP만큼 빠르지 않습니다. 그것이 "신뢰할 수 있는지" 여부는 프로토콜 자체의 특성입니다. 그들이 말할 수 있다면 UDP는 TCP에게 "왜 그렇게 피곤해? 나처럼 데이터를 반대편에 던지는 것이 좋지 않을까?"라고 말할 수 있습니다.

1.7 네트워크 바이트 순서

높고 낮은

10진수 값의 경우 다항식 1 0 n 10^n 이 될 수 있습니다.1 0n 의 합계 표현, 예를 들어, 123 = 1 × 10 2 + 2 × 10 1 + 3 × 10 0 123 = {1×10^2} + {2×10^1} + {3×10 ^0}123=1×1 02+2×1 01+×1 00 , 바이트의 높이는 가중치의 크기에 해당합니다. 예를 들어 정수 0x12345678의 경우 0x12가 가장 높은 바이트이고 가중치는 16의 세 번째 거듭제곱이고 0x78은 가장 낮은 바이트이며 가중치는 16의 0승입니다.

높고 낮은 주소

메모리 주소의 레벨은 메모리 주소의 숫자 크기를 나타냅니다. 예를 들어 0x1000은 0x0100보다 상위 주소입니다.

간단히 말해서 왼쪽이 낮고 오른쪽이 높습니다.

빅 엔디안과 리틀 엔디안

  • Little endian: 데이터의 높은 가중치 비트는 높은 주소에 해당합니다.
  • 빅 엔디안: 그 반대.

2바이트를 차지하는 16비트 정수 0x1234가 있다고 가정합니다. 빅 엔디안 컴퓨터에서 이 정수는 0x12 0x34 순서로 메모리에 저장됩니다. 즉, 최상위 바이트 0x12는 메모리의 하위 주소에 저장되고 최하위 바이트 0x34는 메모리의 상위 주소에 저장됩니다.

little-endian 컴퓨터에서 이 정수는 0x34 0x12 순서로 메모리에 저장됩니다. 즉, 최하위 바이트 0x34는 메모리의 하위 주소에 저장되고 최상위 바이트 0x12는 메모리의 상위 주소에 저장됩니다.

빅 엔디안은 현대의 읽기 및 쓰기 습관과 더 일치한다는 점을 기억하십시오.

네트워크 바이트 순서

데이터를 수신하는 호스트는 상대 호스트가 빅 엔디안인지 리틀 엔디안인지 알고 있습니까?

모른다. 호스트의 크기가 불확실하기 때문에 데이터를 받는 호스트는 상대 호스트가 빅엔디안인지 리틀엔디안인지 알아야 한다. 그렇지 않으면 데이터 읽기 오류가 발생합니다.

데이터를 보내는 호스트가 자신의 크고 작은 엔디안 속성 기능 필드를 헤더 정보에 넣는 것이 좋지 않을까요?

속성 필드를 찾는 전제는 데이터를 받는 호스트가 데이터를 보내는 호스트가 빅 엔디안인지 리틀 엔디안인지 이미 알고 있다는 점인데 이는 모순이다.

따라서 네트워크 바이트 순서는 빅 엔디안 사용을 직접 규정합니다. 따라서 호스트는 데이터를 보내고 받을 때 데이터의 바이트 순서를 변환해야 합니다.

무엇을 변환?

  • 데이터를 보내기 전에 호스트 바이트 순서에서 네트워크 바이트 순서로 변환해야 합니다.
  • 데이터를 수신한 후 네트워크 바이트 순서에서 호스트 바이트 순서로 변환해야 합니다.

일반적인 변환 기능

이 변환은 이미 C 표준 라이브러리에 의해 수행되며 실제로 Windows는 동일한 함수 집합을 사용합니다.

#include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

명명 해석:

  • h: 호스트, 호스트의 바이트 순서를 나타냅니다.
  • n: net, 네트워크 바이트 순서를 나타냅니다.
  • l: long, 32비트 long 정수를 나타냅니다.
  • s: short, 16비트 짧은 정수를 나타냅니다.

일반적으로 테스트 머신이 빅 엔디안인지 리틀 엔디안인지에 관계없이 이식성을 위해 이러한 함수를 호출해야 하며, 머신 자체가 빅 엔디안인 경우 이러한 함수가 직접 반환됩니다.

코딩 습관 : 이론상 불필요한 단계가 가끔 있지만 실제 적용시 다양한 문제가 발생할 수 있으므로 보험을 위해 추가 단계가 수행됩니다.

2. 소켓 네트워크 프로그래밍

2.1 소켓 공통 인터페이스

TCP는 연결 지향이며 소켓을 통해 통신을 실현하는 단계는 다음과 같습니다.

  1. 소켓 만들기(서버 및 클라이언트)
  2. 바인딩 포트 번호(서버)
  3. 청취 소켓(서버 측)
  4. 연결 설정(클라이언트)

UDP는 바이트 지향이며 그 단계는 비교적 간단합니다.

  1. 소켓 만들기(서버 및 클라이언트)
  2. 바인딩 포트 번호(서버)

그 중 TCP와 UDP 서버 모두 소켓을 만들고 포트 번호를 바인드해야 합니다.이 단계는 실제로 도입될 것이며 인터페이스의 수를 통해서만 TCP가 UDP보다 훨씬 많은 작업을 수행했음을 알 수 있습니다.

여기서는 제한된 지식으로 인해 일부 매개변수를 자세히 설명할 수 없으며 TCP/UDP 항목에서 소개합니다.

기능을 통해 man + [函数名]기능의 관련 정보를 조회하는 것이 매우 편리합니다.

헤더 파일은 다음과 같습니다.

#include <sys/types.h>
#include <sys/socket.h>

소켓 생성

socket()소켓을 생성하는 함수

int socket(int domain, int type, int protocol);

매개변수:

  • 도메인(도메인): 소켓 제품군을 지정합니다. 간단히 말해 통신 방법이 로컬인지 네트워크인지 지정합니다.
    • AF_UNIX, AF_LOCAL: 로컬 통신.
    • AF_INET:통신.
  • type: 소켓 유형, 즉 전송 방법을 지정합니다.
    • SOCK_STREAM: 연결 지향 소켓/스트림 형식 소켓.
    • SOCK_DGRAM: 비연결 소켓/데이터그램 소켓.
  • 프로토콜(protocol): 전송 프로토콜을 지정합니다. 기본값은 이며 0일반적으로 사용되는 프로토콜은 다음과 같습니다.
    • IPPROTO_TCP: TCP 전송 프로토콜을 나타냅니다.
    • IPPTOTO_UDP: UDP 전송 프로토콜을 나타냅니다.

묶다

bind()함수는 지정된 IP 주소와 포트 번호로 소켓을 바인딩하는 데 사용됩니다. 일반적으로 TCP 프로토콜 또는 UDP 프로토콜의 서버 측에 설정됩니다.

int bind(int sockfd, const struct sockaddr *addr,
                socklen_t addrlen);

매개변수:

  • sockfd: 바인딩할 소켓 파일 설명자, 그 본질은 배열 첨자입니다.
  • struct sockaddraddr: 바인딩할 IP 주소와 포트 번호를 포함하는 유형 구조에 대한 포인터 입니다 .
  • addraddrlen은 가리키는 주소 구조의 크기 입니다 .

청취 소켓

listen()소켓을 수동 청취 상태로 전환하는 데 사용되는 기능입니다. 일반적으로 TCP 프로토콜의 서버 측에서 설정됩니다.

int listen(int sockfd, int backlog);

매개변수:

  • sockfd: 청취할 소켓 파일 디스크립터.
  • 백로그: 미해결 연결 대기열의 최대 길이, 즉 연결을 기다릴 수 있는 클라이언트 수입니다.

요청을 받다

accept()이 함수는 청취 소켓의 미해결 연결 대기열에서 첫 번째 연결 요청을 추출하고, 연결된 새 소켓을 만들고, 소켓을 가리키는 파일 설명자를 반환하는 데 사용됩니다. 일반적으로 TCP 프로토콜의 서버 측에서 설정됩니다.

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

매개변수:

  • sockfd: 청취 소켓의 파일 설명자.
  • addr: 클라이언트의 주소 정보를 저장하는 데 사용되는 struct sockaddr 유형 구조에 대한 포인터입니다.
  • addrlen: 클라이언트 주소 구조의 크기를 저장하는 데 사용되는 socklen_t 유형의 변수에 대한 포인터입니다.

연결 설정

connect()지정된 소켓과의 연결을 설정하는 데 사용되는 함수입니다. 일반적으로 TCP 프로토콜의 서버 측에서 설정됩니다.

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

매개변수:

  • sockfd는 연결할 소켓 파일 설명자입니다.
  • addr은 연결할 서버의 주소 정보를 포함하는 struct sockaddr유형 .
  • addrlen은 가 가리키는 주소 구조의 크기 addr입니다 .

2.2 공통 소켓

소켓은 서로 다른 호스트 또는 동일한 호스트에 있는 프로세스 간에 통신하는 데 사용되는 통신 메커니즘입니다. 스트림 소켓(SOCK_STREAM), 데이터그램 소켓(SOCK_DGRAM) 및 원시 소켓(SOCK_RAW)을 포함하여 많은 유형의 소켓이 있습니다. 여기서 우리는 웹 소켓에 대해 이야기하고 있습니다.

도메인 간 소켓

도메인 소켓은 특별한 유형의 소켓입니다. 소켓은 서로 다른 호스트 또는 동일한 호스트에 있는 프로세스 간에 통신하는 데 사용되는 통신 메커니즘입니다. 스트림 소켓(SOCK_STREAM), 데이터그램 소켓(SOCK_DGRAM) 및 원시 소켓(SOCK_RAW)을 포함하여 많은 유형의 소켓이 있습니다. 도메인 간 소켓은 이러한 유형 중 하나이며 동일한 호스트에서 프로세스 간 통신에 사용됩니다.

간단히 말해서 도메인 간 소켓은 다른 유형의 소켓과 유사한 API 및 통신 메커니즘을 공유하지만 동일한 호스트에서 프로세스 간 통신에 특화된 소켓 유형입니다.

원시 소켓

원시 소켓(Raw Socket)은 IP 프로토콜 패킷을 전송 계층 프로토콜 형식 없이 직접 보내고 받을 수 있도록 하는 특별한 유형의 소켓입니다. 이는 원시 소켓을 사용할 때 응용 프로그램이 전송 계층 프로토콜의 세부 사항을 자체적으로 처리해야 함을 의미합니다.

원시 소켓은 종종 nmap과 같은 보안 관련 응용 프로그램에서 사용되거나 사용자 공간에서 새로운 전송 계층 프로토콜을 구현하는 데 사용됩니다. 또한 IGMPv4, OSPF(Open Shortest Path First) 및 ICMP(Internet Control Message Protocol)와 같은 네트워크 장치의 라우팅 프로토콜에도 일반적으로 사용됩니다.

웹 소켓

네트워크 소켓(Network Socket)은 서로 다른 호스트에서 프로세스 간 통신에 사용되는 소켓입니다. 네트워크 간 통신을 달성하기 위해 TCP/IP 프로토콜 스택과 같은 네트워크 프로토콜 스택을 사용합니다. 웹 소켓은 IP 주소와 포트 번호를 사용하여 통신 끝점을 식별합니다.

네트워크 소켓에는 스트림 소켓(SOCK_STREAM)과 데이터그램 소켓(SOCK_DGRAM)의 두 가지 유형이 있습니다. 스트림 소켓은 데이터 전송에 TCP 프로토콜을 사용하여 안정적인 연결 지향 통신 서비스를 제공합니다. 데이터그램 소켓은 데이터 전송에 UDP 프로토콜을 사용하여 연결이 없고 신뢰할 수 없는 통신 서비스를 제공합니다.

2.3 sockaddr 구조

소켓 네트워크 소켓의 인터페이스를 소개할 때 sockaddr그 구조가 여러 번 언급되었는데,일반적인소켓 주소 구조는 소켓 프로그래밍에서 서로 다른 프로토콜 계열의 주소 정보를 전송하는 데 사용됩니다. 다음과 같이 정의됩니다.

struct sockaddr {
    
    
sa_family_t sa_family; /* 地址族 */
char sa_data[14]; /* 地址数据 */
};
  • sa_family 필드는 주소의 종류를 지정하기 위해 사용되는 주소 패밀리(address family)를 나타낸다. 일반적인 주소 계열에는 AF_INET(IPv4 주소), AF_INET6(IPv6 주소) 및 AF_UNIX(Unix 도메인 주소)가 포함됩니다.

  • sa_data 필드는 프로토콜 주소를 나타내며 길이와 내용은 주소 패밀리에 따라 다릅니다. 예를 들어 IPv4 주소의 경우 IP 주소와 포트 번호가 포함되고 Unix 도메인 주소의 경우 파일 시스템의 경로 이름이 포함됩니다.

sockaddr 구조는 다양한 유형의 주소를 잘 나타내지 못하기 때문에 sockaddr_in(IPv4 주소용) 및 sockaddr_un(Unix 도메인 주소용)과 같은 주소 계열 특정 구조가 소켓 주소를 나타내는 데 자주 사용됩니다.이러한 구조는 sockaddr 구조와 크기 및 정렬이 동일하며 서로 교환할 수 있습니다.

따라서 이 구조체의 유일한 목적은 컴파일러 경고를 피하기 위해 다른 프로토콜 계열의 주소 구조체 포인터를 "일반" 유형으로 변환하는 것입니다. 예를 들어 IPv4 프로토콜 제품군의 주소 구조 sockaddr_in의 경우 해당 정의는 다음과 같습니다.

struct sockaddr_in {
    
    
sa_family_t sin_family; /* AF_INET */
in_port_t sin_port; /* 端口号 */
struct in_addr sin_addr; /* IPv4地址 */
};

이 구조는 sockaddr 구조보다 더 구체적이며 IPv4 프로토콜 제품군에 필요한 주소 정보를 포함합니다. bind(2)와 같은 소켓 함수를 호출할 때 다음과 같이 sockaddr_in 구조 포인터를 sockaddr 구조 포인터로 캐스팅해야 합니다.

struct sockaddr_in addr;
/* 初始化addr */
bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));

이는 소켓 기능이 sa_family 필드에 따라 실제 주소 유형을 판단하고 해당 처리를 수행할 수 있도록 하기 위한 것입니다. 같은 방식으로 IPv6 또는 UNIX 도메인 소켓과 같은 다른 프로토콜 계열도 sockaddr 구조 포인터로 변환할 수 있는 sockaddr_in6 및 sockaddr_un과 같은 자체 주소 구조를 가지고 있습니다.

따라서 우리는 sockaddr 구조가 서로 다른 프로토콜 계열의 주소 구조 간의 차이점을 숨기는 추상 인터페이스라고 생각할 수 있으므로 소켓을 작동하는 통일된 방법을 사용할 수 있습니다.


이미지-20230429161434528

인터페이스를 균일하게 사용하기 위해 Linux 커널은 구조의 처음 2바이트를 사용하여 소켓 유형을 표시합니다. 즉, 소켓의 유형입니다. sa_family 필드는 sa_family_t형(부호 없는 정수형)의 변수로 보통 2바이트를 차지한다.

주소 패밀리는 소켓이 주소 정보를 해석하는 방법을 결정하는 주소 유형을 지정하는 데 사용됩니다. 일반적인 주소 계열에는 AF_INET(IPv4 주소), AF_INET6(IPv6 주소) 및 AF_UNIX(Unix 도메인 주소)가 포함됩니다. 서로 다른 유형의 소켓은 서로 다른 프로토콜을 사용하여 데이터를 전송하므로 주소 정보를 나타내기 위해 서로 다른 주소 구조를 사용해야 합니다.

주소 계열을 나타내기 위해 sockaddr 구조의 공통 필드를 사용함으로써 Linux 커널은 다양한 유형의 소켓 주소를 일관되게 처리할 수 있으므로 소켓 API의 사용을 단순화합니다. 사용하는 실시예 는 소켓에서 속성을 초기화할 때 struct sockaddr_in또는 를 사용하더라도 어떤 종류의 통신 방식이든 네트워크 또는 로컬 통신이든 관계없이 매개변수를 일률적으로 struct sockaddr_un유형 으로 변환하는 것입니다 sockaddr*. 이와 같이 서로 다른 통신 방식에 대해 서로 다른 인터페이스를 별도로 구현할 필요가 없으므로 사용 비용이 절감됩니다.


멀티 스레드 프로그래밍에서 우리는 종종 void*스레드 함수에 정보를 전달하기 위해 (모든 유형의 데이터를 전달할 수 있음) 소켓을 사용하는데 void*통신 관련 속성을 저장하는 데 소켓을 사용하지 않는 이유는 무엇입니까?

소켓 API의 설계는 BSD Unix 운영 체제가 Bell Labs의 연구원에 의해 개발된 1970년대 후반으로 거슬러 올라갑니다. 당시 C언어와 유닉스 운영체제는 모두 초기 단계였고, 현대 프로그래밍 언어와 운영체제의 많은 기능이 아직 등장하지 않은 상태였다.

소켓 API를 설계할 때 연구자들은 다양한 유형의 네트워크 프로토콜을 지원하기 위한 공통 인터페이스를 제공하기를 희망합니다. 이 목표를 달성하기 위해 서로 다른 유형의 네트워크 주소를 나타내는 공통 소켓 주소 구조 집합을 정의했습니다. 이러한 구조에는 주소 패밀리 및 프로토콜 주소와 같은 정보를 저장하기 위한 특정 필드가 포함되어 있습니다.

포인터를 사용하여 void*유사한 기능을 수행할 수 있지만 이렇게 하면 코드가 더 복잡해지고 유지 관리가 어려워집니다. 프로그래머는 메모리를 수동으로 관리해야 하며 포인터가 가리키는 데이터에 액세스하기 위해 캐스트를 사용해야 합니다. 반대로 소켓 주소를 나타내는 특정 구조 유형을 사용하는 것이 더 간단하고 직관적이며 안전합니다.

결과적으로 소켓 API는 포인터를 사용하는 대신 소켓 주소를 나타내는 특정 구조체 유형을 사용하게 되었습니다 void*. 이 디자인 결정은 소켓 API에 대한 명확하고 간결하며 사용하기 쉬운 인터페이스를 제공했으며 나중에 널리 채택되었습니다.

3. 연습

실제로 이러한 인터페이스를 통해 "루틴"에 따라 네트워크 프로그램을 구현할 수 있습니다. 지금까지 프로세스 간 통신 외에 가장 흥미로운 실험이라고 생각합니다.

기사가 아직 완성되지 않았기 때문에 두 가지 권위 있는 사양 예제가 제공됩니다.

간단한 UDP 네트워크 프로그램 구현

간단한 TCP 네트워크 프로그램 구현

Guess you like

Origin blog.csdn.net/m0_63312733/article/details/130441666