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


网络数据流的地址应该这样规定:先发出的数据是低地址,后发出的数据是高地址 。

  • 大端字节序: 高位存放在低地址,低位存放在高地址
  • 小端字节序: 低位存放在低地址,高位存放在高地址

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

文章插图
如果双方主机的数据在内存存储的字节序不同,就会造成接收方收到的数据出现偏差,所以为了解决这个问题,又有了下面的规定:
  • TCP/IP协议规定,网络数据流采用大端字节序,不管这台主机是大端机还是小端机, 都会按照这个TCP/IP规定的网络字节序来发送/接收数据
  • 所以如果发送的主机是小端机,就需要把要发送的数据先转为大端,再进行发送,如果是大端,就可以直接进行发送 。
为了方便我们进行网络程序的代码编写,有下面几个API提供给我们用来做网络字节序和主机字节序的转换 , 如下:
#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代表的是host,n代表的是network,s代表的是16位的短整型 , l代表的是32位长整形
  • 如果主机是小端字节序,函数会对参数进行处理,进行大小端转换
  • 如果主机是大端字节序,函数不会对这些参数处理,直接返回
注意:在编程中我们需要自行进行大小端转化的就只有三个:ip地址,传输数据和端口,这两个数据需要我们进行大端的转化,其他的在计算机组包的时候会自动给我们转化 。
Socket常见的API常用的有以下几个,后面会具体的介绍
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)int socket(int domain, int type, int protocol);// 绑定端口号 (TCP/UDP, 服务器)int bind(int socket, const struct sockaddr *address,socklen_t address_len);// 开始监听socket (TCP, 服务器)int listen(int socket, int backlog);// 接收请求 (TCP, 服务器)int accept(int socket, struct sockaddr* address,socklen_t* address_len);// 建立连接 (TCP, 客户端)int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);Sockaddr结构体
TCP套接字编写,多进程多线程版本 Linux网络通信

文章插图
  • sockaddr_in用来进行网络通信,sockaddr_un结构体用来进行本地通信
  • sockaddr_in结构体存储了协议家族 , 端口号,IP等信息,网络通信时可以通过这个结构体把自己的信息发送给对方 , 也可以通过这个结构体获取远端的这些信息
  • 可以看出,这三个结构体的前16位时一样的,代表的是协议家族,可以根据这个参数判断需要进行哪种通信(本地和跨网络)
  • 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_INET , sin_port代表的是端口号,sin_addr代表的是网络地址 , 也就是IP地址,用了一个结构体struct in_addr进行描述
struct in_addr{ _be32 a_addr;}这里填充的就是IPv4的地址 , 一个32位的整数
地址转换函数IP地址可以用点分十进制的字符串(例如127.0.0.1),这里涉及到字符串和32位整网络的大端数据之间的相互转换 。下面价绍二者之间转化的库函数:

推荐阅读