linux 网络编程---->多路复用:select实例!

2023-11-05

好吧,我承认找了好久,网上都没有像样的完整的实例,然后自己参照书自己写一个吧!

//!> server 端代码
//!> server.c


#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/socket.h>

#define BUF_LEN 1024
#define SERV_PORT 6000
#define FD_SIZE 100
#define MAX_BACK 100

int main( int argc, char ** argv )
{
    int             listenfd, connfd, sockfd, maxfd, maxi, i;
    int             nready, client[FD_SIZE];        //!> 接收select返回值、保存客户端套接字
    int             lens;
    ssize_t     n;                //!> read字节数
    fd_set        rset, allset;    //!> 不要理解成就只能保存一个,其实fd_set有点像封装的数组
    char         buf[BUF_LEN];               
    socklen_t    clilen;
    struct sockaddr_in servaddr, chiaddr;
   
    if( ( listenfd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 )
    {
        printf( "Create socket Error : %d\n", errno );
        exit( EXIT_FAILURE );
    }
   
    //!>
    //!> 下面是接口信息
    bzero( &servaddr, sizeof( servaddr ) );
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr  =htonl( INADDR_ANY );
    servaddr.sin_port = htons( SERV_PORT );
   
    //!>
    //!> 绑定
    if( bind( listenfd, ( struct sockaddr * )&servaddr, sizeof( servaddr ) ) == -1 )
    {   
        printf("Bind Error : %d\n", errno);
        exit(EXIT_FAILURE  );
    }
   
    //!>
    //!> 监听
    if( listen( listenfd, MAX_BACK ) == -1 )
    {
        printf("Listen Error : %d\n", errno );
        exit( EXIT_FAILURE );
    }
   
    //!> 当前最大的感兴趣的套接字fd
    maxfd = listenfd;    //!> 当前可通知的最大的fd
    maxi = -1;            //!> 仅仅是为了client数组的好处理
   
    for( i = 0; i < FD_SIZE; i++ )    //!> 首先置为全-1
    {
        client[i] = -1;        //!> 首先client的等待队列中是没有的,所以全部置为-1
    }
   
    FD_ZERO( &allset );        //!> 先将其置为0
    FD_SET( listenfd, &allset );
                    //!> 说明当前我对此套接字有兴趣,下次select的时候通知我!
   
    while( 1 )
    {
        rset = allset;//!> 由于allset可能每次一个循环之后都有变化,所以每次都赋值一次
        if( (nready = select( maxfd + 1, &rset, NULL, NULL, NULL )) == -1)
        {                    //!> if 存在关注
            printf("Select Erorr : %d\n", errno );
            exit( EXIT_FAILURE );
        }
       
        if( nready <= 0 )            //!> if 所有的感兴趣的没有就接着回去select
        {
            continue;
        }
       
       
       
        if( FD_ISSET( listenfd, &rset ) )            //!> if 是监听接口上的“来电”
        {                                            //!>
            //!> printf("server listen ...\n");
            clilen = sizeof( chiaddr );
           
            printf("Start doing... \n");
           
               if( ( connfd  = accept( listenfd, (struct sockaddr *)&chiaddr, &clilen ) ) == -1 )
               {                                        //!> accept 返回的还是套接字
                   printf( "Accept Error : %d\n", errno );
                   continue;
               }
              
              
               for( i = 0; i < FD_SIZE; i++ )    //!> 注意此处必须是循环,刚开始我认
                                                   //!> 为可以直接设置一个end_i来直接处
                                                   //!> 理,实质是不可以的!因为每个套接
               {                                    //!> 字的退出时间是不一样的,后面的
                   if( client[i] < 0 )                //!> 可能先退出,那么就乱了,所以只
                   {                                //!> 有这样了!
                       client[i] = connfd;            //!> 将client的请求连接保存
                       break;
                   }
               }
              
               if( i == FD_SIZE )                //!> The last one
               {
                   printf( "To many ... " );
                   close( connfd );            //!> if 满了那么就不连接你了,关闭吧
                continue;                    //!> 返回
               }
                                            //!> listen的作用就是向数组中加入套接字!
            FD_SET( connfd, &allset );    //!> 说明现在对于这个连接也是感兴趣的!
                                            //!> 所以加入allset的阵容
            if( connfd > maxfd )            //!> 这个还是为了解决乱七八糟的数组模型
                                            //!> 的处理
            {
                maxfd = connfd;
            }
           
            if( i > maxi )                    //!> 同上
            {
                maxi = i;
            }
        }

        //!> 下面就是处理数据函数( 其实说本质的select还是串行 )
        for( i = 0; i <= maxi; i++ )        //!> 对所有的连接请求的处理
        {
            if( ( sockfd = client[i] ) > 0 )    //!> 还是为了不规整的数组
            {            //!> 也就说client数组不是连续的全正数或者-1,可能是锯齿状的
                if( FD_ISSET( sockfd, &rset ) )    //!> if 当前这个数据套接字有要读的
                 {
                     memset( buf, 0, sizeof( buf ) );    //!> 此步重要,不要有时候出错
                
                     n = read( sockfd, buf, BUF_LEN);
                     if( n < 0 )
                     {
                         printf("Error!\n");
                         close( sockfd );            //!> 说明在这个请求端口上出错了!
                        FD_CLR( sockfd, &allset );
                        client[i] = -1;
                        continue;
                     }
                    if( n == 0 )
                    {
                        printf("no data\n");
                        close( sockfd );            //!> 说明在这个请求端口上读完了!
                        FD_CLR( sockfd, &allset );
                        client[i] = -1;
                        continue;
                    }
                   
                    printf("Server Recv: %s\n", buf);
                   
                    if( strcmp( buf, "q" ) == 0 )                //!> 客户端输入“q”退出标志
                    {
                        close( sockfd );
                        FD_CLR( sockfd, &allset );
                        client[i] = -1;
                        continue;
                    }
                   
                    printf("Server send : %s\n", buf);
                    write( sockfd, buf, n );        //!> 读出来的写进去
                }
            }
        }
       
    }
   
    return 0;
}


//!> client 端代码
//!> client.c


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

#define MAXLINE 1024
#define SERV_PORT 6000

//!> 注意输入是由stdin,接受是由server发送过来
//!> 所以在client端也是需要select进行处理的
void send_and_recv( int connfd )
{
    FILE * fp = stdin;
    int   lens;
    char send[MAXLINE];
    char recv[MAXLINE];
    fd_set rset;
    FD_ZERO( &rset );
    int maxfd = ( fileno( fp ) > connfd ? fileno( fp ) : connfd  + 1 );   
                                                //!> 输入和输出的最大值
    int n;
   
    while( 1 )
    {
        FD_SET( fileno( fp ), &rset );
        FD_SET( connfd, &rset );            //!> 注意不要把rset看作是简单的一个变量
                                                //!> 注意它其实是可以包含一组套接字的哦,
                                                //!> 相当于是封装的数组!每次都要是新的哦!
       
        if( select( maxfd, &rset, NULL, NULL, NULL ) == -1 )
        {
            printf("Client Select Error..\n");
            exit(EXIT_FAILURE  );
        }
       
        //!> if 连接口有信息
        if( FD_ISSET( connfd, &rset ) )    //!> if 连接端口有信息
        {
            printf( "client get from server ...\n" );
            memset( recv, 0, sizeof( recv ) );
            n = read( connfd, recv, MAXLINE );
            if( n == 0 )
            {
                printf("Recv ok...\n");
                break;
            }
            else if( n == -1 )
            {
                printf("Recv error...\n");
                break;
            }
            else
            {
                lens = strlen( recv );
                recv[lens] = '\0';
                //!> 写到stdout
                write( STDOUT_FILENO, recv, MAXLINE );
                printf("\n");
            }

        }
       
        //!> if 有stdin输入
        if( FD_ISSET( fileno( fp ), &rset ) )    //!> if 有输入
        {
            //!> printf("client stdin ...\n");
           
            memset( send, 0, sizeof( send ) );
            if( fgets( send, MAXLINE, fp ) == NULL )
            {
                printf("End...\n");
                exit( EXIT_FAILURE );
            }
            else
            {
                //!>if( str )
                lens = strlen( send );
                send[lens-1] = '\0';        //!> 减一的原因是不要回车字符
                                            //!> 经验值:这一步非常重要的哦!!!!!!!!
                if( strcmp( send, "q" ) == 0 )
                {
                    printf( "Bye..\n" );
                    return;
                }
               
                printf("Client send : %s\n", send);
                write( connfd, send, strlen( send ) );
            }
        }
       
    }
   
}

int main( int argc, char ** argv )
{
    //!> char * SERV_IP = "10.30.97.188";
    char     buf[MAXLINE];
    int       connfd;
    struct sockaddr_in servaddr;
   
    if( argc != 2 )
    {
        printf("Input server ip !\n");
        exit( EXIT_FAILURE );
    }
   
    //!> 建立套接字
    if( ( connfd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 )
    {
        printf("Socket Error...\n" , errno );
        exit( EXIT_FAILURE );
    }

    //!> 套接字信息
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERV_PORT);
    inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
   
    //!> 链接server
    if( connect( connfd, ( struct sockaddr *  )&servaddr, sizeof( servaddr ) ) < 0 )
    {
        printf("Connect error..\n");
        exit(EXIT_FAILURE);
    }   
   

    //!>
    //!> send and recv
    send_and_recv( connfd );
   
    //!>

    close( connfd );
    printf("Exit\n");
   
    return 0;
}


编译+运行:

gcc -o server server.c
gcc -o client   client.c

./server
./client 10.80.1.251    ( 注意参数你指定的server的IP哦  )


如果要多个一起运行,给个脚本:
#!/bin/sh
index=10

while [ $index -gt 0 ]
do
     ./client  10.80.1.251 &          # 注意在后台哦,所以只能看到在server下的现象,可以自己改进
      $index = `expr $index - 1`
done

exit 0



FROM: http://blog.sina.com.cn/s/blog_6dc9e4cf0100ycuw.html

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

linux 网络编程---->多路复用:select实例! 的相关文章

  • linux AIO (异步IO) 那点事儿

    在高性能的服务器编程中 IO 模型理所当然的是重中之重 需要谨慎选型的 对于网络套接字 我们可以采用epoll 的方式来轮询 尽管epoll也有一些缺陷 但总体来说还是很高效的 尤其来大量套接字的场景下 但对于Regular File 来说
  • 【Linux学习】epoll详解

    什么是epoll epoll是什么 按照man手册的说法 是为处理大批量句柄而作了改进的poll 当然 这不是2 6内核才有的 它是在2 5 44内核中被引进的 epoll 4 is a new API introduced in Linu
  • linux网络编程实现投票功能

    投票系统 1 说明 写了一个投票系统 过程是先配置好服务器 在写一个网上投票功能 要实现网上投票功能 其实功能实现还是很简单的 麻烦一点的在于过程比较繁杂 要做的东西还是挺多的 2 过程 第一步 配置httpd服务器 先配置好httpd服务
  • ipv6 socket bind 失败 - accept_dad

    file proc sys net ipv6 conf interface accept dad variable net ipv6 conf interface accept dad Official reference Whether
  • 网络层:IP协议

    本博文分享的是网络层的IP协议 从IP协议的基本概念 协议格式开始分析并分享出来 IP协议的基本概念 不同于讨论TCP UDP时只讨论通信主机之间的关系 在讨论IP协议中 会加上主机之间的网络来一起进行讨论分析 主机 一般配有IP地址 路由
  • whois命令常见用法

    whois命令常见用法 whois命令简介 安装whois Windows使用whois Linux安装whois whois常见用法 Linux下whois查询域名注册信息 whois命令简介 whois就是一个用来查询域名是否已经被注册
  • C++在线五子棋对战(网页版)项目:websocket协议

    目标 认识理解websocket协议 websocket切换过程和websocket协议格式 认识和学会使用websocketpp库常用接口 了解websocketpp库搭建服务器流程 认识和学会使用websocketpp库bin接口 最后
  • Linux下socket异步通讯聊天程序

    Linux下socket异步通讯聊天程序 转 original from http yangqi org linux socket asynchronous im system Posted by yangqi 2010年02月17日 We
  • linux 网络编程---->多路复用:select实例!

    好吧 我承认找了好久 网上都没有像样的完整的实例 然后自己参照书自己写一个吧 gt server 端代码 gt server c include
  • socket阻塞与非阻塞,同步与异步、I/O模型

    socket阻塞与非阻塞 同步与异步 作者 huangguisu 1 概念理解 在进行网络编程时 我们常常见到同步 Sync 异步 Async 阻塞 Block 非阻塞 Unblock 四种调用方式 同步 所谓同步 就是在发出一个功能调用时
  • Linux网络通信总结

    网络IO之阻塞 非阻塞 同步 异步 单播 多播 组播 广播 多路复用POLL SELECT epoll 超时 read write accept connect 超时 实现 1 用select来设置超时机制 2 使用setsockopt 函
  • 基于tcpdump实例讲解TCP/IP协议

    前言 虽然网络编程的socket大家很多都会操作 但是很多还是不熟悉socket编程中 底层TCP IP协议的交互过程 本文会一个简单的客户端程序和服务端程序的交互过程 使用tcpdump抓包 实例讲解客户端和服务端的TCP IP交互细节
  • linux系统PXE自动装机无人值守

    PXE高效批量网络装机 PXE服务允许客户机通过网络从远程服务器下载引导镜像 并安装文件或整个操作系统 必须满足的前提条件 客户机的网卡支持PXE协议 且主板支持网络引导 网络中有一台DHCP服务器为客户机分配地址 指定引导文件位置 服务器
  • Linux网络和安全:配置、远程访问与防御指南

    文章目录 Linux 网络和安全 引言 网络配置 IP地址配置 配置网络接口 防火墙设置 安全性加强 Linux网络配置及端口管理 网络配置命令 端口管理 防火墙和安全性设置 防火墙管理工具 安全性设置 Linux远程访问技术 SSH和VP
  • Linux网络编程一步一步学 - 目录(汇总)zhuan

    非常好的关于Linux网络编程的文章 我也是无意中在别人的博客里发现的 在网上搜了一下 这类的文章总共还有很多篇 最后我找到了原作者的姓名 周立发 以及作者的博客空间 本人坚决支持原创 在此将其文章进行了汇总 并且空间首页加入了他的博客链接
  • 【网络自定向下的学习】——TCP3次握手和4次挥手详解

    目录 前言 一 可靠数据传输 1 确认应答机制 2 超时重传机制 二 建立连接 三次握手 1 建立连接的过程 2 为什么会有三次握手 3 三次握手可以携带数据吗 4 什么是半连接队列 三 断开连接 4次挥手 1 4次挥手的过程 2 为什么连
  • 组播技术

    1 概述 1 1 产生背景 传统的IP通信有两种方式 一种是在源主机与目的主机之间点对点的通信 即单播 另一种是在源主机与同一网段中所有其它主机之间点对多点的通信 即广播 如果要将信息发送给多个主机而非所有主机 若采用广播方式实现 不仅会将
  • 【网络】——UDP复习笔记

    目录 1 UDP报文结构 2 UDP缓冲区 3 UDP特点 4 课后题 1 UDP报文结构 2 UDP缓冲区 UDP没有真正意义上的发送缓冲区 应用层调用sendto 函数直接将数据应用层的数据拷贝给传输层 传输层再构建UDP报头 然后再交
  • 高并发的epoll+多线程

    epoll是linux下高并发服务器的完美方案 因为是基于事件触发的 所以比select快的不只是一个数量级 单线程epoll 触发量可达到15000 但是加上业务后 因为大多数业务都与数据库打交道 所以就会存在阻塞的情况 这个时候就必须用
  • linux非阻塞socket教程

    本文并非解释什么是非阻塞socket 也不是介绍socket API的用法 取而代替的是让你感受实际工作中的代码编写 虽然很简陋 但你可以通过man手册与其它资源非富你的代码 请注意本教程所说的主题 如果细说 内容可以达到一本书内容 你会发

随机推荐

  • Vue + Spring Boot 项目实战项目简介

    参考https learner blog csdn net article details 88925013 githubhttps github com Antabot White Jotter
  • Java使用Spire.Pdf实现PDF添加图片水印

    通过本文你将学到 Spire Pdf是什么 如何在项目中引入Spire Pdf依赖 项目中基于Spire Pdf实现PDF添加图片水印 一 Spire Pdf是什么 1 Spire Pdf是成都冰蓝科技有限公司开发的一款简单易用 功能强大的
  • 雨课堂 文件和磁盘练习(1)

    若某文件系统索引结点 inode 中有直接地址项和间接地址项 与单个文件长度有关的因素是 间接地址索引的级数 地址项的个数 文件块大 与单个文件长度无关的因素是 索引结点的总数 相关解释 如果系统中有1000个 索引结点 说明有1000个物
  • 面试官:为什么Vue中的v-if和v-for不建议一起用?

    一 作用 v if 指令用于条件性地渲染一块内容 这块内容只会在指令的表达式返回 true值的时候被渲染 v for 指令基于一个数组来渲染一个列表 v for 指令需要使用 item in items 形式的特殊语法 其中 items 是
  • PID温控实验平台搭建(四)——PID温控系统实验代码讲解

    PID温控实验平台搭建 一 PID基础知识介绍 二 PID进阶知识介绍及源码分享 三 从零开始搭建STM32温控实验平台 四 PID温控系统代码讲解 五 最终实验现象与总结 文章目录 前言 一 主程序功能描述 二 部分代码讲解 1 PID程
  • 一种基于 TrustZone 的内生可信执行环境构建方法

    摘要 针对安全模块扩展技术面临的安全风险以及性能较低的问题 提出了一种基于TrustZone技术构建内生可信执行环境的方法 重点研究了计算资源隔离分配 固件可信度量 安全存储 全信任链构建等关键技术 设计了内生可信执行环境系统结构与可信计算
  • Laplace近似积分

    拉普拉斯方法又称为拉普拉斯近似 Laplace Approximation 它可以用来计算一元或多元积分 这里的思想类似于上篇博客中所讲的使用Laplace近似分布一样 这里把
  • c/c++中读入字符串(包含空格)

    1 scanf函数 scanf函数一般格式为scanf s st 但scanf默认回车和空格是输入不同组之间的间隔和结束符号 所以输入带空格 tab或者回车的字符串是不可以的 解决方法如下 利用格式符 它的作用为扫描字符集合 Scanf c
  • 【Transformer】21、AdaViT: Adaptive Tokens for Efficient Vision Transformer

    文章目录 一 背景 二 方法 三 效果 一 背景 Transformer 在多个任务上都取得了亮眼的表现 在计算机视觉中 一般是对输入图像切分成多个 patch 然后计算 patch 之间的自注意力实现下游任务 但由于自注意力机制的计算量是
  • ajax跨域session失效,request跨域获取session失效

    如下代码 传到另外一个域名就获取不到session了 localhost 12402 Home Index Session MemberUser 123456 string url api Home GetTop8FileDown stri
  • 【算法】直接插入排序解析

    活动地址 CSDN21天学习挑战赛 作者 柒号华仔 个人主页 欢迎访问我的主页 个人信条 星光不问赶路人 岁月不负有心人 个人方向 主要方向为5G 同时兼顾其他网络协议 编解码协议 C C linux 云原生等 感兴趣的小伙伴可以关注我 一
  • 正则表达式分割图片和匹配图片正则

    正则表达式分割图片 正则表达式 又称规则表达式 英语 Regular Expression 在代码中常简写为regex regexp或RE 计算机科学的一个概念 正则表达式通常被用来检索 替换那些符合某个模式 规则 的文本 正则表达式是对字
  • Centos7一点心德

    1 在centos7中发现没有table键补全的命令 需要手动去输出 效率不高 但是yum提供了自动补的命令安装 root localhost systemctl restart htt 死活补全不了 常用的命令还可以接受 碰到不常用的命令
  • 【大数据之Kafka】十三、Kafka消费者生产经验之分区的分配及再平衡、数据积压和消费者事务

    1 分区的分配及再平衡 一个consumer group中有多个consumer组成 一个 topic有多个partition组成 使用分区分配策略决定由哪个consumer来消费哪个partition的数据 Kafka有四种主流的分区分配
  • 增加tomcat并发量

    之前在做tomcat的最大并发量测试 现总结如下 tomcat默认的连接是线程阻塞的 即protocol配置为 HTTP 1 1 那么tomcat的最大连接数就受maxThreads和account的限制 maxThreads是最大的线程数
  • Altium Designer 19.1.18 - 将修改后的 PCB 封装更新到当前 PCB 中

    文章目录 将修改后的 PCB 封装更新到当前 PCB 中 参考 将修改后的 PCB 封装更新到当前 PCB 中 打开 PCB Library 选择需要更新到当前 PCB 中的 PCB 封装 使用鼠标右键选择 Update PCB With
  • 一文详解微服务架构

    要理解微服务 首先要先理解不是微服务的那些 通常跟微服务相对的是单体应用 即将所有功能都打包成在一个独立单元的应用程序 从单体应用到微服务并不是一蹴而就的 这是一个逐渐演变的过程 本文将以一个网上超市应用为例来说明这一过程 最初的需求 几年
  • 火柴棒等式 C语言

    Problem Description 给你n根火柴棍 你可以拼出多少个形如 A B C 的等式 等式中的A B C是用火柴棍拼出的整数 若该数非零 则最高位不能是0 用火柴棍拼数字0 9的拼法如图所示 注意 1 加号与等号各自需要两根火柴
  • 微信中发现的问题,做一分析(见同目录其他文)

    1 基本语法 这包括static final transient等关键字的作用 foreach循环的原理等等 今天面试我问你static关键字有哪些作用 如果你答出static修饰变量 修饰方法我会认为你合格 答出静态块 我会认为你不错 答
  • linux 网络编程---->多路复用:select实例!

    好吧 我承认找了好久 网上都没有像样的完整的实例 然后自己参照书自己写一个吧 gt server 端代码 gt server c include