本地socket的select用法

2023-05-16

    学习socket的select用法,下面代码大部分是从网上借鉴的,只不过把网络部分的socket改为了本地socket,并加了一些自己测试用的语句,如果有冒犯之处请联系我,我立马删除,谢谢。

client端

#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>
#include <sys/un.h>

#include <sys/time.h>

#define MAXLINE 5
#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;
    int byte_size;
    int i;   
  
    int nRet;
    struct timeval tmout;
    tmout.tv_sec = 0;
    tmout.tv_usec = 0;


    while( 1)
    {

       FD_ZERO(&rset);

       FD_SET(fileno( fp ), &rset );
       FD_SET(connfd, &rset);         //!> 注意不要把rset看作是简单的一个变量
                                    //!> 注意它其实是可以包含一组套接字的哦,
                                    //!> 相当于是封装的数组!每次都要是新的哦!
 
   
       if( select(maxfd, &rset, NULL, NULL, &tmout ) == -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("Recvok...\n");            break;
          }
          else if( n== -1 )
          {
             printf("Recverror...\n");
             break;
          }
          else
          {
             lens =strlen( recv );
             recv[lens] ='\0';
            //!> 写到stdout
             write(STDOUT_FILENO, recv, MAXLINE );
            printf("\n");
            byte_size = ( strlen(recv) ) * sizeof(char);
            printf("Client recv bytes : %d\n",byte_size);
          }

       }
      
      //!> 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;
             }
          for(i=0; i<5; i++) //测试,连续发五次,看server的返回情况
	   {
             printf("Client send : %s\n", send);
             byte_size = ( strlen(send) ) * sizeof(char);
	     printf("Client send bytes : %d\n",byte_size);
             write(connfd, send, strlen( send ));
 
	   } 
             memset(send, 0, sizeof( send ) );

	 }
       }
      
    }
   
}

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

   //!> 套接字信息
  // bzero(&servaddr, sizeof(servaddr));
   servaddr.sun_family = AF_UNIX;
   strcpy(servaddr.sun_path,"selectfile");
  // 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;
}
    这个代码有个地方需要注意:send[lens-1]= '\0';这一句让我测试的时候吃了点苦头,我定义的数据大小为5字节,但我每次发送3字节数据的时候,server端可以正常返回,但发送4字节或5字节的时候,server只返回前三个字节,举个例子,我输入123,client会连续发送五次123,server也给返回五次123,;如果我输入12345,client发送五次123,server返回五次123,但马上我又按下回车,这时client会先连续发五次5,然后再发五次0,server端给回五个5,却不是分五次回(两次、三次?次数不一定,很是郁闷),我认为这是缓冲区堆积的原因导致的(若果认识错误请指摘,谢谢)。

server端

#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>
#include <sys/un.h> 

#include <sys/time.h>

#define BUF_LEN 3
#define SERV_PORT 6000
#define FD_SIZE 100
#define MAX_BACK 100
#define SOC_FD "selectfile"

int main( int argc, char ** argv )
{
 
   unlink(SOC_FD);
   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_un servaddr, chiaddr;
 
    int j,nRet;

    struct timeval tmout;
    tmout.tv_sec = 0;
    tmout.tv_usec = 0;

  
    if( (listenfd = socket( AF_UNIX, SOCK_STREAM, 0 ) ) == -1 )
    {
       printf("Create socket Error : %d\n", errno );
       exit(EXIT_FAILURE );
    }
   
   //!>
   //!> 下面是接口信息
   // bzero(&servaddr, sizeof( servaddr ) );
   servaddr.sun_family = AF_UNIX;
   strcpy(servaddr.sun_path, SOC_FD);  
   //servaddr.sin_addr.s_addr  =htonl( INADDR_ANY);
  // servaddr.sin_port = htons( SERV_PORT );
   
   //!>
   //!> 绑定
    if( bind(listenfd, ( struct sockaddr * )&servaddr, sizeof(servaddr ) ) == -1 )
   {   
       printf("BindError : %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, &tmout )) ==-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( "Tomany ... " );
               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 ));   //!> 此步重要,不要有时候出错
 
	         sleep(1);            
                 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("nodata\n");
                   close(sockfd );         //!> 说明在这个请求端口上读完了!
                   FD_CLR(sockfd, &allset );
                   client[i] =-1;
                  continue;
                }
    
		  buf[n]= '\0';
             	  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);      //!> 读出来的写进去
              	 printf("\n");
	        
                 memset( buf,0, sizeof( buf ));   //!> 此步重要,不要有时候出错
	
             }

          }
       }
      
    }
  
    return 0;
}

    server端也有改动:sleep(1);有人会说,select的用法就是要让消息立马返回,不阻塞线程,但这里是因为我的这个小demo在client端有个for循环(连续发送五次数据),上面也说了,当发送快于接收,缓冲区堆积,还有,网络(本地感觉也一样,可以把server端看作是一种网络服务端,总感觉这么理解怪怪的,希望有高手能告诉我一下应该怎么解释比较正确)写函数是不负责将数据全部写完之后再返回的。基于以上两点,我就加了sleep,如果不加,client连续发五次消息(假设是123),server会给回5个123,但不一定是分五次回,有可能是两次、三次或五次等。

    最后,推荐一份资料,《Linux程序设计(第4版)》,其实第几版无所谓,内容差不多,这本资料的第15章套接字,里面很详细的介绍了socket的用法,建议想要了解socket的兄弟们去看下,最后的最后,欢迎各路大神指摘我的博客,有错改之才能进步,谢谢。

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

本地socket的select用法 的相关文章

  • 如何考虑权重随机选择一行?

    我有一张看起来像这样的桌子 id primary key content varchar weight int 我想要做的是从该表中随机选择一行 但要考虑到重量 例如 如果我有 3 行 id content weight 1 some co
  • 在今天和昨天之间选择

    我正在尝试在两个日期之间进行选择 如下所示 SELECT p Code p Name sum h PA 1 AS PA sum h PB 1 AS PB sum h PG 1 AS PG sum h GoedkeuringDoorNew G
  • SQL Server 的 SELECT JOIN 语句导致的死锁

    当执行带有两个表的 JOIN 的 SELECT 语句时 SQL Server 似乎 分别锁定语句的两个表 例如通过像这样的查询 这 SELECT FROM table1 LEFT JOIN table2 ON table1 id table
  • 从单个表中选择同一列两次但条件不同

    我想显示员工的姓名和号码以及老板的号码和姓名 如下所示 只有一张表 到目前为止我尝试过 SELECT ID Name Boss SELECT Name FROM Employees WHERE ID IN SELECT Boss FROM
  • 从两个表中选择最大值、最小值

    我有两张桌子 不同之处在于 归档是一个表 另一个保存当前记录 这些是记录公司销售额的表格 在这两个字段中 我们都有其他字段 id 名称 销售价格 我需要从两个表中选择给定名称的最高价格和最低价格 我尝试处理查询 select name ma
  • 如何根据多个字段选择不同的行

    我有一个表 其中包含 MSSQL 数据库中一系列事件的数据 ID Name Date Location Owner 1 Seminar Name 1 2013 08 08 A Location Name
  • 如何在选择查询中创建新列

    在 MS Access 中 我想将新列插入到选择查询的返回结果中 新列的每一行都具有相同的值 例如 我的选择返回列 A B 我希望 C 成为选择查询创建的新列 A B C a1 b1 c a2 b2 c a3 b3 c select A B
  • MySQL 返回用户排名最高的事件

    我目前使用以下查询来获取每个用户的详细信息 SELECT u sums total votes sums no of events FROM user u LEFT JOIN SELECT us user uid count ev even
  • 熊猫选择倒数第二列,这也不是 nan

    我已尽可能多地清理数据并在 Pandas 数据框中读取它们 所以问题是不同的文件有不同的列数 但它总是倒数第二个非纳米列是我想要的 那么有什么办法可以把它们挑出来吗 这是数据的示例 f g h l 0 39994 29 568 29 569
  • 将 HTML 选择元素转换为带有子菜单的树

    我想让一个选择元素有一个树形的子菜单 我希望它是这样的 source colinear com http www colinear com rmenu gif 有没有一个 jQuery 插件可以将 select 元素变成这种东西 这里有树插
  • 为什么 peewee 将“id”列包含到 mysql select 查询中?

    我正在尝试学习如何将 peewee 与 mysql 一起使用 我在 mysql 服务器上有一个现有数据库和一个现有表 该表当前为空 我现在正在测试 gt gt gt db MySQLDatabase nhl user root passwd
  • MySQL ORDER BY rand(),名称 ASC

    我想获取一个包含 1000 个用户的数据库并随机选择 20 个用户 ORDER BY rand LIMIT 20 然后按名称对结果集进行排序 我想出了以下查询not像我希望的那样工作 SELECT FROM users WHERE 1 OR
  • PostgreSQL 选择结果大小

    我在 PostgreSQL 数据库中有一个表 并从该表中进行一些限制的选择 并且我想知道该选择有多少磁盘空间 我知道有一个postgres函数pg total relation size这给了我数据库中某个表的大小 但是如何找到 子表 的大
  • MySQL 中两个 Select 查询的结果相减

    我编写了两个 mysql 查询 一个获取一年中特定月份的总用户 注册 另一个获取一年中特定月份的活跃用户 我需要找到数量inactive当年的用户 为此 我正在考虑减去通过两个单独的查询获得的总用户数和活动用户列 以下是查询 1 Fetch
  • Linq:Select 和Where 之间有什么区别

    The Select and WhereLinq 中提供了方法 对于这两种方法 每个开发人员都应该了解什么 例如 何时使用其中一种而不是另一种 使用一种相对于另一种的优势等 Where 查找匹配的项目并仅返回匹配的项目 过滤 gt IEnu
  • Contenteditable - 从插入符号提取文本到元素末尾

    浏览完所有可能的问题和答案后 我会尝试这种方式 我正在编写 RTE 程序 但未能成功提取 contenteditable 元素中的文本 原因是 每个浏览器处理节点和按键 13 事件的方式略有不同 例如 一个浏览器创建 br 另一个浏览器创建
  • MySql JOINS 的优点/缺点

    当我从多个表中选择数据时 我经常使用 JOINS 最近我开始使用另一种方式 但我不确定从长远来看会产生什么影响 例子 SELECT FROM table 1 LEFT JOIN table 2 ON table 1 column table
  • VB SQL 语句未选择正确的行

    我试图使用 SELECT 语句在我的数据库中 选择 一个人 但它没有选择正确的人 我也不确定为什么 我正在使用访问数据库 数据库连接代码 Imports System Data OleDb Module Database Connectio
  • jQuery:如何仅根据表标题从表的列中选择值

    我有一个带有标题 ID 的表 我需要选择此标题下的所有字段 我无权访问源代码 并且该表中没有使用任何类 关于如何完成这件事有什么想法吗 要获取第一列 function var col td nth child 1
  • codeigniter,获取mysql表列中的最大值

    我正在使用 codeigniter 2 我有一个 mysql 表列 存储每个学生所用的时间 例如 1 2327 0 6547 1 9876 我想获得最大值 值该列 这是我的代码 this gt db gt select max time t

随机推荐

  • 21-《电子入门趣谈》第四章_自己制作电路板-4.2洞洞板的介绍和经典案例使用教程

    好消息 xff1a 请在手机淘宝或闲鱼上搜索 电子入门趣谈 xff0c 有惊喜哦 我把全本电子入门趣谈的电子版 xff08 包括科技提升和理论升华部分 xff0c 共计50余万字 xff09 放到上面开始兜售啦 xff0c 如果您真的喜欢这
  • vlc-添加自定义的demuxer解复用插件----播放h264裸文件

    使用vlc3 0 6 在ubuntu 64bit上编译 xff0c vlc使用插件的方式组织对多种视频源的支持 xff0c 比如 avi mp4 mkv 等等 xff0c 这里想添加一个自己的demuxer xff0c 从一个h 264文件
  • 进程管理(五)--linux进程内核栈

    在进程创建时 xff0c 内核会为进程创建一系列数据结构 xff0c 其中最重要的就是上章学习的task struct结构 xff0c 它就是进程描述符 xff0c 表明进程在生命周期内的所有特征 同时 xff0c 内核为进程创建两个栈 x
  • [802.11]IEEE 802.11认证方式介绍

    一 802 11认证方式 802 11有开放系统认证 xff08 open system authentication xff09 和共享密钥认证 xff08 shared keyauthentication xff09 两种方式 1 1
  • 对‘std::xxx’未定义的引用

    出现一大串 对 std xxx 未定义的引用 的原因 xff1a 对于gcc后缀文件 xff0c 编译的时候可以用gcc g 43 43 xff0c 但是链接的时候要用g 43 43 xff0c 因为gcc和g 43 43 在编译的时候是相
  • 快速傅里叶变换

    FFT xff0c 即为快速傅氏变换 xff0c 是离散傅氏变换的快速算法 xff0c 它是根据离散傅氏变换的奇 偶 虚 实等特性 xff0c 对离散傅立叶变换的算法进行改进获得的 它对傅氏变换的理论并没有新的发现 xff0c 但是对于在计
  • C++项目开发中的一些问题及解决记录

    1 std vector类使用 xff1a https blog csdn net weixin 41743247 article details 90635931 2 vector求和 xff1a include lt numeric g
  • win32和android 的cocos2dx环境搭建详细教程

    转载 请注明出处 xff1a http blog csdn net aa4790139 article details 8086635 详细搭建步骤如下 xff1a 1 Android 开发环境搭建 Android开发环境搭建不是重点 相信
  • 快速傅里叶变换在信号处理中的应用

    傅里叶变换FT xff08 Fourier Transform xff09 是一种将信号从时域变换到频域的变换形式 它在声学 信号处理等领域有广泛的应用 计算机处理信号的要求是 xff1a 在时域和频域都应该是离散的 xff0c 而且都应该
  • 卷积

    随着机器学习的逐渐升温 xff0c 卷积神经网络这个专业词汇也越来越多地出现在我们眼前 卷积神经网络是一种前馈神经网络 xff0c 包括一维 二维以及三维卷积神经网络 这篇文章我们先来学习了解一下卷积的概念 在泛函分析中 xff0c 卷积是
  • 二叉树基础知识总结

    现实生活当中 xff0c 我们每个家庭都会有一个家谱 xff0c 来罗列家庭成员的关系 例如父亲下面的分支里有儿子或者女儿 xff0c 而父亲又属于祖父祖母的下部分支 其实这个家谱在计算机科学中映射的就是树形的表示方法 可见在很久以前 xf
  • 物联网(Iot)台灯设计完整教程(长图文)

    现如今随着物联网的概念深入人心 xff0c 物联网的设备也越来越普及 xff0c 本篇文章介绍的就是一个物联网台灯的设计 该设计主要包含物联网芯片开发 微信客户端开发 后台服务器端开发以及三个组件之间互联等 xff0c 其总体设计逻辑框图如
  • SVN打标签方法及在此过程中的问题处理

    所谓的 打标签 xff0c 我个人的理解是 xff1a 项目取得了阶段性成果 xff0c 需要保存在标签 xff08 tags文件夹 xff09 中 xff0c 以备不时之需 我采用的打标签的方法是 xff1a 1 在SVN客户端打标签 前
  • STM32 CAN 过滤器、滤波屏蔽器配置总结

    http blog csdn net jixiangrurui article details 39370027 一 过滤组 过滤器编号介绍 在 STM32 互联型产品中 xff0c CAN1 和 CAN2 分享 28 个过滤器组 其它 S
  • Linux内核源码之自旋锁的实现

    1 Linux内核同步 Linux内核中有许多共享资源 xff0c 这些共享资源是内核中进程都有机会访问到的 内核对其中一些共享资源的访问是独占的 xff0c 因此需要提供机制对共享资源进行保护 xff0c 确保任意时刻只有一个进程在访问共
  • cmake编译c++程序

    当在Linux系统下编写程序时候 xff0c 如果没有类似于visual studio vs code等IDE 集成开发环境 时 xff0c 如何编译 运行程序呢 xff1f 一种方法是编写makefile文件 xff0c 用makefil
  • #if 0 /#if 1用法介绍(屏蔽代码块)

    当屏蔽掉大块代码时 xff0c 使用 34 if 0 34 比使用 34 34 要好 xff0c 因为用 34 34 做大段的注释要防止被注释掉的代码中有嵌套的 34 34 这会导致注释掉的代码区域不是你想要的范围 xff0c 当被注释掉的
  • 调试工具之UDP/TCP网络调试助手NetAssist

    参考文章列表 xff1a https blog csdn net zhzht19861011 article details 4545260 https blog csdn net qq 29350001 article details 7
  • idea第一次新建SpringBoot项目报错 Error:(3, 32) java: 程序包org.springframework.boot不存在

    跟着网上的教程第一次创建SpringBoot xff0c 创建好之后他们都可以直接写Controller类 xff0c 然后运行就能从localhost访问hello word了 但是我再第一次创建之后 xff0c 出现下面的情况 Erro
  • 本地socket的select用法

    学习socket的select用法 xff0c 下面代码大部分是从网上借鉴的 xff0c 只不过把网络部分的socket改为了本地socket xff0c 并加了一些自己测试用的语句 xff0c 如果有冒犯之处请联系我 xff0c 我立马删