【C++】epoll + socket 网络编程实现一个简单的 server

2023-05-16

文章目录

  • 服务端
  • 客户端

本着学习 epoll 的态度,本文使用 epoll + socket 实现了一个简单的 server ,可以接收多个客户端的连接并与之通信。

话不多说,直接上代码,完整的代码见 github epoll 。

参考:IO 多路转接(复用)之 epoll

服务端

服务端的代码如下:

//
// Created by lixiaoqing on 2022/7/18.
//

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <string>
#include <iostream>

// socket 最大连接数
#define SOCKET_COUNT (64)

// epoll 监听的描述符的最大数量
#define EPOLL_COUNT (100)

int main() {

    int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (socket_fd == -1) {
        perror("socket error");
        exit(1);
    }

    // 设置端口复用
    int opt = 1;
    setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(9999);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);  // 监听本地所有IP
    int ret = bind(socket_fd, (struct sockaddr *) &addr, sizeof(addr));
    if (ret == -1) {
        perror("bind error");
        exit(1);
    }

    ret = listen(socket_fd, SOCKET_COUNT);
    if (ret == -1) {
        perror("listen error");
        exit(1);
    }

    int epoll_fd = epoll_create(EPOLL_COUNT);
    if (epoll_fd == -1) {
        perror("epoll_create error");
        exit(1);
    }

    struct epoll_event ev;
    ev.data.fd = socket_fd;
    ev.events = EPOLLIN;  // 检测 socket_fd 读缓冲区是否有数据
    ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket_fd, &ev);
    if (ret == -1) {
        perror("epoll_ctl error");
        exit(1);
    }

    struct epoll_event evs[1024];
    int size = sizeof(evs) / sizeof(struct epoll_event);

    // 持续检测
    for (;;) {
        printf("epoll_wait ...\n");
        int num = epoll_wait(epoll_fd, evs, size, -1);
        for (int i = 0; i < num; ++i) {
            int cur_fd = evs[i].data.fd;
            if (cur_fd == socket_fd) {
                int new_fd = accept(socket_fd, NULL, NULL);
                ev.data.fd = new_fd;
                ev.events = EPOLLIN;
                ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, new_fd, &ev);
                if (ret == -1) {
                    perror("epoll_ctl accept error");
                    continue;
                }
            } else {
                char buf[1024]{};
                int len = recv(cur_fd, buf, sizeof(buf), 0);
                if (len == 0) {
                    printf("客户端 %d 已经断开了连接\n", cur_fd);
                    epoll_ctl(epoll_fd, EPOLL_CTL_DEL, cur_fd, NULL);
                    close(cur_fd);
                } else if (len > 0) {
                    printf("客户端说: %s\n", buf);
                    std::string text;
                    getline(std::cin, text);  // 等待输入
                    text.insert(0, "from server: ");
                    text.push_back('\n');
                    send(cur_fd, text.c_str(), text.length(), 0);
                } else {
                    perror("socket recv error");
                    continue;
                }
            }
        }
    }

    // 关闭 socket
    close(epoll_fd);
    close(socket_fd);

    return 0;
}

客户端

客户端的代码如下:

//
// Created by lixiaoqing on 2022/7/15.
//

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/epoll.h>

int main() {

    int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (socket_fd == -1) {
        perror("socket error");
        exit(1);
    }

    // 设置端口复用
    int opt = 1;
    setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(9999);
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    int ret = connect(socket_fd, (struct sockaddr *) &addr, sizeof(addr));
    if (ret == -1) {
        perror("connect error");
        exit(1);
    }

    char buf[1024]{};
    strcpy(buf, "Hello world");
    int len = send(socket_fd, buf, strlen(buf), 0);
    if (len < 0) {
        perror("send error");
        exit(1);
    }

    len = recv(socket_fd, buf, sizeof(buf), 0);
    if (len < 0) {
        perror("recv error");
        exit(1);
    }
    printf("client recv: %s\n", buf);

    // 关闭 socket
    close(socket_fd);

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

【C++】epoll + socket 网络编程实现一个简单的 server 的相关文章

  • Cefsharp.WinForms-v112.3.0 带您最新版体验(小更新)

    一 准备 下载最新包及依赖包 对应 NET4 5 2 后续版本可能4 6 2 到packages中 本地升级更快 NuGet Gallery CefSharp WinForms 112 3 0 NuGet Gallery CefSharp
  • 无人机飞行控制算法、控制律设计软件与半物理仿真

    工业级多功能可编程飞行控制系统专业的图形化控制律设计软件灵活强大的工程应用开发平台DSP处理器及高精度传感器自定义高速遥测数据采集嵌入式半物理仿真系统丰富的用户设备接口适用于固定翼 旋翼机 特殊飞行器 车船艇 机器人 云台等 概 述 xff
  • 程序调试记录

    最近把师兄的程序在万兆网络上进行测试 xff0c 现在把调试中出现的问题进行记录 xff1a 1 xff09 其中一共是十六块板子 xff0c 板子的配置文件是sipixel xml xff0c 每块板子的配置信息里都有对应的IP xff0
  • 华清远见嵌入式学习day27——编译工具和环境搭建

    0 系统移植四天课程安排 1 编译工具 xff0c 环境搭建 2 bootloader 3 kernel 4 文件系统 1 嵌入式系统的应用领域 1 军事 2 医疗 3 移动设备 4 家电 5 工控 2 什么是嵌入式系统 一般的定义 xff
  • tf.Variable函数的用法

    tf Variable xff08 initializer xff0c name xff09 xff1a initializer是初始化参数 xff0c 可以有tf random normal xff0c tf constant xff0c
  • Docker入门操作+文件备份

    文件备份操作 bin sh it is a shell script which provides function of auto backup ecology logfiles regularly by 494389 date 61 9
  • Qnap Docker(Container Station)更改国内镜像源

    0x01qpkg环境 通常qnap市场中下载的qpkg应用 xff0c 其环境变量就在自己的包环境中 所以要修改系统中的配置 xff0c 通常需要修改qpkg应用中对应的配置 即 share CACHEDEV1 DATA qpkg xxx
  • docker源码编译(containerd+runc源码编译)

    目录 源码下载docker cli docker enginebugsgo get timeoutdebian超时git clone TLS containerd源码编译runc源码编译docker 43 containerd 43 run
  • ros中激光雷达的消息类型(sensor_msgs/LaserScan Message)说明

    最近在做一些视觉和激光数据融合的项目 xff0c 但是对激光数据的结构不是太了解 xff0c 因此查了很多相关的内容 xff0c 记录以下 下图是在http wiki ros org中截取的图片 xff1a Header 是一个结构体 xf
  • Linux下安装常见的configure错误列表

    Linux下安装常见的configure错误列表 标签 xff1a linux nginx configure 安装 错误 it 附一些常见的configure错误列表供参考 configure error No curses termca
  • 世界上不存在完美的人性

    白夜追凶 观后感 作者 xff1a 朱金灿 来源 xff1a http blog csdn net clever101 前几天看了克里斯多福 诺兰导演的惊悚电影 白夜追凶 剧情大致如下 xff1a 威尔 多莫 xff08 阿尔 帕西诺饰 x
  • k210-arduino深度学习视觉机械臂抓取

    一 arduino对机械臂的基础控制 1 首先实现arduino对机械臂的控制 xff08 点动和自动 xff09 xff0c 六个轴分别定义为xyzjkl 点动 xff1a 点动方式为按一下对应按键使对应轴正转或反转3度 xff0c 此方
  • stm32串口助手配置步骤

    串口设置的一般步骤可以总结为如下几个步骤 xff1a 1 串口时钟使能 xff0c GPIO 时钟使能 2 设置引脚复用器映射 xff1a 调用 GPIO PinAFConfig 函数 3 GPIO 初始化设置 xff1a 要设置模式为复用
  • linux查看新增串口、USB设备,Minicom + Usb转串口

    查看新增串口 USB设备 注意 xff1a 虚拟机环境下的ubuntu默认情况下是不能自动识别的 xff0c 需要在虚拟机窗口右下角点击 34 Prolific USB Serial Controller 34 xff0c 然后选择 34
  • yolo图像检测数据集格式转换:xml 与 txt格式相互转换

    格式介绍 一图流介绍的比较详细 xff0c 一般图像检测数据集格式为txt或者xml格式 xff0c 在使用labelimg进行标注的时候 xff0c 可以设置获得不同格式的数据集 xff0c 以满足不同算法训练格式要求 xff1a 一般建
  • 二分类确定画出roc曲线,以及基于roc曲线获得最佳划分阈值

    问题 在做二分类问题时候 xff0c 有正样本和负样本 构建的算法 xff0c 针对每个样本会输出一个分数值 假设该分数大小为 0 1 区间内的值 有时候单纯地以分数0 5位阈值划分样本为预测为1或者预测为0 xff0c 效果有时候并不好
  • Andrew Ng和OpenAI教你写prompt

    课程地址 xff1a https learn deeplearning ai chatgpt prompt engb站搬运 xff1a https www bilibili com video BV1No4y1t7Zn 教学人员 xff1a
  • Datawhale-chatGPT用于句词分类

    NLU基础 句子级别的分类 Token级别的分类 相关API chatGPT Style prompt建议 NLU应用 文档问答 分类 实体微调 智能对话
  • Datawhale-AIGC的人才培养

    清晰有效的提示 情景学习 思维链 提示的应用 致谢
  • C语言中include是什么?

    include 34 stdio h 34 include 34 math h 34 main double x s printf 34 input number n 34 scanf 34 lf 34 amp x s 61 sin x p

随机推荐