C++网络编程快速入门(二):Linux下使用select演示简单服务端程序

目录

  • select参数解释
  • select使用规范
  • select使用缺点
  • 基本流程
  • 实例代码
  • 通信效果演示
  • 往期文章

select参数解释

extern int select (int __nfds, fd_set *__restrict __readfds,fd_set *__restrict __writefds,fd_set *__restrict __exceptfds,struct timeval *__restrict __timeout);

__nfds:一般设置为所有需要使用select函数检测时间的fd中的最大值+1
__readfds:需要监听可读事件的fd集合
__writefds:需要监听可写事件的fd集合
__exceptfds:需要监听可写事件的fd集合
__timeout:超时时间,在这个设定的时间内检测这些fd事件,超过这个超时时间,select将立即返回
fd_set这个结构体信息如下:

/* fd_set for select and pselect.  */
typedef struct{/* XPG4.2 requires this member name.  Otherwise avoid the namefrom the global namespace.  */
#ifdef __USE_XOPEN__fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];
# define __FDS_BITS(set) ((set)->fds_bits)
#else__fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS];
# define __FDS_BITS(set) ((set)->__fds_bits)
#endif} fd_set;

可以简化看做:

typedef struct {long int __fds_bits[16];	
} fd_set;

long int 占8字节,也就是8 * 8 = 64个bit,所以__fds_bits数组总共就占64 * 16 = 1024个fd状态。每个bit位0表示无事件,1表示有事件。

select使用规范

1、select在调用前后可能会修改readfdswritefdsexceptfds中的内容,所以如果在下次调用时复用这些fd_set,则要在下次调用前使用FD_ZEROfd_set清空,然后调用FD_SET将要检测的fd添加到fd_set中
2、linux系统下select函数也会修改timeval结构体的值,所以想要复用也必须给其重新设置值
3、select函数的timeval结构体中的tv_sectv_usec如果都被设置为0,代表着检测事件的总时间被设置为0,行为就变成了select检测相关集合中的fd,如果没有需要的事件,则立即返回
4、如果select函数的timeval参数设置为NULL,则select会一直阻塞下去,直到我们需要的事件被触发
5、Linux下,select函数第一个参数必须设置为需要检测事件fd中最大值+1,所以每次产生一个新fd都需要和maxfd作比较

select使用缺点

1、select函数需要将fd集合从用户态拷贝到内核态,在fd较多时开销较大。并且每次检测时也是在内核中遍历这个fd_set。
2、单个进程能够监视的文件描述符数量上存在最大限制,linux上我们算过了,只有1024个
3、select函数每次调用之前都要对传入的参数重新设定,比较麻烦
4、在linux上select函数的实现原理是底层的poll函数,所以select和poll本质上没有区别

基本流程

在这里插入图片描述

实例代码

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <iostream>
#include <sys/time.h>
#include <vector>
#include <errno.h>
#include <stdio.h>
#include <string.h>
using namespace std;
int main() {// 创建一个监听socketint listenfd = socket(AF_INET, SOCK_STREAM, 0);if (listenfd == -1) {cout << "create listen error" << endl;return -1;}// 初始化服务器地址struct sockaddr_in bindaddr;bindaddr.sin_family = AF_INET;bindaddr.sin_addr.s_add当断开各个客户端时,服务端的select函数对各个客户端fd进行检测时,仍然会触发可读事件r = htonl(INADDR_ANY);bindaddr.sin_port = htons(3000);if (bind(listenfd, (struct sockaddr *)& bindaddr, sizeof(bindaddr)) == -1) {cout << "bind listen socket error" << endl;close(listenfd);return -1;}// 启动监听if (listen(listenfd,SOMAXCONN) == -1) {cout << "listen error" << endl;close(listenfd);return -1;}// 存储客户端socket的数组vector<int> clientfds;int maxfd;while (true) {fd_set readset;// 对标志位清零FD_ZERO(&readset);// 将监听的socket加入到待检测的可读事件中// 第listenfd位被置为1FD_SET(listenfd, &readset);maxfd = listenfd;// 将客户端socket加入到待检测的可读事件中for (int i = 0; i < clientfds.size(); i++) {if (clientfds[i] != -1) {FD_SET(clientfds[i], &readset);maxfd = max(maxfd, clientfds[i]);}}// 设置超时时间为1stimeval time;time.tv_sec = 1;time.tv_usec = 0;// 暂且只检测可读事件,不检测可写和异常事件int ret = select(maxfd + 1, &readset, NULL, NULL, &time);if (ret == -1) {// 出错if (errno != EINTR)break;} else if (ret == 0) {// select函数超时continue;} else {// 检测到某个socket有事件// 是否是监听socket的可读事件if (FD_ISSET(listenfd, &readset)) {// 如果是监听socket的可读事件,表示现在有新的连接到来struct sockaddr_in clientaddr;socklen_t clientaddrlen = sizeof(clientaddr);// 接受客户端连接int clientfd = accept(listenfd, (struct sockaddr *)& clientaddr, &clientaddrlen);if (clientfd == -1) {cout << "client socket error" << endl;break;} else {cout << "accept a client connection , fd:" << clientfd << endl;clientfds.push_back(clientfd);}} else {// 从client socket上接受数据// 假设对端发送过来的数据长度不超过63个字符char recvbuf[64];for (int i = 0; i < clientfds.size(); i++) {if (clientfds[i] != -1 && FD_ISSET(clientfds[i], &readset)) {memset(recvbuf, 0, sizeof(recvbuf));// 接受数据int length = recv(clientfds[i], recvbuf, 64, 0);if (length <= 0) {cout << "recv data error, clientfd:" << clientfds[i] << endl;close(clientfds[i]);// 不直接删除该元素而是将位置元素标记clientfds[i] = -1;continue;} else {cout << "clientfd:" << clientfds[i] << "recv data: " << recvbuf << endl;}}}}}}// 处理之后,关闭所有客户端for (int i = 0; i < clientfds.size(); i++) {if (clientfds[i] != -1)close(clientfds[i]);}// 关闭监听close(listenfd);return 0;
}

通信效果演示

这里不需要写客户端程序,直接用nc命令模拟,指定一下服务端的ip地址和端口号就可以通信了,127.0.0.1就是系统的回环地址,直接是用于本机内的socket的通信。这里我们开了两个客户端,分别发送hello和hello world。
由于nc命令发送的数据是按换行符区分的,所以数据包最后一个都是以\n结束。
当断开各个客户端时,服务端的select函数对各个客户端fd进行检测时,仍然会触发可读事件
不过此时对这些fd进行调用recv函数的话会返回0,表示对端关闭了连接。然后服务端这边将fd进行置-1,然后关闭连接即可。
在这里插入图片描述

往期文章

C++网络编程快速入门(一):TCP网络通信基本流程以及基础函数使用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/376815.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android转载一:Android文件命名规范

REF&#xff1a;http://blog.csdn.net/gulianchao/article/details/23391651 (一) Layout命名 1&#xff0e;contentview命名&#xff1a;activity_功能模块.xml 例如&#xff1a;activity_main.xml、activity_more.xml 2&#xff0e;Dialog命名&#xff1a;dialog_描述.xml …

C++网络编程快速入门(三):阻塞与非阻塞式调用网络通信函数

目录阻塞与非阻塞定义send与recvconnect一些问题为什么要将监听socket设置为非阻塞阻塞与非阻塞定义 阻塞模式指的是当前某个函数执行效果未达预期&#xff0c;该函数会阻塞当前的执行线程&#xff0c;程序执行流在超时时间到达或者执行成功后恢复原有流程。非阻塞模式相反&am…

socket 端口和地址复用

https://blog.csdn.net/weibo1230123/article/details/79978745 https://blog.csdn.net/weixin_42157432/article/details/115560824 在linux socket网络编程中&#xff0c;大规模并发TCP或UDP连接时&#xff0c;经常会用到端口复用&#xff1a; int opt 1; if (setsockopt…

MyEclipse老是弹出problem occurred窗口

有的时候是因为jsp页面中的java脚本有误&#xff0c;比如说<% String name"";>就会出现错误&#xff0c;因为结束标签少了一个百分号&#xff05;。转载于:https://www.cnblogs.com/passer1991/archive/2013/03/15/2961624.html

Mysql中代替like模糊查询的一种方法

使用Mysql的函数instr,可代替传统的like方式查询,并且速度更快。 instr函数&#xff0c;第一个参数是字段&#xff0c;第二个参数是要查询的串&#xff0c;返回串的位置&#xff0c;第一个是1&#xff0c;如果没找到就是0. 例如&#xff1a; select username from prefix_user …

Linux网络故障排查命令(ifconfig、ping、telnet、netstat、lsof、nc、curl、tcpdump)

目录ifconfig-s&#xff0c;显示网卡信息的精简列表-a、up、down将IP地址绑定到某个网卡&#xff0c;以及解绑操作pingtelnetnetstatlsofnc模拟一个服务器程序和客户端程序进行通信发送文件curltcpdump参数连接一个正常的监听端口ifconfig 该命令用来查看当前系统的网卡和IP地…

My Oracle Support Metalink站点最近将放弃flash界面转而使用ADF HTML

根据oracle官方博客的报道《The New My Oracle Support User Interface (HTML-based) 》&#xff0c; MY ORACLE SUPPORT开发team会在最近将support.oracle.com站点从原来的flash界面迁移到基于ADF HTML的用户界面上。 实际上在2012年的 January 27&#xff0c; MOS开发team就…

心跳检测以及应用层心跳包机制设计

博主联系方式&#xff1a; QQ:1540984562 微信&#xff1a;wxid_nz49532kbh9u22 QQ交流群&#xff1a;750313950&#xff08;嵌入式方向&#xff09; QQ交流群&#xff1a;856398158&#xff08;后端方向&#xff09; 目录心跳检测应用场景死连接情况保活传递有效业务数据心跳包…

一个DBA的工作写照

一个DBA的工作写照&#xff0c; 一个DBA的内心 Know the DBA Mind! DBA也是 IT民工啊&#xff0c; 民工何苦为难民工&#xff01; 转载于:https://www.cnblogs.com/macleanoracle/archive/2013/03/19/2968227.html

UVALive 6257 Chemist's vows --一道题的三种解法(模拟,DFS,DP)

题意&#xff1a;给一个元素周期表的元素符号&#xff08;114种&#xff09;&#xff0c;再给一个串&#xff0c;问这个串能否有这些元素符号组成&#xff08;全为小写&#xff09;。 解法1&#xff1a;动态规划 定义&#xff1a;dp[i]表示到 i 这个字符为止&#xff0c;能否有…

hdu 1025(最长非递减子序列的n*log(n)求法)

题目链接&#xff1a;http://acm.hdu.edu.cn/showproblem.php?pid1025 经典题。。。最长非递减序列的n*log(n)求法。。。orz... View Code 1 #include<iostream>2 const int N500007;3 using namespace std;4 int city[N];5 int dp[N];//dp[i]保存的是长度为i的最长不降…

消息队列重要机制讲解以及MQ设计思路(kafka、rabbitmq、rocketmq)

目录《Kafka篇》简述kafka的架构设计原理&#xff08;入口点&#xff09;消息队列有哪些作用&#xff08;简单&#xff09;消息队列的优缺点&#xff0c;使用场景&#xff08;基础&#xff09;消息队列如何保证消息可靠传输死信队列是什么&#xff1f;延时队列是什么&#xff1…

数据库归档模式

1、在sys身份下登陆oracle&#xff0c;执行命令archive log list; SQL> archive log list; Database log mode Archive Mode Automatic archival Enabled Archive destination USE_DB_RECOVERY_FILE_DEST Oldest online log sequence …

转载|网络编程中阻塞式函数的底层逻辑

逛知乎看到的&#xff0c;觉得写的挺透彻的&#xff0c;转载一下&#xff0c;原文链接&#xff1a;Unix网络编程里的阻塞是在操作系统的内核态创建一个线程来死循环吗&#xff1f; 原文以阻塞式的recv函数作为讲解&#xff0c;但是所有阻塞式的api底层逻辑基本相通。 下面是正文…

树的存储结构2 - 数据结构和算法42

树的存储结构 让编程改变世界 Change the world by program 孩子表示法 我们这次换个角度来考虑&#xff0c;由于树中每个结点可能有多棵子树&#xff0c;可以考虑用多重链表来实现。 就像我们虽然有计划生育&#xff0c;但我们还是无法确保每个家庭只养育一个孩子的冲动&a…

Sharepoint 2013 发布功能(Publishing features)

一、默认情况下&#xff0c;在创建网站集时&#xff0c;只有选择的模板为‘ Publishing Portal&#xff08;发布门户&#xff09;’与‘ Enterprise Wiki&#xff08;企业 Wiki&#xff09;’时才默认启用发布功能&#xff0c;如下图所示&#xff1a; 二、发布功能包含两块&…

【草稿】windows + vscode 远程开发

主要分为三个步骤&#xff1a; 1、开启openssh服务 2、通过ssh命令连接到远程服务器 3、通过vscode连接远程服务器进行开发调试 ssh概念 SSH是较可靠&#xff0c;专为远程登陆会话和其他网络服务提供安全性得协议&#xff0c;利用ssh协议可以有效防止远程管理过程中得信息…

POJ3185(简单BFS,主要做测试使用)

没事做水了一道POJ的简单BFS的题目 这道题的数据范围是20,所以状态总数就是&#xff08;1<<20&#xff09; 第一次提交使用STL的queue&#xff0c;并且是在队首判断是否达到终点&#xff0c;达到终点就退出&#xff0c;超时&#xff1a;&#xff08;其实这里我是很不明白…

sql server根据表中数据生成insert语句

几个收藏的根据数据库生成Insert语句的存储过程[修正版]----根据表中数据生成insert语句的存储过程--建立存储过程&#xff0c;执行spGenInsertSQL 表名--感谢playyuer----感谢szyicol--CREATEproc[dbo].[spGenInsertSQL](tablenamevarchar(256))asbegindeclaresqlvarchar(8000…

杂感无题|

今天中午和组里面的人吃饭&#xff0c;聊起了科兴跳楼的事情。这事其实前几天我华为的mentor就转给我了&#xff0c;当时也没太在意&#xff0c;在脉脉上看了看&#xff0c;也不知晓是谁&#xff0c;想着可能又是抑郁症吧。 饭后依旧绕着食堂散步&#xff0c;ly说那个人好像还是…