• 【Linux网络编程】基础API


    总结《Linux高性能服务器编程》第5章

    第五章 Linux网络编程基础API

    socket地址API

    • socket最开始的含义是一个IP地址和端口对(ip,port),它唯一地表示了使用TCP通信的一端;

    • 主机字节序和网络字节序
      • 字节序分为大端字节序/网络字节序(big endian)和小端字节序/主机字节序(little endian)

      • 大端字节序是指一个整数的高位字节(23~31 bit)存储在内存的低地址处,低位字节(0
        ~7 bit)存储在内存的高地址处;

      • Linux提供了如下4个函数来完成主机字节序和网络字节序之间的转换

        #include<netinet/in.h>
        unsigned long int htonl(unsigned long int hostlong);
        unsigned short int htons(unsigned short int hostshort);
        unsigned long int ntohl(unsigned long int netlong);
        unsigned short int ntohs(unsigned short int netshort);
        
        • 1
        • 2
        • 3
        • 4
        • 5
    • 通用socket地址
      • socket网络编程接口中表示socket地址的是结构体sockaddr以及sockaddr_storage;

        #include<bits/socket.h>
        struct sockaddr
        {
        sa_family_t sa_family; //地址族类型(sa_family_t)的变量,与协议族类型对应
        char sa_data[14]; //存放socket地址值
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6

        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eHVGWxFa-1668427347952)(img/Linux高性能服务器编程/image-20221025085429539.png)]

    • 专用socket地址
      • UNIX本地域协议族:结构体sockaddr_un;
      • TCP/IP协议族:结构体sockaddr_in和sockaddr_in6;
      • 所有专用socket地址(以及sockaddr_storage)类型的变量在实际使用时都需要转化为通用socket地址类型sockaddr;
    • IP地址转换函数
      • 将用点分十进制字符串表示的IPv4地址转化为用网络字节序整数表示的IPv4地址

        in_addr_t inet_addr(const char*strptr);
        int inet_aton(const char*cp, struct in_addr*inp);
        int inet_pton(int af,const char*src, void*dst);
        
        • 1
        • 2
        • 3
      • 将用网络字节序整数表示的IPv4地址转化为用点分十进制字符串表示的IPv4地址

        char*inet_ntoa(struct in_addr in);
        const char*inet_ntop(int af, const void*src, char*dst, socklen_t cnt);
        
        • 1
        • 2

    创建socket

    • Linux中,所有东西都是文件,socket也是可读、可写、可控制、可关闭的文件描述符

    • 使用socket系统调用可创建一个socket

      #include<sys/types.h>
      #include<sys/socket.h>
      int socket(int domain,int type,int protocol);
      
      • 1
      • 2
      • 3
      • domain参数告诉系统使用哪个底层协议族:PF_INET、PF_INET6、PF_UNIX;
      • type参数指定服务类型:SOCK_STREAM、SOCK_UGRAM;
      • protocol参数是在前两个参数构成的协议集合下,再选择一个具体的协议,默认设置为0;

    命名socket

    • 将一个socket与socket地址绑定称为给socket命名,系统调用是bind

      #include<sys/types.h>
      #include<sys/socket.h>
      int bind(int sockfd, const struct sockaddr*my_addr, socklen_t addrlen);
      
      • 1
      • 2
      • 3
      • bind将my_addr所指的socket地址分配给未命名的sockfd文件描述符;
      • 绑定时两种常见错误:
        • EACCES,被绑定的地址是受保护的地址,仅超级用户能够访问;
        • EADDRINUSE,被绑定的地址正在使用中;

    监听socket

    • 服务器通过listen调用来被动接受连接;

    • 使用listen系统调用创建监听队列以存放待处理的客户连接

      #include<sys/socket.h>
      int listen(int sockfd,int backlog);
      
      • 1
      • 2
      • sockfd参数指定被监听的socket,backlog参数提示内核监听队列的最大长度,监听队列的长度如果超过backlog,服务器将不受理新的客户连接;

    接受连接

    • accept系统调用从listen监听队列中接受一个连接

      #include<sys/types.h>
      #include<sys/socket.h>
      int accept(int sockfd, struct sockaddr*addr, socklen_t*addrlen);
      
      • 1
      • 2
      • 3

    发起连接

    • 客户端需要通过connect系统调主动与服务器建立连接;

      #include<sys/types.h>
      #include<sys/socket.h>
      int connect(int sockfd, const struct sockaddr* serv_addr, socklen_t addrlen);
      
      • 1
      • 2
      • 3
      • 连接时两种常见错误:
        • ECONNREFUSED,目标端口不存在,连接被拒绝;
        • ETIMEDOUT,连接超时;

    关闭连接

    • 关闭该连接对应的socket;

    • 通过关闭普通文件描述符的系统调用来完成;

      #include<unistd.h>
      int close(int fd);
      
      • 1
      • 2
      • fd:file describeter,文件描述符
      • 每次将fd的引用计数减1,只有当fd的引用计数为0时,才真正关闭连接;
    • 无论如何都要立即终止连接,使用shutdown系统调用

      #include<sys/socket.h>
      int shutdown(int sockfd,int howto);
      
      • 1
      • 2

    数据读写

    • TCP数据读写
      • 对文件的读写操作read和write同样适用于socket;

      • socket编程接口提供了专用于socket数据读写的系统调用:

        #include<sys/types.h>
        #include<sys/socket.h>
        ssize_t recv(int sockfd,void*buf,size_t len,int flags);
        ssize_t send(int sockfd,const void*buf,size_t len,int flags);
        
        • 1
        • 2
        • 3
        • 4
    • UDP数据读写
      • socket编程接口中用于UDP数据报读写的系统调用

        #include<sys/types.h>
        #include<sys/socket.h>
        ssize_t recvfrom(int sockfd, void*buf, size_t len, int flags, struct sockaddr* src_addr, socklen_t* addrlen);
        ssize_t sendto(int sockfd,const void*buf, size_t len, int flags, const struct sockaddr* dest_addr, socklen_t addrlen);
        
        • 1
        • 2
        • 3
        • 4
    • 通用数据读写函数
      • TCP流数据/UDP数据报均可以使用

        #include<sys/socket.h>
        ssize_t recvmsg(int sockfd,struct msghdr*msg,int flags);
        ssize_t sendmsg(int sockfd,struct msghdr*msg,int flags);
        
        //msg参数是msghdr结构体类型的指针
        
        • 1
        • 2
        • 3
        • 4
        • 5

    带外标记

    • 内核通知应用程序带外数据到达的两种常见方式:I/O复用产生的异常事件和SIGURG信号;

    • 使用sockatmark系统调用判断sockfd是否处于带外标记,即下一个被读取到的数据是否是带外数据

      #include<sys/socket.h>
      int sockatmark(int sockfd);
      
      • 1
      • 2

    地址信息函数

    • 获取一个连接socket的本端以及远端的socket地址

      #include<sys/socket.h>
      int getsockname(int sockfd, struct sockaddr*address, socklen_t* address_len);
      int getpeername(int sockfd, struct sockaddr*address, socklen_t* address_len);
      
      • 1
      • 2
      • 3

    socket选项

    • 读取和设置socket文件描述符属性的方法

      #include<sys/socket.h>
      int getsockopt(int sockfd,int level,int option_name, void* option_value, socklen_t* restrict option_len);
      int setsockopt(int sockfd,int level, int option_name, const void* option_value, socklen_t option_len);
      
      • 1
      • 2
      • 3
      • 常用socket选项

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F1QJ2auL-1668427347953)(img/Linux高性能服务器编程/image-20221025093741649.png)]

    网络信息API

    • socket地址的两个要素,即IP地址和端口号,都是用数值表示的,不便于记忆和扩展;

    • 可用使用网络信息API实现主机名到IP地址的转换等功能;

    • 获取主机的完整信息
      #include<netdb.h>
      struct hostent* gethostbyname(const char*name);
      struct hostent* gethostbyaddr(const void*addr, size_t len,int type);
      
      • 1
      • 2
      • 3
      • gethostbyname:根据主机名称获取主机的完整信息;
      • gethostbyaddr:根据IP地址获取主机的完整信息;
    • 获取主机的完整信息
      #include<netdb.h>
      struct servent* getservbyname(const char*name, const char*proto);
      struct servent* getservbyport(int port, const char*proto);
      
      • 1
      • 2
      • 3
      • getservbyname:根据名称获取某个服务的完整信息;
      • getservbyport:根据端口号获取某个服务的完整信息;
    • getaddrinfo
      #include<netdb.h>
      int getaddrinfo(const char*hostname, const char*service,const
      struct addrinfo* hints, struct addrinfo** result);
      
      • 1
      • 2
      • 3
      • 既能通过主机名获得IP地址,也能通过服务器名获得端口号;
    • getnameinfo
      #include<netdb.h>
      int getnameinfo(const struct sockaddr*sockaddr,socklen_t addrlen, char*host, socklen_t hostlen, char*serv, socklen_t servlen, int flags);
      
      • 1
      • 2
      • 通过socket地址同时获得以字符串表示的主机名和服务器名;
  • 相关阅读:
    【BRCM】博通ESDK6.5中添加50210S光口驱动及配置光电自适应的实例
    使用Aggregated APIServer扩展你的kubernetes API
    C. Complementary XOR CodeTON Round 3 (Div. 1 + Div. 2, Rated, Prizes)
    Sitecore XP 10.3(latest) Docker一键部署
    求解最大公约数(两种)
    腾讯云服务器简介_CVM优势_常见问题解答
    Salesforce撤离中国后,谁来缓解在华跨国企业的焦虑?
    用另一种方式解决机房管理助手!(非结束进程版)
    数据在内存中的存储——深度解析
    离线数仓 (四) --------- 用户行为数据采集模块
  • 原文地址:https://blog.csdn.net/weixin_42659457/article/details/127854367