TCP套接字编写,多进程多线程版本 Linux网络通信( 三 )

  • IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位地址类型, 16位端口号和32位IP地址;而IPv6地址用sockaddr_in6结构体来表示
  • IPv4、 IPv6地址类型分别定义为常数AF_INET、 AF_INET6 。这样,只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容
  • socket API可以都用struct sockaddr *类型表示,在使用的时候需要强制转化成sockaddr;这样的好处是程序的通用性,可以接收IPv4,IPv6,以及UNIX Domain Socket各种类型的sockaddr结构体指针为参数
  • 注意:IPv4和IPv6分别有自己对应的结构体,但是为了统一,我们不知道用户要传的是ipv4还是ipv6,所以就类似于我们不知道用户要输入char还是int类型,此时我们就会写成void *类型;同理,为了统一,这里有个通用的套接字结构体struct sockaddr,将结构体IPv4和IPv6转化成sockaddr类型就可以了,struct sockaddr会根据ipv4和ipv6结构体的前几位判断需要传输的协议类型是IPv4还是IPv6 。
    sockaddr_in的结构: 因为我们主要用到网络通信,所以这里主要介绍这个结构体,打开/usr/include/linux/in.h
    TCP套接字编写,多进程多线程版本 Linux网络通信

    文章插图
    sin_family代表的是地址类型,我们主要用的是AF_INETsin_port代表的是端口号,sin_addr代表的是网络地址,也就是IP地址,用了一个结构体struct in_addr进行描述
    struct in_addr{ _be32 a_addr;}这里填充的就是IPv4的地址,一个32位的整数
    地址转换函数IP地址可以用点分十进制的字符串(例如127.0.0.1),这里涉及到字符串和32位整网络的大端数据之间的相互转换 。下面价绍二者之间转化的库函数:
    int inet_pton(int af, const char *src, void *dst);功能: 将点分十进制字符串转换成32位网络大端的数据参数: af:AF_INET IPV4AF_INET6 TPV6 src:点分十进制串的首地址 dst:32位网络数据的地址返回值:成功返回1,失败返回-1const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);功能: 将32位大端的网络数据转化成点分十进制字符串参数: af:AF_INET IPV4AF_INET6 TPV6 src:32位大端的网络数据地址 dst:存储点分十进制串地址 size:存储点分进制串数组的大小返回值:成功则返回指向数组的指针,出错返回NULL注意:net_ntop函数的dst参数不可以是一个空指针 。调用者必须为目标存储单元分配内存并指定其大小,调用成功时,这个指针就是该函数的返回值char *inet_ntoa(struct in_addr in);参数: in_addr:描述ip地址的结构体注意: inet_ntoa这个函数内部会申请一块空间,保存转换后的IP的结果,这块空间被放在静态存储区,不需要我们手动释放 。且第二次调用该函数,会把结果放到上一次的静态存储区中,所以会覆盖上一次调用该函数的结果,是线程不安全的 。inet_ntop这个函数是由调用者自己提供一个缓冲区保存结果,是线程安全的 。
    TCP通信的基本流程服务端:
    1. 调用 socket 函数创建 socket(侦听socket)2. 调用 bind 函数 将 socket绑定到某个ip和端口的二元组上3. 调用 listen 函数 开启侦听4. 当有客户端请求连接上来后,调用 accept 函数接受连接,产生一个新的 socket(客户端 socket)5. 基于新产生的 socket 调用 send 或 recv 函数开始与客户端进行数据交流6. 通信结束后,调用 close 函数关闭侦听 socket
    TCP套接字编写,多进程多线程版本 Linux网络通信

    文章插图
    看上图:给大家讲解一下服务端的流程
    1.首先服务端会调用socket函数创建一个套接字,上面说过了套接字是一个特殊的”网络文件“,存在读写缓冲区

    经验总结扩展阅读