升级!!!运用select实现一个简单的TCP通信!

2023-05-16

升级!!!运用select实现一个简单的TCP通信

  • 多路转接模型
  • select模型
    • select模型的操作流程简介
  • TCP的实现
    • 封装一个TCP服务端
    • 封装一个select类
    • main程序

多路转接模型

多路转接IO:对大量的描述符进行就绪事件监控–让进程能够仅仅对就绪的描述符执行操作 不仅仅提高效率,而且避免阻塞。 只要是存在对描述符进行监控的需求,都可以使用多路转接模型进行事件的监控

多路转接模型使用场景:只要对描述符有(可读,可写,异常)事件的监控需求都可以使用多路转接模型。也使用与对大量描述符进行监控,但是同一时间只有少量的描述符活跃的场景(因为三种模型都是并发处理的)。
当然udp可以使用多路转接模型,udp只有一个描述符,阻塞操作也可以,所以可以用也可以不用,使用了select模型或者epoll模型也没有差别。

select模型

select模型的操作流程简介

  1. 定义某个事件的描述符集合(可读事件描述符集合,可写事件描述符集合,异常事件描述符集合),初始化清空集合。对哪个描述符关心什么事件,就把这个描述符添加到相应事件的描述符集合中
  2. 将集合拷贝到内核中进行监控(只有内核才能知道你的文件描述符是否就绪),监控的原理是轮询遍历判断。(若超时等待,有描述符就绪事件则调用返回)
  3. 监控调用的返回,表示监控出错/ 有描述符就绪 / 监控等待超时了 并且调用返回的时候,将事件监控的描述符集合中未就绪的描述符集合从集合中移除(只保留就绪的描述符)
    注意:因为调用返回的时候修改了集合,因此下次监控的时候,就需要重新向集合中添加描述符。
  4. 轮询判断哪个描述符仍然在哪个集合中,就确定了这个描述符是否就绪了某个事件,然后进行对应事件的操作即可。
    注意:select并不会直接返回给用户就绪的描述符直接操作,而是返回了就绪的描述符集合,因此需要我们进行轮询判断

TCP的实现

封装一个TCP服务端

#pragma once
#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdio.h>
#include <string>
#include <string.h>
using namespace std;

//创建TCP类,提供监听listen,accept,recv,send等功能
class TcpSvr{
  public:
    //构造函数
    TcpSvr()
      :sock_fd_(-1)
    {

    }
    ~TcpSvr()
    {
    }
    //创建套接字
    int CreateSocket()
    {
      sock_fd_ = socket(AF_INET, SOCK_STREAM, 0);
      if(sock_fd_ < 0)
      {
        //创建套接字失败
        perror("socket\n");
        return -1;
      }
      printf("套接字创建成功!\n");
      return 0;
    }
    //绑定地址信息
    int Bind(const string& IP, const uint16_t port)
    {
      sockaddr_in addr;
      //协议
      addr.sin_family = AF_INET;
      //IP地址
      addr.sin_addr.s_addr = inet_addr("0.0.0.0");
      //端口
      addr.sin_port = htons(19999);
      int ret = bind(sock_fd_, (struct sockaddr*)&addr, sizeof(addr));
      if(ret < 0)
      {
        perror("bind");
        return -1;
      }
      printf("地址绑定成功!\n");
      return 0;
    }
    //listen侦听套接字
    int Listen(int backlog = 5)
    {
      int ret = listen(sock_fd_, backlog);
      if(ret < 0)
      {
        perror("listen");
        return -1;
      }
      printf("监听成功!\n");
      return 0;
    }
    //获取新连接
    int Accept(TcpSvr* ts, struct sockaddr_in* peer_addr, socklen_t* peer_addrlen)
    {
      int new_sock = accept(sock_fd_, (struct sockaddr*)peer_addr, peer_addrlen);
      if(new_sock < 0)
      {
        perror("accept");
        return -1;
      }
      else 
      {
        printf("获取新连接成功!\n");
        ts->SetSocketFd(new_sock);
        return 0;
      }
    }

    void SetSocketFd(int fd)
    {
      sock_fd_ = fd;
      printf("SetSockfd成功!");
    }
    //发送信息
    int Send(const string& data)
    {
      ssize_t send_size = send(sock_fd_, data.c_str(), data.size(), 0);
      if(send_size < 0)
      {
        perror("send");
        return -1;
      }
      return send_size;
    }
    //接收信息
    int Recv(string* data)
    {
      char buf[1024] = {0};
      ssize_t recv_size = recv(sock_fd_, buf, sizeof(buf) - 1, 0);
      if(recv_size < 0)
      {
        perror("recv");
        return -1;
      }
      else if(recv_size == 0)
      {
        printf("peer shutdown...\n");
        return 0;
      }
      data->assign(buf, strlen(buf));
      return recv_size;
    }
    int GetSockfd()
    {
      return sock_fd_;
    }
    private:
    //套接字
    int sock_fd_;
};


封装一个select类

#pragma once 
#include <stdio.h>
#include <iostream>
#include <sys/socket.h>
#include <sys/select.h>
#include <unistd.h>
#include <vector>
#include "tcp.hpp"
using namespace std;
class SelectSvr
{
  public:
    //构造函数
    SelectSvr()
      :nfds_(-1)
    {
      FD_ZERO(&readfds_);
    }
    //析构函数
    ~SelectSvr(){}

    //添加事件集合
    void AddFd(int fd)
    {
      FD_SET(fd, &readfds_);
      //printf("添加fd: %d成功!\n",fd);
      //判断最大文件描述符是否改变
      if(fd > nfds_)
      {
        nfds_ = fd;
      }
      // printf("添加fd后,nfds 为 : %d\n",nfds_);
    }

    //删除事件集合
    void DeleteFd(int fd)
    {
      //删除文件描述符事件
      FD_CLR(fd, &readfds_);
      //判断最大的文件描述符是否改变
      for(int i = nfds_; i >= 0; i--)
      {
        if(FD_ISSET(i, &readfds_) == 1)
        {
          nfds_ = i;
          break;
        }
      }
    }

    //select监听事件
    int SelectWait(struct timeval* tv, std::vector<TcpSvr>* vec)
    {
      fd_set tmp = readfds_;
      int ret = select(nfds_ + 1, &tmp, NULL, NULL, tv);
      // printf("SelectWait :nfds 的值为 :%d\n", nfds_);
      if(ret < 0)
      {
        perror("select");
        return -1;
      }
      else if(ret == 0)
      {
        //说明阻塞超时了
        printf("time out...\n");
        return 0;
      }
      //正常监听返回结果
      for(int i = 0; i <= nfds_; i++)
      {
        if(FD_ISSET(i, &tmp) == 1)
        {
          TcpSvr ts;
          ts.SetSocketFd(i);
          vec->push_back(ts);
        }
      }
      return ret;
    }
    
  private:
    //听事件集合
    fd_set readfds_;
    //最大描述符
    int nfds_;
};

main程序

#include "select.hpp"
#include "tcp.hpp"
#define CHECK_RET(p) if(p < 0){return 0;}

int main(int argc, char* argv[])
{
  if(argc != 3)
  {
    printf("usage...\n");
    return 0;
  }
  TcpSvr ts;

  string ip = argv[1];
  uint16_t port = atoi(argv[2]);
  CHECK_RET(ts.CreateSocket());
  //创建套接字
  CHECK_RET(ts.Bind(ip, port));
  //绑定地址信息
  CHECK_RET(ts.Listen());
  //侦听套接字
  
  SelectSvr ss;
  ss.AddFd(ts.GetSockfd());
  //将侦听套接字加入到听时间集合当中去
  
  while(1)
  {
    struct timeval tv;
    tv.tv_sec = 2;
    tv.tv_usec = 0;
    std::vector<TcpSvr> vec;
    vec.clear();
    //开始侦听
    int ret = ss.SelectWait(&tv, &vec);
    cout << "vec.size(): " << vec.size() << endl;
    if(ret < 0)
    {
      perror("select");
      return -1;
    }
    else if(ret == 0)
    {
      continue;
    }
    //开始循环判断事件集合中的就绪描述符
    for(size_t i = 0; i < vec.size(); i++)
    {
      //如果是侦听套接字
      if(vec[i].GetSockfd() == ts.GetSockfd())
      {
        cout << "begin accept...\n"<< endl;
        //创建新的套接字
        TcpSvr new_ts;
        struct sockaddr_in peer_addr;
        socklen_t peer_addrlen = sizeof(peer_addr);
        ts.Accept(&new_ts, &peer_addr, &peer_addrlen);
        printf("new socket is %d, peer_ip is %s, peer_port is %d\n", new_ts.GetSockfd(), inet_ntoa(peer_addr.sin_addr), ntohs(peer_addr.sin_port));
        //添加新的文件描述符到读事件集合中
        ss.AddFd(new_ts.GetSockfd());
      }
      else 
      {
        //开始沟通
        TcpSvr new_tcp_svr = vec[i];
        string data;
        data.clear();
        int ret = new_tcp_svr.Recv(&data);
        if(ret < 0)
        {
          perror("Recv");
          //对端关闭了,那么关闭套接字
          close(new_tcp_svr.GetSockfd());
          //将文件描述符从事件集合中移除出来
          ss.DeleteFd(new_tcp_svr.GetSockfd());
          continue;
        }
        else if(ret == 0)
        {
          close(new_tcp_svr.GetSockfd());
          ss.DeleteFd(new_tcp_svr.GetSockfd());
          continue;
        }
        printf("Recv data %s\n", data.c_str());
        string send_data = "i am server~~";
        new_tcp_svr.Send(send_data);
      }
    }
  }
  close (ts.GetSockfd());
  return 0;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

升级!!!运用select实现一个简单的TCP通信! 的相关文章

  • 语音信号处理 | 基于Hilbert-Huang变换的基音检测方法

    HHT原理 Hiibert Huang变换是由Huang等人于1998年提出来的一种信号分析方法 xff0c 它主要由两个部分组成 经验模型分解 Empirical Mode Decomposition EMD 和希尔伯特变换 xff08
  • 机器学习 | 使用TensorFlow搭建神经网络实现鸢尾花分类

    鸢尾花分类问题是机器学习领域一个非常经典的问题 xff0c 本文将利用神经网络来实现鸢尾花分类 实验环境 xff1a Windows10 TensorFlow2 0 Spyder 参考资料 xff1a 人工智能实践 xff1a Tensor
  • 语音信号处理 | 基于卡尔曼滤波的语音增强算法

    文章目录 1 概述2 卡尔曼滤波原理被估计的信号离散卡尔曼滤波算法参数选择 3 基于卡尔曼滤波的语音增强算法语音模型分析参数确定 4 程序实现语音数据的导入 加噪与分帧卡尔曼滤波器参数初始化卡尔曼滤波过程结果可视化 5 运行结果与结果分析运
  • UDP实时图像传输进阶篇——1080P视频传输

    在UDP实时图像传输一文中 xff0c 介绍了如何使用UDP来实现图像的实时传输 xff0c 并使用C 进行了发送端和接收端的搭建 但是文中的方法是对整张图片进行JPEG压缩 xff0c 并通过UDP一次性地发送到接收端 xff0c 由于一
  • 机器人路径规划之Dijkstra算法

    在机器人路径规划之动态窗口法文中 xff0c 介绍了一种局部路径规划方法 动态窗口法 xff0c 本文将介绍一种全局路径规划方法 Dijkstra算法 狄克斯特拉算法 Dijkstra算法是从一个顶点到其余各顶点的最短路径算法 xff0c
  • 语音信号处理 | 使用短时能量和谱质心特征进行端点检测

    文章目录 概述原理及MATLAB实现基本流程特征提取短时能量谱质心 阈值估计和阈值化处理提取语音片段 MATLAB2020a中的VAD函数参考 概述 在复杂的应用环境下 xff0c 从音频中分割出语音信号和和非语音信号 xff0c 是一个很
  • 语音信号处理 | 傅里叶变换、短时傅里叶变换、小波变换、希尔伯特变换、希尔伯特黄变换

    在信号处理领域 xff0c 存在诸多变换 xff0c 比如标题中的五个变换 本文将对这五个变换进行介绍和比较 在开始之前 xff0c 我们需要先理清什么是平稳信号 xff0c 什么是非平稳信号 我们知道 xff0c 自然界中几乎所有信号都是
  • clang-format格式文件。可以直接复制引用

    Language Cpp BasedOnStyle LLVM AccessModifierOffset 2 AlignAfterOpenBracket Align AlignConsecutiveMacros false AlignCons
  • 多线程之多核线上考试试题瞎解

    匆忙的大三早已结束 xff0c 时隔两月 xff0c 再以此文祭奠我炸掉的多核考试 这次考试真正能写出来的也就两道题 xff0c 以下简单地记录一下 第二题 随机产生2个10 10的浮点数矩阵A和B xff0c 先将矩阵A和B作转置 xff
  • 视觉SLAM | RealsenseD435i相机标定

    在运行VINS MONO VINS Fusion等SLAM方案的时候 xff0c 需要很准确的相机参数 xff0c 否则很容易漂移 本文是RealsenseD435i相机标定过程的记录 xff0c 标定主要有三个步骤 IMU标定相机标定IM
  • 视觉SLAM | 使用RealsenseD435i运行VINS-Fusion

    使用RealsenseD435i运行VINS Fusion VINS Fusion是基于双目的视觉惯导方案 xff0c 不太符合我目前的需求 xff0c 但这是我使用的第一个视觉SLAM方案 xff0c 接下来还是简单记录下 运行环境 硬件
  • 视觉SLAM | 在ROS上运行ORB-SLAM2

    本文直接使用的github上的orb slam 2 ros实现在ROS上运行ORB SLAM2 xff0c 这个ros包能够得到相机的位姿以及稀疏点云 xff0c 而且删掉了对Pangolin的依赖 xff0c 进行可视化时要用RViz 运
  • ROS报错记录及解决方法(不定期更新)

    ROS下载缓慢 如果是在国内安装 xff0c 建议在安装之前先配置国内的镜像源 xff0c 在软件和更新进行更改即可 参考 xff1a Ubuntu18 04下安装ROS 由于没有公钥 xff0c 无法验证下列签名 安装ROS时报错 xff
  • ROS与STM32通信

    ROS与STM32是用串口进行通信的 xff0c 主要有两种方式 xff0c 一是将STM32作为一个节点 xff0c 二是制作一个上位机节点 负责与STM32进行串口通信 xff0e 第一种方式需要专门的硬件 xff0c 所以我选择第二种
  • 使用VScode搭建ROS开发环境

    俗话说 xff02 工欲善其事必先利其器 xff02 xff0c 之前在Ubuntu上运行的ROS项目都是用vim或者gedit编写和修改代码 xff0c 然后在终端编译运行 xff0c 很不方便 xff0c 函数跳转查看都没办法实现 所以
  • TCP实时图像传输

    之前尝试过使用UDP进行图像传输 xff0c 而UDP协议要求包小于64K xff0c 对于较大的图像 xff0c 需要使用分片压缩的方式进行传输 xff0c 操作较复杂 xff0c 同时不能保证图片的每一部分都能够正确传输 详见 xff1
  • STM32部分BUG及解决方法记录(不定期更新)

    1 编译使用CubeMX生成的代码时报错 Error L6218E Undefined symbol HAL PWREx DisableUCPDDeadBattery referred from stm32g4xx hal msp o 解决
  • 语音信号处理 | Python实现端点检测

    由于项目需要 xff0c 我要使用Python对语音进行端点检测 xff0c 在之前的博客使用短时能量和谱质心特征进行端点检测中 xff0c 我使用MATLAB实现了一个语音端点检测算法 xff0c 下面我将使用Python重新实现这个这个
  • 进程,线程,应用程序。概念理解

    简单的说 xff0c 进程 可以承载一组相关的 NET 程序集 xff0c 而 应用程序域 xff08 简称AppDomain xff09 是对该进程的逻辑细分 一个应用程序域进一步被细分成多个 上下文边界 xff0c 这些边界用来分组目的
  • 搭建STM32开发环境

    安装keil 点击这里下载安装最新版的keil 破解 以管理员身份运行keil xff0c 打开File gt License Management 复制CID xff0c 如下 xff1a 运行keygen2032 exe xff0c 修

随机推荐

  • 路径规划 | 图搜索算法:DFS、BFS、GBFS、Dijkstra、A*

    地图数据常常可以用图 Graph 这类数据结构表示 xff0c 那么在图结构中常用的搜索算法也可以应用到路径规划中 本文将从图搜索算法的基本流程入手 xff0c 层层递进地介绍几种图搜索算法 首先是两种针对无权图的基本图搜索算法 xff1a
  • 移动机器人中地图的表示

    在学习算法之前 xff0c 首先要做的是理解数据 xff0c 所以本专栏在开始介绍运动规划算法前 xff0c 首先介绍一下地图的数据形式 地图有很多种表示形式 xff0c 在移动机器人中比较常用的是尺度地图 拓扑地图 语义地图 尺度地图 x
  • 路径规划 | 随机采样算法:PRM、RRT、RRT-Connect、RRT*

    基于图搜索的路径规划算法主要用于低维度空间上的路径规划问题 xff0c 它在这类问题中往往具有较好的完备性 xff0c 但是需要对环境进行完整的建模工作 xff0c 在高维度空间中往往会出现维数灾难 为了解决这些问题 xff0c 本文将介绍
  • ROS多机通信

    配置主从机IP地址 分别使用sudo vi etc hosts在主从机的 etc hosts文件中添加下面的代码 xff0c 其中pi是主机的用户名 xff0c esdc是从机的用户名 ip要相应的进行更改 xff0c 可以使用ifconf
  • 路径规划 | 图搜索算法:JPS

    JPS算法全称为Jump Point Search xff0c 也就是跳点算法 xff0c 可以视为A 算法的一种改进算法 xff0c 它保留了A 算法的主体框架 xff0c 区别在于 xff1a A 算法是将当前节点的所有未访问邻居节点加
  • 路径规划 | 随机采样算法:Informed-RRT*

    在文章路径规划 随机采样算法 xff1a PRM RRT RRT Connect RRT 中 xff0c 介绍了具备渐近最优性的RRT 算法 随着采样点数的增多 xff0c RRT 算法的规划结果会逐渐收敛到最优 但是可以观察到 xff0c
  • Ubuntu20安装ROS noetic

    Ubuntu20对应的ROS版本为ROS noetic xff0c 安装过程如下 xff1a 1 打开Software amp Updates xff0c 勾选main universe restricted multiverse这四项 2
  • 使用VSCode进行远程C++开发

    本文以Windows连接Ubuntu子系统 WSL 为例来介绍VSCode的远程开发流程 首先在VSCode中安装Remote WSL插件 xff0c 重启VSCode xff0c 如下图所示 xff0c 连接WSL 如果是其他远程 xff
  • ROS话题发布和订阅节点的C++&Python实现

    本文将分别使用C 43 43 和Python来实现话题发布者和订阅者 xff0c 首先创建一个功能包 xff0c 命名为topic pub sub xff0c 添加roscpp xff0c rospy等依赖项 C 43 43 实现 创建话题
  • 只要活着,我愿意一辈子都做程序员

    前不久 xff0c 我看过一个有意思的帖子 xff0c 标题是 35岁是程序员的终点 作者列举了35岁的年龄已经不适合继续做程序员的种种原因 xff0c 试图说服在这个年龄段的程序员做出改变 xff0c 初一看 xff0c 我自己也觉得很有
  • 机器人自主导航 | ROS与移动底盘通信

    本实验是实现机器人自主导航的重要步骤 xff0c 对于轮式机器人 xff0c 可以通过在底盘加装轮式里程计的方式来获得机器人的速度数据 xff0c 这些数据可以用来辅助机器人实现自主定位 xff0c 同时机器人还需要将控制指令发送给移动底盘
  • 使用C++调用Python模块(Linux)

    使用Python调用C 43 43 库见 xff1a 我的另一篇博客 工程配置 本文使用的项目构建工具为CMake xff0c 使用FindPython工具在CMake工程中找到Python库 xff0c 注意CMake最低版本为3 12
  • ROS开机自启设置

    使用robot upstart功能包即可实现节点的开机自启 安装功能包 安装robot upstart功能包 xff0c 本文使用的Ubuntu20对应的ROS版本为noetic span class token function sudo
  • 信号处理 | 维纳滤波推导

    首先给出互相关函数定义 xff1a r s x m
  • 信号处理 | AR模型与Levinson-Durbin递推

    模型形式 由高斯白噪声驱动的全极点模型表示如下 xff1a e n
  • 使用Python调用C++库(基于pybind11)

    本文将用C 43 43 编写一个简单的向量运算库 xff0c 然后使用pybind11将其封装为python包 xff0c 再使用python调用 C 43 43 程序使用CMake构建 使用C 43 43 调用Python模块见 xff1
  • FTP实现Ubuntu与Windows文件互传

    FTP实现window与ubuntu文件互传 本文将介绍如何使用FTP实现Ubuntu和Windows间的文件互传 xff0c 基本方法是在Ubuntu主机上安装FTP服务端 xff0c 在其他设备 Windows 上安装FTP客户端 以下
  • KD-Tree详解: 从原理到编程实现

    C 43 43 实现链接 https gitee com ghowoght kd tree 在点云操作中 xff0c 常常需要从大量点云中找到距离输入点最近的点 xff0c 如果使用线性搜索 xff0c 逐个判断与输入点的距离 xff0c
  • FRP + NoMachine远程桌面开发指南

    在FRP内网穿透 远程ssh终端 43 rdp桌面配置 中介绍了如何远程使用FRP 43 RDP进行外网远程桌面访问 xff0c 但是实测很卡 在两台Ubuntu主机上进行了测试 xff0c 云服务器有8M带宽 xff0c 所以对这种远程桌
  • 升级!!!运用select实现一个简单的TCP通信!

    升级 xff01 xff01 xff01 运用select实现一个简单的TCP通信 多路转接模型select模型select模型的操作流程简介 TCP的实现封装一个TCP服务端封装一个select类main程序 多路转接模型 多路转接IO