《Unix高级环境编程》之套接字接口函数(一)

本文详细介绍了TCP/IP协议的基础知识及网络构成要素,包括网卡、中继器、网桥、路由器、交换机、网关等的作用及区别。深入解析了TCP/IP的分层模型、各层的主要协议及其功能,并重点讲解了套接字接口的应用,涵盖了创建套接字、套接字描述符管理、进程标识、字节序转换等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

套接字

TCP/IP 基础

网络构成要素:

  1. 网卡:连接互联网

  2. 中继器:作为对网络中衰弱的信号进行放大和发送的设备,连接在物理层,以便传输给另一个电缆。并不能判断数据是否有错,也无法改变传输速度。

    • 一般用于同种介质之家你转接工作,有时候也可以用于电缆和光缆之间调整信号。

    • 有的中继器叫做集线器。

  3. 网桥/2层交换机:用于连接一个网络与另外一个网络,属于OSI模型的第二层数据链路层(因此也叫做2层交换机),根据数据帧的内容发送给相邻的其他网络。

    • 例如常用的交换机。(根据交换机连接的不同网络设备的物理地址,发送内容)

    • 根据物理地址(MAC地址)进行处理。

  4. 路由器/3层交换机:在网络层上连接两个网络的设备,并对根据IP地址,分组报文进行转发的设备(即,将报文根据IP地址分类,转发给不同IP)。

    • 根据IP地址进行处理。

    • 可以连接不同的数据链路层。

  5. 4-7层交换机:负责处理传输层到应用层的数据,负载均衡器,带宽控制,广域网加速器,防火墙。

  6. 网关:将传输层到应用层的数据进行转换和转发的设备。进行协议的转换和数据的转发。同一类型协议之间数据转发的交租应用网关,

TCP/IP的分层模型:

  1. 物理层(硬件):负责传输数据的硬件

  2. 数据链路层(网络接口层):利用以太网中的数据链路进行通信。

    • 有时候,网络接口层和物理层成为网络通信层。
  3. 网络层:使用IP协议,基于IP地址转发分包数据。

    • IP:IP地址以主机作为标识,是分组交换的一种协议,但是不具有重发机制,属于非可靠性传输协议。

    • ICMP:IP数据包在发送过程中一旦发生异常导致无法到达目的地址,需要给发送端发送一个异常通知,有时候也用来诊断网络的健康状况。

    • ARP:从分组数据包中解析出物理地址(MAC)的一种协议。

  4. 传输层:传输层主要的功能是让应用程序之间实现通信。十分应用程序的是端口号。

    • TCP:一种面向有链接的传输协议,可以保证通信主机之间通信可达。可以正确处理丢包,乱序的情况,但是为了建立连接至少7次收包发包,浪费网络流量,不适合在视屏会议等场合使用。

    • UDP:一种面向无连接的传输协议。常用于数据量较少,或多播,广播通信。

    • 网络层和传输层通常由操作系统提供

  5. 应用层:对应于OSI模型中的会话层+表示层+应用层

    • WWW:浏览器于服务器之间的协议是HTTP(超文本传输协议),主要的数据格式是HTML。HTTP属于OSI中的应用层协议,HTML属于OSI中的表示层协议。

    • 电子邮件(SMTP,Simple Mail Transfer Protocol)

    • 文本传输(FTP, File Transfer Protocol):使用过程中会建立两个连接:发出传输请求要哦用到的控制连接,实际传输时用到的数据连接。

    • 远程登录(TELNET与SSH):

    • 网络管理协议(SNMP,Simole Network Management Protocol)

套接字接口
  1. 使用 socket 函数创建套接字

    
    #include <sys/socket.h>
    
    int socket(int domain, int type, int protocol);
    • 功能:创建套接字

    • 参数:

      • domain:确定通信的特性,包括地址格式,每个域都有自己的地址格式。POSIX.1指定的域包括:

        • AF_INET :IPv4地址域。

        • AF_INET6 :IPv6地址域。

        • AF_UNIX :UNIX域。

        • AF_UNSPEC :未指定,可以代表任何域。

      • type:确定套接字的类型。POSIX.1定义的套接字类型有:

        • SOCK_SEQPACKET :长度固定、有序、可靠的面向连接报文传递,默认协议SCTP(Stream Control Transfer Protocol)。从套接字接收的数据量和对方发送的一致。

        • SOCK_STREAM :有序、可靠、双向的面向连接字节流,默认协议TCP。应用程序分不清字节流服务报文界限,从套接字读取数据时可能需要多次函数调用。

        • SOCK_DGRAM :长度固定、不可靠的无连接报文传递,默认协议UDP。

        • SOCK_RAW :IP协议的数据报接口,默认协议直接基于IP。套接字提供了一个数据报接口用于直接访问网络层(IP层),使用它时需要自己构造协议首部。创建 SOCK_RAW 套接字需要超级用户特权。

      • protocol:通常为0,表示按给定的域和套接字类型选择默认协议。在 domain 和 type 给定的情况下如果有多个协议,可以用 protocol 指定协议。因特网域套接字定义的协议:

        • IPPROTO_IP: Ipv4网际协议;

        • IPPROTO_IPV6:Ipv6网际协议;

        • IPPROTO_ICMP:因特网控制报文协议(Internet Control Message Protocol);

        • PPROTO_RAW:原始IP数据包协议;

        • IPPROTO_TCP:传输控制协议;

        • IPPROTO_UDP:用户数据报协议;

    • 尽管套接字描述符是文件描述符,担不是所有使用文件描述符的函数都能处理套接字描述符,下面是有关函数的支持情况:

      • close 释放套接字

      • dup dup2 正常复制

      • fcntl 支持一些命令,如 F_DUPFD 、 F_GETFD 、 F_GETFL 、 F_GETOWN 、 F_SETFD 、 F_SETFL、 F_SETOWN

      • fstat 支持一些 stat 结构成员,由实现定义

      • ioctl 支持部分命令,依赖于底层设备驱动

      • poll 正常使用

      • read readv 等价于无标志位的 recv

      • select 正常使用

      • write writev 等价于无标志位的 send

    • 返回值:

      • 成功: 返回描述符

      • 失败:返回-1,设置errno。

  2. 禁止套接字上的输入输出:

    
    #include <sys/socket.h>
    
    int shutdown(int sockfd, int how);
    • 功能:

    • 参数:

      • how:

        • SHUT_RD ,关闭读端,即无法从套接字读取数据。

        • SHUT_WR ,关闭写端,即无法用套接字发送数据。

        • SHUT_RDWR ,关闭读写,同时无法读取和发送数据。

    • 返回值:

      • 成功: 返回0

      • 失败:返回-1,设置errno。

    • close:释放套接字,只有最后一个描述符关闭才会释放网络端点。

  3. 网络间进程标识:计算机网络地址,计算机上用端口表示的服务。

  4. 字节序:就是不同计算机是大端还是小端的问题,造成通信的问题。TCP/IP协议栈规定使用大端字节序通信。

    • 大端:低字节数据存储在高地址

    • 小端:低字节数据存储在低地址

  5. 处理机字节序和网络字节序之间的转换:

    
    #include <arpa/inet.h>
    
    /* 将主机字节序的32位整型数转换为网络字节序 */
    uint32_t htonl(uint32_t hostlong);
    /* 将主机字节序的16位整型数转换为网络字节序 */
    uint16_t htons(uint16_t hostshort);
    /* 将网络字节序的32位整型数转换为主机字节序 */
    uint32_t ntohl(uint32_t netlong);
    /* 将网络字节序的16位整型数转换为主机字节序 */
    uint16_t ntohs(uint16_t netshort);
  6. 地址格式:地址标识特定通信域中的套接字端点,地址格式和特定通信域相关。为兼容不同格式的地址,地址被强制转换为通用的地址结构 sockaddr ,Linux系统中该结构定义如下:

    
    #include <netinet/in.h>
    
    struct sockaddr {
        sa_family_t sa_family;      /* 地址类型 */
        char        sa_data[14];    /* 变长地址 */
    };
    • in_port_t 定义为 uint16_t , in_addr_t 定义为 uint32_t 。
    • AF_INET 域中,套接字地址结构如下:

      struct in_addr {
          in_addr_t       s_addr;         /* IPv4地址32位 */
      };
      struct sockaddr_in {
          sa_family_t     sin_family;     /* 地址类型 */
          in_port_t       sin_port;       /* 端口号 */
          struct in_addr  sin_addr;       /* IPv4地址 */
          unsigned char   sin_zero[8];    /* 0填充 */
      };    
    • AF_INET6 域中,套接字地址结构如下:

      struct in6_addr {
      uint8_t         s6_addr[16];    /* IPv6地址128位 */
      };
      struct sockaddr_in6 {
          sa_family_t     sin6_family;    /* 地址类型 */
          in_port_t       sin6_port;      /* 端口号 */
          uint32_t        sin6_flowinfo;  /* 传输类和流信息 */
          struct in6_addr sin6_addr;      /* IPv6地址 */
          uint32_t        sin6_scope_id;  /* 作用域的接口集 */
      };
  7. 地址二进制和十进制的转换:可以用 inet_ntop 和 inet_pton 函数对IPv4和IPv6地址作二进制和点分十进制字符串之间的转换。类似的还有 inet_addr 和 inet_ntoa 等函数,但它们只能用于IPv4地址。

    
    #include <arpa/inet.h>
    
    const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
    • 功能:将af域中的网络字节序的二进制地址转换成为字符串,存在dst指向的缓冲区

    + 参数:

    + `af`:只支持`AF_INET`和`AF_INET6`;
    
    + `size`:指定dst的缓冲区的大小,可用 INET_ADDRSTRLEN 和 INET6_ADDRSTRLEN 来为IPv4和IPv6地址的字符串设置足够大的空间。
    
    • 返回值:

      • 成功:返回指向dst的指针;

      • 失败:返回空指针,设置errno;

        
        #include <arpa/inet.h>
        
        int inet_pton(int af, const char *src, void *dst);
    • 功能:将af域中的字符串地址转换成为网络字节序的二进制地址,存在dst指向的缓冲区

    + 参数:

    + `af`:只支持`AF_INET`和`AF_INET6`;
    
    • 返回值:

      • 成功:返回1;

      • 失败:返回0,则表示src中的字符串不是有效的网络地址;返回-1,则表示af不是有效的address family,并设置errno;

  8. 地址查询:找到给定计算机的主机信息:

    • 涉及的文件:/etc/hosts host 数据文件

      
      #include <netdb.h>
      
      extern int h_errno;
      struct hostent *gethostent(void);/*system V / POSIX 扩展*/
    • 获取主机结构体信息

    • 结构体:

      struct hostent {
          char  *h_name;            /* 主机名 */
          char **h_aliases;         /* 主机别名列表 */
          int    h_addrtype;        /* 主机地址类型 */
          int    h_length;          /* 地址长度 */
          char **h_addr_list;       /* 地址列表 */
      };
    • 返回值:

      • 成功返回结构体指针;

      • 失败返回空指针,错误编号存在h_errno

        
        #include <netdb.h>
        
        extern int h_errno;
        void sethostent(int stayopen);
    • 功能:

    • stayopen

      • 0:

      • 非0值:在调用gethostent之后文件保持打开。

        
        #include <netdb.h>
        
        extern int h_errno;
        void endhostent(void);
    • 功能:关闭文件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值