linux非阻塞socket教程

2023-11-17

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

 

本教程内容如下:

 

        1. 改变一个阻塞的socket为非阻塞模式。

        2. select模型

        3. FD宏

        4. 读写函数

        5. 写一个非阻塞socket代码片

        6. 整个代码

        7.下一步

 

        如果你在此有许多问题,那么恭喜你,在初级阶段,任何人都没有剥夺你发现问题的权利。关于你所发现的问题,请不要犹豫email我。

        你可以自由发表本教程到任何WWW或FTP网部,但无论如何也要保持原教程的原型。这样我将会非常感谢你。

 

1.改变一个阻塞的socket为非阻塞模式

 

        简单的几行代码就可以创建一个socket 并连接,看起来如此简单。(你可以自己加入错误处理)

  1. s = socket(AF_INET, SOCK_STREAM, 0);  
  2. memset(&sin, 0, sizeof(struct sockaddr_in));  
  3. sin.sin_family = AF_INET;  
  4. sin.sin_port = htons(port);  
  5. sin.sin_addr.s_addr = inet_addr(hstname);  
  6. if(sin.sin_addr.s_addr == INADDR_NONE) {  
  7. connect(s, (struct sockaddr *)&sin, sizeof(sin))  

 

        有很多种方法可以设置socket为非阻塞模式, 我在unix下常用的方法如下:

  1. int x;  
  2. x=fcntl(s,F_GETFL,0);  
  3. fcntl(s,F_SETFL,x | O_NONBLOCK);  

 

        到现在为此, 这个socket已为非阻塞模式了,我们可以把焦点放在如何用它了。 但是在接着写代码之前,我们要看看我们将要用到的命令。

 

2.选择模型

 

        select这个方法用来检测一个socket是否有数据到来或是是否有准备好的数据要发送。声明如下:

  1. select(s, &read_flags, &write_flags, &exec_flags, timer);  

 

s                               socket的句柄

read_flags                读描述字集合。检查socket上是否有数据可读。

write_flags               写描述字集合。检查socket上是否已有数据可发送。

exec_flags                错误描述字集合。(本教程这儿不介绍)

timer                         等待某个条件为真时超时时间。

 

在本教程中,我们将如下用:

  1. select(s, &read_flags, &write_flags, NULL, timer);  

exec_flags参数设为null,因为在我们的程序中我们不需要关心exec事件。(解释它,超出了本教程的范围)

 

3.FD宏

 

        在select方法中的描述字集合是用来检测socket上发生的读取事件的,它用法如下:

  1. FD_ZERO(s, &write_flags)      sets all associated flags  
  2.                               in the socket to 0  
  3. FD_SET(s, &write_flags)       used to set a socket for checking  
  4. FD_CLR (s, &write_flags)      used to clear a socket from  being checked  
  5. FD_ISSET(s, &write_flags)     used to query as to if the socket is ready  
  6.                               for reading or writing.  

 

        如何用,我们在后面的代码中展示。

 

4.读取方法

 

       你应知道如何用下面的两个方法,但是在非阻塞模式下,它们的用法有一点点不同。

  1. write(s,buffer,sizeof(buffer))   send the text in "buffer"  
  2. read(s,buffer,sizeof(buffer))    read available data into "buffer"  

 

        当一个socket用非阻塞模式时,当调用这两个方法的时候,它们将立即返回,比如,如果没有数据的时候,它们将不会阻塞等待数据,而是返回一个错误。从现在开始,我们就要看代码了。

 

5.写一个非阻塞socket代码片

   

       首先声明要用到的变量:

  1. fd_set read_flags,write_flags; // the flag sets to be used  
  2. struct timeval waitd;          // the max wait time for an event  
  3. char buffer[8196];             // input holding buffer  
  4. int stat;                      // holds return value for select();  

       

        我们程序运行的大部份时间都花费在不断调用select(它将花费我们大部份CPU时间),至到有数据准备好读或取。此时,timer就体现了它的意义。它决定select将等待多久。下面就是用法,请仔细分析:

  1. // Insert Code to create a socket  
  2.   
  3. while(1) // put program in an infinite loop of reading and writing data  
  4.  {  
  5.   waitd.tv_sec = 1;  // Make select wait up to 1 second for data  
  6.   waitd.tv_usec = 0; // and 0 milliseconds.  
  7.   
  8.   FD_ZERO(&read_flags); // Zero the flags ready for using  
  9.   FD_ZERO(&write_flags);  
  10.   
  11.   // Set the sockets read flag, so when select is called it examines  
  12.   // the read status of available data.  
  13.   FD_SET(thefd, &read_flags);  
  14.                                       
  15.   // If there is data in the output buffer to be sent then we  
  16.   // need to also set the write flag to check the write status  
  17.   // of the socket  
  18.   if(strlen(outbuff)!=0) FD_SET(thefd, &write_flags);  
  19.   
  20.   // Now call select  
  21.   stat=select(s+1, &read_flags,&write_flags,(fd_set*)0,&waitv);  
  22.   if(stat < 0) {  // If select breaks then pause for 5 seconds  
  23.      sleep(5);    // then continue  
  24.      continue;  
  25.      }  
  26.   // Now select will have modified the flag sets to tell us  
  27.   // what actions can be performed  
  28.   
  29.   // Check if data is available to read  
  30.   if (FD_ISSET(thefd, &read_flags)) {  
  31.     FD_CLR(thefd, &read_flags);  
  32.     // here is where you use the read().  
  33.     // If read returns an error then the socket  
  34.     // must be dead so you must close it.  
  35.     }  
  36.   
  37.   //Check if the socket is prepared to accept data  
  38.   if (FD_ISSET(thefd, &write_flags)) {  
  39.     FD_CLR(thefd, &write_flags);  
  40.     // this means the socket is ready for you to use write()  
  41.     }  
  42.   
  43.   // Now here you can put in any of the precedures that you want  
  44.   // to happen every 1 second or so.  
  45.   
  46.   // now the loop repeats over again  

 

补充:请确保只有在你有数据发送的情况下才设置write_flag这个描述字集合,因为socket一量创建总是可写的。也就是说,如果你设置了这个参数,select将不会等待,而是马上返回并一直循环,它将抢占CPU99%的利用率,这是不允许的。

 

6.整个代码

 

      最后利用我们所学,写一个简单的客户端。当然用非阻塞模式写一个客户端有点大采小用,这儿我们只是为了展示用法。更多示例请看第7节内容。

 

  1. #include <sys/types.h>  
  2. #include <sys/socket.h>  
  3. #include <sys/time.h>  
  4. #include <netinet/in.h>  
  5. #include <netdb.h>  
  6. #include <stdio.h>  
  7. #include <string.h>  
  8. #include <unistd.h>  
  9. #include <stdlib.h>  
  10. #include <fcntl.h>  
  11.   
  12. // this routine simply converts the address into an  
  13. // internet ip  
  14. unsigned long name_resolve(char *host_name)  
  15. {  
  16. struct in_addr addr;  
  17. struct hostent *host_ent;  
  18.   if((addr.s_addr=inet_addr(host_name))==(unsigned)-1) {  
  19.     host_ent=gethostbyname(host_name);  
  20.     if(host_ent==NULL) return(-1);  
  21.     memcpy(host_ent->h_addr, (char *)&addr.s_addr, host_ent->h_length);  
  22.     }  
  23.   return (addr.s_addr);  
  24. }  
  25.   
  26. // The connect routine including the command to set  
  27. // the socket non-blocking.  
  28. int doconnect(char *address, int port)  
  29. {  
  30. int x,s;  
  31. struct sockaddr_in sin;  
  32.   
  33.   s=socket(AF_INET, SOCK_STREAM, 0);  
  34.   x=fcntl(s,F_GETFL,0);              // Get socket flags  
  35.   fcntl(s,F_SETFL,x | O_NONBLOCK);   // Add non-blocking flag  
  36.   memset(&sin, 0, sizeof(struct sockaddr_in));  
  37.   sin.sin_family=AF_INET;  
  38.   sin.sin_port=htons(port);  
  39.   sin.sin_addr.s_addr=name_resolve(address);  
  40.   if(sin.sin_addr.s_addr==NULL) return(-1);  
  41.   printf("ip: %s/n",inet_ntoa(sin.sin_addr));  
  42.   x=connect(s, (struct sockaddr *)&sin, sizeof(sin));  
  43.   if(x<0) return(-1);  
  44. return(s);  
  45. }  
  46.   
  47. int main (void)  
  48. {  
  49. fd_set read_flags,write_flags; // you know what these are  
  50. struct timeval waitd;            
  51. int thefd;             // The socket  
  52. char outbuff[512];     // Buffer to hold outgoing data  
  53. char inbuff[512];      // Buffer to read incoming data into  
  54. int err;           // holds return values  
  55.   
  56.   memset(&outbuff,0,sizeof(outbuff)); // memset used for portability  
  57.   thefd=doconnect("203.1.1.1",79); // Connect to the finger port  
  58.   if(thefd==-1) {  
  59.     printf("Could not connect to finger server/n");  
  60.     exit(0);  
  61.     }  
  62.   strcat(outbuff,"jarjam/n"); //Add the string jarjam to the output  
  63.                               //buffer  
  64.   while(1) {  
  65.     waitd.tv_sec = 1;     // Make select wait up to 1 second for data  
  66.     waitd.tv_usec = 0;    // and 0 milliseconds.  
  67.     FD_ZERO(&read_flags); // Zero the flags ready for using  
  68.     FD_ZERO(&write_flags);  
  69.     FD_SET(thefd, &read_flags);  
  70.     if(strlen(outbuff)!=0) FD_SET(thefd, &write_flags);  
  71.     err=select(thefd+1, &read_flags,&write_flags,  
  72.                (fd_set*)0,&waitd);  
  73.     if(err < 0) continue;  
  74.     if(FD_ISSET(thefd, &read_flags)) { //Socket ready for reading  
  75.       FD_CLR(thefd, &read_flags);  
  76.       memset(&inbuff,0,sizeof(inbuff));  
  77.       if (read(thefd, inbuff, sizeof(inbuff)-1) <= 0) {  
  78.         close(thefd);  
  79.         break;  
  80.         }  
  81.       else printf("%s",inbuff);  
  82.       }  
  83.     if(FD_ISSET(thefd, &write_flags)) { //Socket ready for writing  
  84.       FD_CLR(thefd, &write_flags);  
  85.       write(thefd,outbuff,strlen(outbuff));  
  86.       memset(&outbuff,0,sizeof(outbuff));  
  87.       }  
  88.     // now the loop repeats over again  
  89.     }  
  90. }  

 

7.下一步

 

        其它更多的示例代码从此教程中分离,以zip文件的方式给出。为了更好的理解所学, 你最好参考一些结构更复杂,技术更强的代码:

 http://users.cybernex.net.au/jj/sock.zip


原文链接: http://blog.csdn.net/favormm/article/details/5296621




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

linux非阻塞socket教程 的相关文章

  • 【Linux学习】epoll详解

    什么是epoll epoll是什么 按照man手册的说法 是为处理大批量句柄而作了改进的poll 当然 这不是2 6内核才有的 它是在2 5 44内核中被引进的 epoll 4 is a new API introduced in Linu
  • 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地址 路由
  • webbench剖析

    webbench 其为linux上一款web性能压力测试工具 它最多可以模拟3万个并发连接数来测试服务器压力 其原理为fork多个子进程 每个子进程都循环做web访问测试 子进程将访问的结果通过管道告诉父进程 父进程做最终结果统计 其主要原
  • 【Linux网络(C++)】——网络套接字(TCP/UDP编程模型)多进程,多线程,线程池服务器开发(画图解析)

    目录 一 套接字基本概念 IP地址 TCP和UDP协议 端口号 端口号vs 进程pid 网络字节序 本地字节序转换成网络字节序 网络字节序转换为本地字节序 二 套接字的基本操作 socket的创建 域 domain 类型 type 协议 P
  • 高并发的epoll+线程池,业务在线程池内

    epoll是linux下高并发服务器的完美方案 因为是基于事件触发的 所以比select快的不只是一个数量级 单线程epoll 触发量可达到15000 但是加上业务后 因为大多数业务都与数据库打交道 所以就会存在阻塞的情况 这个时候就必须用
  • 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交互细节
  • 基于epoll的聊天室程序

    epoll相对于poll和select这两个多路复用的I O模型更加的高效 epoll的函数很简单 麻烦的地方在于水平出发和边沿触发 用张图来说明下 ET 边沿 只是在状态反转时触发 比如从不可读到可读 而LT 水平 就是如果可读 就会一直
  • Linux网络编程一步一步学 - 目录(汇总)zhuan

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

    什么是Socket Socket接口是TCP IP网络的API Socket接口定义了许多函数或例程 程式员能够用他们来研发TCP IP网络上的应用程式 要学Internet上的TCP IP网络编程 必须理解Socket接口 Socket接
  • 【网络自定向下的学习】——TCP3次握手和4次挥手详解

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

    B S通信简述 整个计算机网络的实现体现为协议的实现 TCP IP协议是Internet的核心协议 HTTP协议是比TCP更高层次的应用层协议 HTTP HyperText Transfer Protocol 超文本传输协议 是互联网上应用
  • linux非阻塞socket教程

    本文并非解释什么是非阻塞socket 也不是介绍socket API的用法 取而代替的是让你感受实际工作中的代码编写 虽然很简陋 但你可以通过man手册与其它资源非富你的代码 请注意本教程所说的主题 如果细说 内容可以达到一本书内容 你会发
  • 【IP协议(一)】——IP数据报格式及其含义,IP数据报的切分

    个人主页 努力学习的少年 版权 本文由 努力学习的少年 原创 在CSDN首发 需要转载请联系博主 如果文章对你有帮助 欢迎关注 点赞 收藏 一键三连 和订阅专栏哦 IP数据报格式 版本 占4位 指ip协议的版本 首部长度 表示IP数据报中报
  • 【网络自定向下学习】——TCP报文段的详细解析

    个人主页 努力学习的少年 版权 本文由 努力学习的少年 原创 在CSDN首发 需要转载请联系博主 如果文章对你有帮助 欢迎关注 点赞 收藏 一键三连 和订阅专栏哦 目录 一 Tcp报文段的结构 二 首部长度 三 窗口大小 四 序列号和确认序
  • poll()函数详解

    poll提供的功能与select类似 不过在处理流设备时 它能够提供额外的信息 include
  • Http协议详解

    引入 超文本传输协议 HTTP HyperText Transfer Protocol 是互联网上应用最为广泛的一种网络协议 所有的WWW文件都必须遵守这个标准 设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法 1960年美

随机推荐

  • [现代控制理论]5_系统的可控性_controllability

    现代控制理论 11 现代控制理论串讲 完结 pdf获取 现代控制理论 10 可观测性与分离原理 观测器与控制器 现代控制理论 9 状态观测器设计 龙伯格观测器 现代控制理论 8 5 线性控制器设计 轨迹跟踪simulink 现代控制理论 8
  • Ubuntu20.04如何卸载软件

    自此换了Ubuntu系统 很多用起来特别不顺手 例如现在删个软件 window系统右击强制删除就完了 现在都不知道咋整 gt lt 特此整理了如下几种情况 希望能帮到跟我一样状况的小伙伴O O 手动卸载 点击 设置 应用程序 选中自己要卸载
  • 手把手带你撸zookeeper源码系列目录

    手把手带你撸zookeeper源码 前言 手把手带你撸zookeeper源码 配置文件 手把手带你撸zookeeper源码 如何启动一个zookeeper服务 手把手带你撸zookeeper源码 从源码角度分析zookeeper启动时都做了
  • Linux系统之Centos安装epel源

    Linux系统之Centos安装epel源 一 检查本地系统环境 1 检查系统版本 2 检查系统内核版本 二 检查yum仓库 1 查看repo文件 2 检查yum仓库的状态 三 安装epel源 1 查看系统epel release noar
  • 【论文笔记_目标检测_2022】Cross Domain Object Detection by Target-Perceived Dual Branch Distillation

    基于目标感知双分支提取的跨域目标检测 摘要 在野外 跨领域目标检测是一项现实而具有挑战性的任务 由于数据分布的巨大变化和目标域中缺乏实例级注释 它的性能会下降 现有的方法主要关注这两个困难中的任何一个 即使它们在跨域对象检测中紧密耦合 为了
  • arcgis不闭合线转面_ArcGIS不闭合线转面

    ArcGIS不闭合线转面 1 打开ArcMap用Add Data加载shp Polyline线文件 2 选Editor编辑 Start Editing开始编辑 3 选Editor编辑 More Editing Tools Topology拓
  • java:hashMap: get(null)引发的对其数据结构具体形态的思考

    ref 原文 https blog csdn net fenglongmiao article details 79656198 note 我们知道HashMap集合是允许存放null值的 hashMap是根据key的hashCode来寻找
  • 软工导论知识框架(五)面向对象方法学

    传统软件工程方法学适用于中小型软件产品开发 面向对象软件工程方法学适用于大型软件产品开发 一 四要素 对象 类 继承 传递消息实现通信 二 概念 1 对象 具有相同状态的一组操作的集合 对状态和操作的封装 2 类 对具有相同状态和相同操作的
  • 带你手写基于 Spring 的可插拔式 RPC 框架(三)通信协议模块

    在写代码之前我们先要想清楚几个问题 我们的框架到底要实现什么功能 我们要实现一个远程调用的 RPC 协议 最终实现效果是什么样的 我们能像调用本地服务一样调用远程的服务 怎样实现上面的效果 前面几章已经给大家说了 使用动态代理 在客户端生成
  • CentOS7中安装mysql

    1 确保本机的mysql已经卸载干净 需要将mariadb和mysql全部卸载 rpm qa grep i mariadb rpm qa grep i mysql 使用rpm ev nodeps 命令将查询出来的文件逐一卸载 sudo rp
  • Docker Compose、Docker Swarm (docker进阶 狂神)

    文章目录 Docker Compose 安装 开源项目 博客 实战 自己编写微服务上线 Docker Swarm 四台机器安装docker环境 Swarm集群搭建 Raft协议 体会 灰度发布 金丝雀发布 其他命令学习方式 Docker C
  • notepad++以16进制查看文件

    1 Notepad 可以编辑PE文件 二进制文件即HEX码 2进制 16进制都可以 通过附加的组件HexEditor即可实现 另外一款Notepad 自带插件TextFX也有这个功能 但实现效果不如Hex Editor 下载地址 https
  • CH1-绪论

    文章目录 算法时间复杂度的计算 一 冒泡排序简介 从小到大排序 算法时间复杂度的计算 我们一般只关心随着问题规模n趋于无穷时 函数中对函数结果影响最大的项 比如说 T n 3n 3 当n非常大的时候 常数3和n的系数3对函数结果的影响就很小
  • vue--综合组件间的通信

    二 综合组件之间的通信 实现一个ToDoList 完成所有的组件的创建和使用 add点击add按钮时候 将用户输入的内容 todoinput 显示在 todolist 核心代码 兄弟组件间通信 步骤1 var bus new Vue 步骤2
  • QT210烧写UBOOT到SD卡原理以及UBOOT启动

    原文地址 http blog csdn net shushi0123 article details 8018998 世界早已进入cortex a8了 我也得跟进一下所以买了QT210的开发板 长话短说开始搞SD卡烧写UBOOT 从SD启动
  • TCP选项之SO_LINGER

    SO LINGER这个选项在我以前带队改造haproxy的时候引出过一个reset RST 客户端连接的bug SO LINGER作用 设置函数close 关闭TCP连接时的行为 缺省close 的行为是 如果有数据残留在socket发送缓
  • 手动实现 call、apply、bind

    手动实现 call apply bind 改变 this的指向 就是将函数fn放入传入的context中 然后执行context fn 此时的fn中的this就变成了context 在函数执行完毕之后 需删除context中的fn call
  • 腾讯云2核4G服务器性能如何?能安装几个网站?

    腾讯云2核4G服务器能安装多少个网站 2核4g配置能承载多少个网站 一台2核4G服务器可以安装多少个网站 阿腾云2核4G5M带宽服务器目前安装了14个网站 从技术角度是没有限制的 只要云服务器性能够用 想安装几个网站就安装几个网站 但是从公
  • Windows系统下安装Ubuntu子系统

    总共分三步 1 网上是有Windows 10版本的安装教程 链接如下 14条消息 Windows系统中安装ubutu子系统 惜洛 Jankin的博客 CSDN博客 2 补充Windows 11版本的安装 大同小异 3 如果出现报错 14条消
  • linux非阻塞socket教程

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