IO多路复用实现TCP并发服务器(select)

2023-05-16

大致描述一下。先定义 fd_set 要监视的读事件readfds,FD_SET将sockfd放到readfds事件里,更新文件最大描述符,select函数返回值是返回准备好的文件描述符个数(这里不考虑出错和超时),根据返回值for循环遍历文件描述符FD_ISSET挨个判断文件描述符有没有准备好。如果有准备好的文件描述符,再判断是不是sockfd。如果是sockfd表示有新客户端连接,这里就建立accept连接,acceptfd加入到事件里,并更新最大文件描述符。如果是其他的文件描述符表示有客户端发来消息,这里就recv接收消息并处理。、有客户端退出或断开连接就删除该文件描述符FD_CLR。

server.c

#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#define ERRLOG(ermsg)                                      \
    do {                                                   \
        printf("%s %s %d:", __FILE__, __func__, __LINE__); \
        perror(ermsg);                                     \
        exit(-1);                                          \
    } while (0)

int main(int argc, const char* argv[])
{
    if (argc != 3) {
        printf("retype!\n");
        return -1;
    }
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) {
        ERRLOG("socket error");
    }
    // 填充服务器信息结构体
    struct sockaddr_in serveraddr;
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(atoi(argv[2]));
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);

    socklen_t serveraddr_len = sizeof(serveraddr);

    if (-1 == bind(sockfd, (struct sockaddr*)&serveraddr, serveraddr_len)) {
        ERRLOG("bind error");
    }
    if (-1 == listen(sockfd, 10)) {
        ERRLOG("listen error");
    }
    // 定义客户端消息结构体
    struct sockaddr_in clientaddr;
    memset(&clientaddr, 0, sizeof(clientaddr));
    socklen_t clientaddr_len = sizeof(clientaddr);

    int acceptfd; // accept函数返回值
    int maxfd;
    int ret; // select返回值
    int i;
    char buf[128] = { 0 };
    int recvnum; // recv函数返回值

    // 创建要监视的文件描述符集合
    fd_set readfds; // 母本
    fd_set readfdscopy; // 副本
    FD_ZERO(&readfds); // 清空集合初始化
    FD_ZERO(&readfdscopy); // 清空集合初始化
    // 把sockfd添加到要监视的集合中
    FD_SET(sockfd, &readfds);
    // 更新最大文件描述符
    maxfd = maxfd > sockfd ? maxfd : sockfd;

    while (1) {
        readfdscopy = readfds;
        ret = select(maxfd + 1, &readfdscopy, NULL, NULL, NULL);
        if (ret == -1) { // select 返回值-1表示失败
            ERRLOG("select error");
        } else { // select返回值为0表示超时,这里先不考虑,程序到这里就表示就准备好的文件描述符
            // 遍历文件描述符 判断文件描述符有没有准备好
            for (i = 3; i < maxfd + 1 && ret != 0; i++) {
                if (FD_ISSET(i, &readfdscopy)) { // fd_isset判断文件描述符是不是在集合里,如果                                                       进来就表示有文件描述符准备好了
                    ret--; // 进入if就表示是准备好的文件描述符,ret-1
                    // 再判断是sockfd还是acceptfd
                    if (i == sockfd) {
                        // 进到这里表示有新的客户端连接
                        acceptfd = accept(sockfd, (struct sockaddr*)&clientaddr, 
                                                                &clientaddr_len);
                        if (acceptfd == -1) {
                            ERRLOG("accept error");
                        }
                        printf("客户端[%d]连接到服务器\n", acceptfd);
                        // 将新来的acceptfd放到集合里
                        FD_SET(acceptfd, &readfds);
                        // 更新最大文件描述符
                        maxfd = maxfd > acceptfd ? maxfd : acceptfd;
                    } else {
                        memset(buf, 0, sizeof(buf));
                        // 进到这里表示有客户端发来消息
                        recvnum = recv(i, buf, sizeof(buf), 0);
                        if (recvnum == -1) {
                            ERRLOG("recv error");
                        } else if (recvnum == 0) {
                            printf("客户端[%d]断开连接\n", i);
                            close(i);
                            FD_CLR(i, &readfds); // 从集合中删除
                            continue; // 退出本次for循环
                        }
                        if (!strcmp(buf, "quit")) {
                            printf("客户端[%d]退出了\n", i);
                            close(i);
                            FD_CLR(i, &readfds); // 从集合中删除
                            continue; // 退出本次for循环
                        }
                        printf("客户端[%d]发来数据[%s]\n", i, buf);
                        strcat(buf, "--cmcc");
                        if (-1 == send(i, buf, sizeof(buf), 0)) {
                            ERRLOG("send error");
                        }
                    }
                }
            }
        }
    }
    return 0;
}

client.c

#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#define ERRLOG(ermsg)                                      \
    do {                                                   \
        printf("%s %s %d:", __FILE__, __func__, __LINE__); \
        perror(ermsg);                                     \
        exit(-1);                                          \
    } while (0)

int main(int argc, const char* argv[])
{
    if (argc != 3) {
        printf("Usage : %s <IP> <PORT>\n", argv[0]);
        return -1;
    }
    // 1创建套接字
    int sockfd = socket(AF_INET, SOCK_STREAM, 0); // AF_INET:IPV4,SOCK_STREAM:TCP
    if (-1 == sockfd) {
        ERRLOG("socket error");
    }
    // 2填充服务器网络信息结构体

    struct sockaddr_in serveraddr = {
        .sin_family = AF_INET,
        .sin_port = htons(atoi(argv[2])), // 端口号  argv[2]输入进来的是char *字符串,需要转换成数用atoi
        .sin_addr.s_addr = inet_addr(argv[1]), // IP地址
    };
    socklen_t serveraddr_len = sizeof(serveraddr);
    char buf[128];
    if (connect(sockfd, (struct sockaddr*)&serveraddr, serveraddr_len) == -1) {
        ERRLOG("connect error");
    }
    printf("与服务器连接成功\n");

    while (1) {
        memset(buf, 0, sizeof(buf));
        fgets(buf, sizeof(buf), stdin);
        buf[strlen(buf) - 1] = '\0';
        if (-1 == send(sockfd, buf, sizeof(buf), 0)) {
            ERRLOG("send error");
        }
        int ret = recv(sockfd, buf, sizeof(buf), 0);
        if (ret == -1) {
            ERRLOG("recv error");
        }
        if (ret == 0) {//服务器断开连接
            break;
        }
        printf("接收的数据%s\n", buf);
    }
    close(sockfd);

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

IO多路复用实现TCP并发服务器(select) 的相关文章

随机推荐

  • Atitit HTTP 认证机制基本验证 (Basic Authentication) 和摘要验证 (Digest Authentication)attilax总结Atitit HT

    Atitit HTTP 认证机制 基本验证 Basic Authentication 和摘要验证 Digest Authentication attilax总结 1 1 最广泛使用的是基本验证 Basic Authentication 和摘
  • 实验二 读取和理解激光雷达数据

    1 体验内容 xff08 1 xff09 为rplidar添加USB权限 注 xff1a 实验室的rplidar A2买得比较早 xff0c 硬件版本可能与github程序不匹配 xff0c 出现运行错误 xff0c 解决方法为到 Wind
  • Linux下使用Netfilter框架编写内核模块(统计协议层ping特定地址丢包数)

    一 linux内核中neitfilter的处理过程 1 5个HOOK点的执行点说明 xff1a 数据包从进入系统 xff0c 进行IP校验以后 xff0c 首先经过第一个HOOK函数NF IP PRE ROUTING进行处理 xff1b 然
  • 基于TCP 实现客户端之间通信【1】

    前段时间学习了基于TCP协议下实现服务器与一个客户端的通信 xff0c 服务器与多个客户端之间的通信 xff0c 以及客户端之间的互相通信 下面就是我写的利用TCP和多线程技术实现客户端之间互相通信的代码 xff1a 服务器端 xff1a
  • 回环接口(loop-back/loopback)

    回环接口 xff08 loop back loopback xff09 Moakap 整理 Loopback 接口是一个虚拟网络接口 xff0c 在不同的领域 xff0c 其含义也大不一样 1 xff0e TCP IP 协议栈中的 loop
  • socket封装HTTP请求

    之前写过两个socket封装的HTTP GET请求 xff0c 可是知其然 xff0c 不知所以然 这次写POST请求就有点懵逼了 还是从大佬的文章出发 xff1a https blog csdn net a19881029 article
  • access中,如何删除不可见空格

    1 可以使用 Trim 函数 xff0c 删除字符串 首 尾 的正常空格 span class token keyword update span 表名 span class token keyword set span 字段 span c
  • Raspberry Pi树莓派分类和其相似产品介绍

    文章目录 前言一 1代树莓派二 2代树莓派三 3代树莓派四 4代树莓派五 树莓派Pico六 目前可替代板子介绍引用 前言 树莓派官网 作为小型计算机的代表 xff0c 树莓派可是作为开山鼻祖 xff0c 本文聊一下目前树莓派几代板子发展历程
  • ARM汇编指令ldr和MOV的区别

    1 八位图 数据 2 MOV指令 MOV指令可以把立即数或者寄存器内容 xff08 注意 xff1a 这里绝对不可以是内存 xff01 xff01 xff09 传递给一个寄存器 MOV对于立即数是有要求的 xff0c 就是上边的 8位图 数
  • GPS NEMA 0183协议

    一 NMEA0183标准语句 GPS常用语句 GPGGA 例 xff1a GPGGA 092204 999 4250 5589 S 14718 5084 E 1 04 24 4 19 7 M 0000 1F 字段0 xff1a GPGGA
  • 使用setvbuf更改printf的默认buffer 行为

    有3种buffer行为 xff0c 不缓冲 xff0c 基于块的缓冲 和 基于行的缓冲 stdout xff08 printf xff09 默认是基于行的缓冲 xff0c 即写到stdout的字符都会被缓冲起来直到一个换行符输出的时候 xf
  • linux 下 tcpdump详解 后篇(自己实现抓包过滤)

    一 概述 在了解了tcpdump的原理后 xff0c 你有没有想过自己去实现抓包过滤 xff1f 可能你脑子里有个大概的思路 xff0c 但是知道了理论知识 xff0c 其实并不能代表你完全的理解 只要运用后 xff0c 你才知道哪些点需要
  • 结构体与共同体(联合体)的妙用

    结构体与共同体 xff08 联合体 xff09 的妙用 学习过C语言后 xff0c 大家都了解了结构体与共同体 两者之间的区别是 xff1a 共同体 xff1a 使几个不同类型的变量共占一段内存 相互覆盖 所占内存长度是各最长的成员占的内存
  • [北力电子] 无人机4G图传数传一体 pixhawk mavlink GSLINK 720P

    随着无人机和4G技术的发展 xff0c 实时监看空中视角的画面已经成为可能GSLINK突破传统的传输方式 xff0c 利用了4G网络将数据和视频流融为一体进行无限距离的传输 用户使用EC2地面站 xff08 Mission Planner
  • C#笔记(基础篇)

    简介 第一次发博客 xff0c 欢迎交流沟通 因为学习虚拟现实需要所以暑假一个月学了点C xff0c 做了笔记 xff0c 在这进行分享 xff0c 欢迎浏览 有些代码打在VS中 没有记录在笔记里 请见谅 xff08 视频指路 xff09
  • 《机器人操作系统(ROS)浅析》笔记

    机器人操作系统 xff08 ROS xff09 浅析 这是看了 A Gentle Introduction to ROS 这本书后记的笔记 xff0c 网上刚好找得到中文版的 xff0c 就看中文版了 xff0c 欢迎大佬批评指正 xff0
  • 搭建ROS小车——硬件购买篇(基于arduino mega2560,L298N)

    在寒假期间完成了ROS小车的制作 xff0c 记录一下 xff0c 不过也有很多不足 xff0c 之后再进行修正 xff0c 本系列文章将分为硬件购买篇 下位机篇 上位机篇 功能拓展篇四个部分来进行讲解 上位机 树莓派3B 43 16GBS
  • 搭建ROS小车——上位机篇(基于arduino mega2560,L298N)

    环境 上位机为树莓派3B 43 环境 xff1a ubuntu mate16 04 43 ros kineticros master在电脑上 xff0c 环境为ubuntu18 04 43 ros melodic 考虑到之后可能会拓展别的功
  • R语言入门学习

    R语言 视频链接 xff1a https www bilibili com video BV19x411X7C6 数据分析过程 数据采集 数据存储 数据分析 数据挖掘 数据可视化 进行决策 1 Rstudio使用 1 1 入门 TAB补齐
  • IO多路复用实现TCP并发服务器(select)

    大致描述一下 先定义 fd set 要监视的读事件readfds xff0c FD SET将sockfd放到readfds事件里 xff0c 更新文件最大描述符 xff0c select函数返回值是返回准备好的文件描述符个数 xff08 这