C++ POSIX API超详细分析

目录
  • 1.网络通信
  • 2.posix API
  • 3.POSIX网络API
  • 4.函数内部过程解析
    • 4.1 socket套接字创建
    • 4.2 bind 绑定端口
    • 4.3 网络字节序和主机字节序
    • 4.4 listen监听fd
    • 4.5 connect发起连接请求
    • 4.6 accept()接收请求建立连接
    • 4.7 消息的发送和接收
    • 4.8 粘包问题
    • 4.9 close

1.网络通信

1.消息传递(管道、FIFO、消息队列)

2.同步(互斥量、条件变量、读写锁、文件和写记录锁、信号量)

3.共享内存(匿名的和具名的)

使用TCP/IP协议 通过socket完成

2.posix API

目的:实现不同系统上的源代码的可移植性。

举例:linux和windows都要实现基本的posix标准,linux把fork函数封装成posix_fork(随便说的),windows把creatprocess函数也封装成posix_fork,都声明在unistd.h里。这样,程序员编写普通应用时候,只用包含unistd.h,调用

3.POSIX网络API

4.函数内部过程解析

4.1 socket套接字创建

int socket(int domain, int type, int protocol);
//参数分别是地址族、 套接字类型和协议
//AF_INET  SOCK_STREAM 可默认为0
//IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等

4.2 bind 绑定端口

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

sockfd:即socket描述字

addr:一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。

struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 0.0.0.0 监听时,监听所有的地址
server_addr.sin_port = htons(port);
struct sockaddr_in {
    sa_family_t    sin_family; /* address family: AF_INET  协议族*/
    in_port_t      sin_port;   /* port in network byte order  端口号*/
    struct in_addr sin_addr;   /* internet address  IP地址*/
};
/* Internet address. */
struct in_addr {
    uint32_t       s_addr;     /* address in network byte order */
};

通常服务器在启动的时候都会绑定一个众所周知的地址(如ip地址+端口号),用于提供服务,客户就可以通过它来接连服务器;而客户端就不用指定,有系统自动分配一个端口号和自身的ip地址组合。这就是为什么通常服务器端在listen之前会调用bind(),而客户端就不会调用,而是在connect()时由系统随机生成一个。但是我认为客户端也可以绑定。

4.3 网络字节序和主机字节序

小端:小字节放前面,大字节放后面
大端:大字节放前面,小字节放后面
转换端口
uint16_t htons(uint16_t hostshort); //主机字节序->网络字节序
uint16_t ntohs(uint16_t netshort); //网络字节序->主机字节序
转IP
 htonl(uint32_t hostlong);//主机字节序->网络字节序
 ntohl(uint32_t netlong);//网络字节序->主机字节序

4.4 listen监听fd

int listen(fd,size) // 无错返回0,错误返回-1

服务端调用listen()后,开始监听网络上发送给socket的连接请求。

也就是说,开始接收请求了。

4.5 connect发起连接请求

int connect(int sockfd,
const struct sockaddr *serv_addr,
int socklen_t addrlen);

4.6 accept()接收请求建立连接

accept()函数只做两件事,将连接请求从全连接队列中取出,给该连接分配一个fd并返回。

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

4.7 消息的发送和接收

read()/write()

recv()/send()

readv()/writev()

recvmsg()/sendmsg()

recvfrom()/sendto()

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count); // 目的fd 消息 消息长度
ssize_t write(int fd, const void *buf, size_t count);
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
              const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                 struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

4.8 粘包问题

2个数据包同时被提出,但是由于数据是在一起的,没有办法分离

解决方法:

  • 在包头添加一个数据包长度的字段,标明长度来确定数据包
  • 在包结束后添加分割符,这里注意分割符要选择不经常使用的

注意: 1优于2,因为,添加分割符,需要遍历整个消息来找到分隔符,这样大大影响效率,但是2可以结合1使用。

4.9 close

#include <unistd.h>
int close(int fd);

close操作只是使相应socket描述字的引用计数-1,只有当引用计数为0的时候,才会触发TCP客户端向服务器发送终止连接请求。

过程分析

1.正常情况下一方调用close情况如下图:

2.当双方同时调用close,如下图:

当同时发送close时,两边同时发送fin 和ack 这时候调用time_wait等待消息发送完毕。

Fin_wait_1作用?

等待对方回复,超时自动重发fin。

Fin_wait_2作用?

等待对方业务逻辑处理后,发送fin包。这里有可能出现死等待的情况服务器如果出现大量的Fin_wait_2可能需要考虑是不是没有close,或者close之前做了耗时操作。time_wait 作用?

防止最后一个ACK没有顺利到达对方,超时重新发送ack。time_wait时常一般是120s可以修改。

服务器掉线重启出现端口被占用怎么办?

其实主要是由于还处于time_wait状态,端口并没有真正释放。这时候可以设置SO_REUSEADDR属性,保证掉线能马上重连。

到此这篇关于C++ POSIXAPI超详细分析的文章就介绍到这了,更多相关C++ POSIXAPI内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • VS Code C/C++环境配置教程(无法打开源文件“xxxxxx.h” 或者 检测到 #include 错误,请更新includePath) (POSIX API)

    一.问题描述与分析 编辑C/C++程序,我推荐使用C/C++,VS Code相对于别的编译器来说有很多的优势.但是如果第一次使用的话,会觉得其不好用.因为如果不配置好的话,操作会比较麻烦. 注意:我这里是在windows下编写Linux程序. 例如在使用VS Code编辑C/C++程序在没有配置好的情况下,会出现如下图情况, 出现这种情况的原因是 在VS Code没有找到头文件.或者是VS Code没有配置好. 为了很好的解释上述的问题,请先了解下Cygwin.MinGW.POSIX等,并了解下

  • C++ POSIX API超详细分析

    目录 1.网络通信 2.posix API 3.POSIX网络API 4.函数内部过程解析 4.1 socket套接字创建 4.2 bind 绑定端口 4.3 网络字节序和主机字节序 4.4 listen监听fd 4.5 connect发起连接请求 4.6 accept()接收请求建立连接 4.7 消息的发送和接收 4.8 粘包问题 4.9 close 1.网络通信 1.消息传递(管道.FIFO.消息队列) 2.同步(互斥量.条件变量.读写锁.文件和写记录锁.信号量) 3.共享内存(匿名的和具名

  • Java超详细分析泛型与通配符

    目录 1.泛型 1.1泛型的用法 1.1.1泛型的概念 1.1.2泛型类 1.1.3类型推导 1.2裸类型 1.3擦除机制 1.3.1关于泛型数组 1.3.2泛型的编译与擦除 1.4泛型的上界 1.4.1泛型的上界 1.4.2特殊的泛型上界 1.4.3泛型方法 1.4.4类型推导 2.通配符 2.1通配符的概念 2.2通配符的上界 2.3通配符的下界 题外话: 泛型与通配符是Java语法中比较难懂的两个语法,学习泛型和通配符的主要目的是能够看懂源码,实际使用的不多. 1.泛型 1.1泛型的用法

  • Redis超详细分析分布式锁

    目录 分布式锁 应用场景 使用Redis 实现分布式锁 单机版Redis实现分布式锁 使用原生Jedis实现 使用Springboot实现 分布式锁 为了保证一个方法在高并发情况下的同一时间只能被同一个线程执行,在传统单体应用单机部署的情况下,可以使用Java并发处理相关的API(如ReentrantLcok或synchronized)进行互斥控制.但是,随着业务发展的需要,原单体单机部署的系统被演化成分布式系统后,由于分布式系统多线程.多进程并且分布在不同机器上,这将使原单机部署情况下的并发控

  • 非常适合新手学生的Java线程池超详细分析

    目录 线程池的好处 创建线程池的五种方式 缓存线程池CachedThreadPool 固定容量线程池FixedThreadPool 单个线程池SingleThreadExecutor 定时任务线程池ScheduledThreadPool ThreadPoolExecutor创建线程池(十分推荐) ThreadPoolExecutor的七个参数详解 workQueue handler 如何触发拒绝策略和线程池扩容? 线程池的好处 可以实现线程的复用,避免重新创建线程和销毁线程.创建线程和销毁线程对

  • C++超详细分析红黑树

    目录 红黑树 红黑树的概念 红黑树的性质 红黑树结点的定义 红黑树的插入操作 情况一 情况二 情况三 红黑树的验证 用红黑树封装map.set 红黑树的迭代器 封装map 封装set 红黑树 红黑树的概念 红黑树的概念 红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black. 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的. 红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是

  • C++ 超详细分析数据结构中的时间复杂度

    别别着急划走哈,如果你跟我一样是大学生,那么你发现了一个宝藏!我们往后看--> 要想了解时间复杂度和空间复杂度,我们得知道什么是时间复杂度和空间复杂度! 有的人看到这就明白了,而有的人却去追求它的内涵: 见名知意嘛,时间复杂度不就是表示一个算法运行完所需要的时间?这还用问?错错错! 我来举一个很简单的例子:你家隔壁老王买了一台 i9 12900k 和 RTX3080Ti 整个64GB的内存,你眼瞅着你 4G的内存,洋垃圾的处理器,打开个PS都要冒烟的那种,来来来,你跟我说说能比吗? 所以简单来说

  • Java数据结构超详细分析二叉搜索树

    目录 1.搜索树的概念 2.二叉搜索树的简单实现 2.1查找 2.2插入 2.3删除 2.4修改 3.二叉搜索树的性能 1.搜索树的概念 二叉搜索树是一种特殊的二叉树,又称二叉查找树,二叉排序树,它有几个特点: 如果左子树存在,则左子树每个结点的值均小于根结点的值. 如果右子树存在,则右子树每个结点的值均大于根结点的值. 中序遍历二叉搜索树,得到的序列是依次递增的. 二叉搜索树的左右子树均为二叉搜索树. 二叉搜索树的结点的值不能发生重复. 2.二叉搜索树的简单实现 我们来简单实现以下搜索树,就不

  • Java 栈与队列超详细分析讲解

    目录 一.栈(Stack) 1.什么是栈? 2.栈的常见方法 3.自己实现一个栈(底层用一个数组实现) 二.队列(Queue) 1.什么是队列? 2.队列的常见方法 3.队列的实现(单链表实现) 4.循环队列 一.栈(Stack) 1.什么是栈? 栈其实就是一种数据结构 - 先进后出(先入栈的数据后出来,最先入栈的数据会被压入栈底) 什么是java虚拟机栈? java虚拟机栈只是JVM当中的一块内存,该内存一般用来存放 例如:局部变量当调用函数时,我们会为函数开辟一块内存,叫做 栈帧,在 jav

  • C++ 超详细分析多态的原理与实现

    目录 多态的定义及实现 多态的构成条件 虚函数重写 C++11的override和final 抽象类 多态的原理 虚函数表 动态绑定与静态绑定 单继承和多继承关系的虚函数表 单继承中的虚函数表 多继承中的虚函数表 常见问题 多态的定义及实现 多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态. 比如买票这个行为,当普通人买票时,是全价买票:学生买票时,是半价买票:军人买票时是优先买票. 多态的构成条件 多态是在不同继承关系的类对象,去调用同一函数

  • C++ 超详细分析数据结构中的时间复杂度

    目录 什么是时间复杂度和空间复杂度 如何计算时间复杂度和空间复杂度 如何计算时间复杂度和空间复杂度 别别着急划走哈,如果你跟我一样是大学生,那么你发现了一个宝藏!我们往后看--> 什么是时间复杂度和空间复杂度 要想了解时间复杂度和空间复杂度,我们得知道什么是时间复杂度和空间复杂度! 有的人看到这就明白了,而有的人却去追求它的内涵: 见名知意嘛,时间复杂度不就是表示一个算法运行完所需要的时间?这还用问?错错错! 我来举一个很简单的例子:你家隔壁老王买了一台 i9 12900k 和 RTX3080T

随机推荐