在做Socket编程时,当要处理一个server对应多个client,这种可以每个客户端用一个线程来处理,但是客户端太多,程序的性能会降低。Windows提供了select模型,很好的处理了一对多的模型。select的申明如下:

Int WSAAPI select(
_In_ int nfds,   //0,无意义
_Inout_opt_ fd_set FAR * readfds,  //检查可读性集合
_Inout_opt_ fd_set FAR * writefds, //检查可写性集合
_Inout_opt_ fd_set FAR * exceptfds,//进行异常检测的Socket
_In_opt_ const struct timeval FAR * timeout //非阻塞模式中设置最大等待时间;

msdn可查看参数的详细解释:

Any two of the parameters, readfds, writefds, or exceptfds, can be given as null. At least one must be non-null, and any non-nulldescriptor set must contain at least one handle to a socket.

In summary, a socket will be identified in a particular set when select returns if:

其中任何两个参数readfds、writefds或exceptfds都可以被指定为null。至少一个必须是非空的,任何非空描述符集必须包含一个套接字的至少一个句柄。

总之,当select返回以下情况时,套接字将在特定集合中标识:

readfds:

If listen has been called and a connection is pending, accept will succeed.
       如果监听已被调用,且连接正在挂起,此时调用accept函数会成功

Data is available for reading (includes OOB data if SO_OOBINLINE is enabled).
       有数据可读,如果启用SO_OOBINLINE,则包含OOB数据

Connection has been closed/reset/terminated.
       连接已经关闭,重置或终止

writefds:

If processing a connect call (nonblocking), connection has succeeded.
Data can be sent.
exceptfds:

If processing a connect call (nonblocking), connection attempt failed.
OOB data is available for reading (only if SO_OOBINLINE is disabled).
fd_set的定义如下

typedef struct fd_set {u_int fd_count;               /* how many are SET? */SOCKET  fd_array[FD_SETSIZE];   /* an array of SOCKETs */
} fd_set;#define FD_SETSIZE      64

可知select模型最多可以处理64个套接字

Return Value
    The select function returns the total number of socket handles that are ready and contained in the fd_set structures, zero if the time limit expired, or SOCKET_ERROR if an error occurred. If the return value is SOCKET_ERROR, WSAGetLastError can be used to retrieve a specific error code.

调用成功时select函数返回准备好并包含在fd_set结构中的套接字句柄的总数,如果时间限制过期,则返回零,如果发生错误,则返回SOCKET_ERROR。如果返回值是SOCKET_ERROR,则可以使用WSAGetLastError来检索特定的错误代码。

select模型紧密结合的四个宏来简化对socket的操作:

FD_CLR( s,*set) 从队列set删除句柄s。

FD_ISSET( s, *set) 检查句柄s是否存在与队列set中。

FD_SET( s,*set )把句柄s添加到队列set中。

FD_ZERO( *set ) 把set队列初始化成空队列。

select判断sockets是否刻度的步骤:
1.将该套接字加入到readfds集合;
2.readfds作为第二个参数调用select函数;
3.当select函数返回时,应用程序判断该套接字是否任然存在于readfds集合;
4.如果该套接字存在于readfds集合,则表明该套接字刻度,此时可以调用recv函数接受数据,否则,

下面的代码是一个server对应多个client, 仅做测试demo, 如果是实际项目,需实际处理。

服务端代码

#include<winsock2.h>
#include<iostream>using  namespace  std;#pragma comment(lib,"Ws2_32.lib")int  main()
{//初始化winsock的环境WSADATA  wd;if (WSAStartup(MAKEWORD(2, 2, ), &wd) == SOCKET_ERROR){cout << "WSAStartup  error:" << GetLastError() << endl;return 0;}//1.创建监听套接字SOCKET  sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (sListen == INVALID_SOCKET){cout << "socket  error:" << GetLastError() << endl;return 0;}//2.绑定到ip与端口sockaddr_in  addr;addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");addr.sin_port = htons(8000);addr.sin_family = AF_INET;int len = sizeof(sockaddr_in);if (bind(sListen, (SOCKADDR*)&addr, len) == SOCKET_ERROR){cout << "bind  error:" << GetLastError() << endl;return 0;}//3.监听套接字if (listen(sListen, 5) == SOCKET_ERROR){cout << "listen  error:" << GetLastError() << endl;return 0;}//4. select开始了fd_set  readSet;//定义一个读(接受消息)的集合FD_ZERO(&readSet);//初始化集合FD_SET(sListen, &readSet);//不停的select才可以读取套接字的状态改变while (true){fd_set tmpSet;//定义一个临时的集合FD_ZERO(&tmpSet);//初始化集合tmpSet = readSet;// 每次循环都是所有的套接字//利用select选择出集合中可以读写的多个套接字,有点像筛选int ret = select(0, &tmpSet, NULL, NULL, NULL);//最后一个参数为NULL,一直等待,直到有数据过来if (ret == SOCKET_ERROR){continue;}//成功筛选出来的tmpSet可以发送或者接收的socketfor (int i = 0; i < tmpSet.fd_count; ++i){//获取到套接字SOCKET  s = tmpSet.fd_array[i];//接收到客户端的链接if (s == sListen){SOCKET  c = accept(s, NULL, NULL);//fd_set集合最大值为64if (readSet.fd_count < FD_SETSIZE){//往集合中添加客户端套接字FD_SET(c, &readSet);cout << "欢迎" << c << "进入聊天室!" << endl;//给客户端发送欢迎char buf[100] = { 0 };sprintf(buf, "欢迎%d进入聊天室!", c);send(c, buf, 100, 0);}else{cout << "达到客户端容量上线!" << endl;}}else//一定是客户端{//接收客户端的数据char buf[100] = { 0 };ret = recv(s, buf, 100, 0);if (ret == SOCKET_ERROR || ret == 0){closesocket(s);FD_CLR(s, &readSet);cout << s << "离开聊天室!" << endl;}else{cout << s << "说:" << buf << endl;}}}}//关闭监听套接字closesocket(sListen);//清理winsock环境WSACleanup();return 0;
}

客户端代码

#include<winsock2.h>//winsock2的头文件
#include<iostream>
using  namespace std;//勿忘,链接dll的lib
#pragma comment(lib, "ws2_32.lib")int  main()
{//加载winsock2的环境WSADATA  wd;if (WSAStartup(MAKEWORD(2, 2), &wd) != 0){cout << "WSAStartup  error:" << GetLastError() << endl;return 0;}//1.创建流式套接字SOCKET  s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (s == INVALID_SOCKET){cout << "socket  error:" << GetLastError() << endl;return 0;}//2.连接服务器sockaddr_in   addr;addr.sin_family = AF_INET;addr.sin_port = htons(8000);addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");int len = sizeof(sockaddr_in);if (connect(s, (SOCKADDR*)&addr, len) == SOCKET_ERROR){cout << "connect  error:" << GetLastError() << endl;return 0;}//3接收服务端的消息char buf[100] = { 0 };recv(s, buf, 100, 0);cout << buf << endl;//3随时给服务端发消息int  ret = 0;do{char buf[100] = { 0 };cout << "请输入聊天内容:";cin >> buf;ret = send(s, buf, 100, 0);} while (ret != SOCKET_ERROR && ret != 0);//4.关闭监听套接字closesocket(s);//清理winsock2的环境WSACleanup();return 0;
}

例如,测试结果如下,在服务端窗口可以看到客户端的连接情况。

select模型只处理了64个socket, 如果多于64个如何处理呢,Hp-Socket, IOCP这两种可以试试, 暂时没有项目用到,以后再研究研究。

原文链接:https://blog.csdn.net/yao_hou/article/details/102728970

C++ socket编程select模型相关推荐

  1. java socket编程 select_windows socket编程select模型使用

    int select( int nfds,            //忽略 fd_ser* readfds,    //指向一个套接字集合,用来检测其可读性 fd_set* writefds,   / ...

  2. linux socket编程epoll模型实现群发消息

    1.实现功能 本代码主要实现了socket编程epoll模型实现多个客户端连接服务器,客户端可以进行群发消息和接收用户输入文本信息,然后发送该信息给服务器,服务器收到后发送应答信息.客户端接收并显示该 ...

  3. socket编程 -- epoll模型服务端/客户端通信的实现

    https://blog.csdn.net/y396397735/article/details/50680359 本例实现如下功能: 支持多客户端与一个服务端进行通信,客户端给服务端发送字符串数据, ...

  4. socket select模型

    由于socket recv()方法是堵塞式的,当多个客户端连接服务器时,其中一个socket的recv调用时,会产生堵塞,使其他连接不能继续. 如果想改变这种一直等下去的焦急状态,可以多线程来实现(不 ...

  5. 服务端升级为select模型处理多客户端

    文章目录 1 服务端升级为select模型处理多客户端 1.1 概述 1.2 服务端实现 1 服务端升级为select模型处理多客户端 1.1 概述 之前我们的设计是服务端与客户端1对1的阻塞模式网络 ...

  6. socket编程的select模型

    在掌握了socket相关的一些函数后,套接字编程还是比较简单的,日常工作中碰到很多的问题就是客户端/服务器模型中,如何让服务端在同一时间高效的处理多个客户端的连接,我们的处理办法可能会是在服务端不停的 ...

  7. 糖儿飞教你学C++ Socket网络编程——28. 使用select模型实现一对多通信

    在项目10中,采用多线程技术实现了TCP协议的一对多通信,但如果客户端过多,就会导致服务器端的线程数量膨胀,使得服务器的资源占用过大.能不能让TCP程序在一个线程中同时与多个客户端进行通信呢?答案是可 ...

  8. socket编程(二) select 模型

    select 模型是winsock中最常见的I/O模型,主要是它能够防止程序在套接字处于阻塞模式的时候经过一次I/O操作后被阻塞,同时也能够防止套接字处于非阻塞模式产生的WSAEWOULDBLOCK错 ...

  9. 朴素、Select、Poll和Epoll网络编程模型实现和分析——Select模型

    在<朴素.Select.Poll和Epoll网络编程模型实现和分析--朴素模型>中我们分析了朴素模型的一个缺陷--一次只能处理一个连接.本文介绍的Select模型则可以解决这个问题.(转载 ...

  10. 转:socket select模型示例

    // selectSocketMode.cpp : Defines the entry point for the console application. // #include "std ...

最新文章

  1. SSM框架整合(Spring+SpringMVC+MyBatis)
  2. 算法-----第一个错误的版本
  3. python入门只需20分钟-史上最详细python学习路线-从入门到精通,只需5个月时间...
  4. word2vec词向量 文本分类实现(TensorFlow版,算法TextCNN)
  5. Java LocalDate类| ofYearDay()方法与示例
  6. java7 uri,细数Java8中那些让人纵享丝滑的文件操作
  7. Phoenix 关联hbase表历史数据
  8. Oracle BBED 工具介绍
  9. playbook管理配置文件
  10. 古代汉语(王力版)笔记
  11. MeasureSpec
  12. SpringCloud_03_Feign入门示例
  13. 小红书6.18种草拔草投放攻略,品牌制胜决策时刻
  14. LaTeX 页眉设置
  15. 基于Python的自动聊天机器人
  16. 十年饮冰,难凉热血--毕业四年多的经历,致敬每一位IT男
  17. vs无法产生pdb文件,也就无法断点调试
  18. 零基础怎样自学编程?新手如何学习编程?编程学习入门指南
  19. from..import 语句
  20. Linux学习笔记--12(iptables与firewalld)

热门文章

  1. 天龙八部linux 换win,Linux从菜鸟到大师之天龙八部 第三部文本编辑处理.doc
  2. xcode动态改变窗口大小_[SwiftUI 100天] 在 SwiftUI 中动态过滤 @FetchRequest
  3. jamon java_JAMon监控web工程方法的调用性能
  4. matlab四宫格画图_科学网—Matlab画图(一):生成高质量的供发表和展示用的图 - 周建锋的博文...
  5. python有向图遍历_python – 获取有向图的所有边对. networkx
  6. 高等代数期末考试题库及答案_复旦大学2019--2020学年第一学期19级高等代数I期末考试第六大题...
  7. netty 5 心跳
  8. MySQL基础3-SQL语言
  9. 转:python命令行解析工具Argparse
  10. 线程的同步互斥之事件对象(Event)