【Linux】网络编程套接字

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

1 预备知识

1.1 IP地址

  • IP协议有两个版本分别是IPv4和IPv6。没有特殊说明默认都是IPv4
  • 对于IPv4IP地址是一个四个字节32为的整数对于IPv6来说IP地址是128位的整数
  • 我们通常也使用 “点分十进制” 的字符串表示IP地址例如 180.101.50.172用点分割的每一个数字表示一个字节范围是 [0, 255] 。

  • 公网IP通常用来唯一地表示互联网中唯一的主机。

  • 在IP数据包头部中, 有两个IP地址, 分别叫做源IP地址, 和目的IP地址。

  • 源 IP 和目的 IP对一个报文来讲回答了从哪里来到哪里去的问题最大的意义是指导一个报文该如何进行路径选择。

1.2 端口号

  • 端口号port是传输层协议的内容。它是一个 2 字节 16 位的整数用来唯一标识一台主机上的一个进程。

 

进程具有独立性进程间通信的前提工作先得让不同的进程看到同一份资源这份资源在这里就是网络

源端口号和目的端口号描述数据是哪个进程发的要发给哪个进程。

一个进程可以关联多个端口号但是一个端口号不可以关联多个进程这个可以由端口号的概念得出。

1.3 TCP协议和UDP协议

我们需要先对 TCP 协议和 UDP 协议有一个直观的认识后面再详细讨论。

  • TCPTransmission Control Protocol传输控制协议
    • 传输层协议。
    • 有连接。
    • 可靠传输。
    • 面向字节流
  • UDPUser Datagram Protocol用户数据报协议
    • 传输层协议。
    • 无连接。
    • 不可靠传输。
    • 面向数据报

说明

  • TCP 的可靠和 UDP 的不可靠都是中性词客观的没有谁好谁不好只有谁更合适。
  • 字节流以字节为单位进行数据获取可以根据需求进行数据量的获取得到的数据需要自己解析才能得到相应的数据报。
  • 数据报以数据报为单位进行获取的要么无法获取要么获取到的数据就是一个完整的数据报不需要上层的解析。

1.4 网络字节序

我们已经知道内存中的多字节数据相对于内存地址有大端和小端之分磁盘文件中的多字节数据相对于文件中的偏移地址也有大端和小端之分。网络数据流同样有大端和小端之分那么为了避免网络通信中不同主机大小端不一致的问题应如何定义网络数据流的地址呢

  1. 发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出。
  2. 接收主机把从网络上接收到的字节依次保存在接收缓冲区中也是按内存地址从低到高的顺序保存。
  3. 因此网络数据流的地址应该这样规定先发出的数据是低地址后发出的数据是高地址。
  4. TCP/IP 协议规定网络数据流应采用大端字节序即低地址高字节。
  5. 不管这台主机是大端机还是小端机都会按照这个 TCP/IP 规定的网络字节序来发送/接收数据。如果当前发送主机是小端机就需要先将数据转成大端否则就忽略直接发送即可。
     

 

为了使网络程序具有可移植性使同样的 C 代码在大端和小端计算机上编译后都能正常运行可以调用以下库函数做网络字节序和主机字节序的转换

注这些函数名很好记h 表示 host n 表示 network l 表示 32 位长整数s 表示 16 位短整数。

举个例子htonl函数表示将 32 位的长整数从主机字节序转换为网络字节序。

2 socket 编程接口

2.0  socket 常见 API

网络通信的标准方式有很多种比如基于 IP 的网络通信它对应的通信协议家族是 AF_INET网络套接字还有原始套接字、域间套接字。有很多种类的套接字其实就是编程接口。这几种编程接口都是各自不同的体系于是就会有不同套的编程接口这样就会很麻烦因此干脆把不同套的编程接口统一为同一套编程接口也就是下面的这一套。换言之要使用不同种类的通信方式只需要改变传入的参数即可。

// 创建 socket 文件描述符 (客户端 + 服务器, TCP/UDP)
int socket(int domain, int type, int protocol);

// 绑定端口号 (服务器, TCP/UDP)
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

// 设置socket文件状态为监听状态 (服务器, TCP)
int listen(int sockfd, int backlog);

// 接受连接 (服务器, TCP)
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

// 发起连接 (客户端, TCP)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

 于是为了支持不同种类的通信方式struct sockaddr的结构就被设计出来了它是一种通用结构。

在这里插入图片描述 

 struct sockaddr_in是用来网络的之间的进程通信struct sockaddr_un是用来本地的进程间通信的。

socket API 的参数都用struct sockaddr *类型表示在使用时传入各种类型的struct sockaddr指针强转成struct sockaddr *即可。

这样API 内部只要取得某种struct sockaddr的首地址不需要知道具体类型就可以根据地址类型字段确定结构体中的内容。这就相当于面向对象中的多态特性

 

2.1 socket 系统调用

socket的作用为网络通信创建一个 socket 文件

socket的参数
 ① domain指定协议家族。我们选择 AF_INET 。
 ② type指定套接字类型。对于 TCP SOCK_STREAM 应选择 对于 UDP 应选择 SOCK_DGRAM 。
 ③ protocol指定协议类型。在 TCP 和 UDP 中我们设为 0 即可。

socket的返回值
 ① 成功返回一个 socket 文件描述符。
 ② 错误返回 -1 。

2.2 bind 系统调用

bind的作用将本地地址和一个 socket 文件进行绑定。

bind的参数
 ① sockfd传入 socket 文件描述符。
 ② addr用于指定本端的 socket 信息。
 ③ addrlen用于指定本端的 socket 信息的大小。

bind的返回值
 ① 成功返回 0 。
 ② 错误返回 -1 。
 

 

2.3 recvfrom 系统调用 

recvfrom的作用从一个 socket 文件接收数据。

recvfrom的参数
 ① sockfd传入 socket 文件描述符。
 ② buf用于存放读到的数据的用户层缓冲区。
 ③ len用户层缓冲区的大小。
 ④ flags读的方式。我们这里默认设为 0 即可。
 ⑤ src_addr输入输出型参数用于获取对端的 socket 信息。
 ⑥ addrlen输入输出型参数用于获取对端的 socket 信息的大小。

recvfrom的返回值
 ① 成功返回接收的字节数当对端连接关闭时返回 0。
 ② 错误返回 -1 。

2.4 sendto 系统调用 

sendto的作用从一个 socket 文件发送数据。

sendto的参数
 ① sockfd传入 socket 文件描述符。
 ② buf用于发送数据的用户层缓冲区。
 ③ len发送数据的长度。
 ④ flags发送的方式。我们这里默认设为 0 即可。
 ⑤ dest_addr目标对端的 socket 信息。
 ⑥ addrlen目标对端的 socket 信息的大小。

sendto的返回值
 ① 成功返回发送的字节数。
 ② 错误返回 -1 。

 2.5 listen 系统调用

 

2.6 accept 系统调用 

 

2.7 connect 系统调用

 

 

 

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: linux