select模式的思想

创建FD_SET fd_all,并初始化FD_ZERO(&fd_all);

Step1  初始时:

Step2   加入一个套接字之后,比如FD_SET(sServer,&fd_all);

Step3   调用select函数之后,有两种情况

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

上述的select假设是在非阻塞的情况下。由图示可以看出, select对FD_SET的结构进行了动态改变,没有变化的会置为0,有变化的会保持为1,这就是select的思想。

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

保持FD_SET的状态

假设有1,2,3,4四个标号的套接字加入FD_SET

//伪码
FD_SET(,&fd_all);
FD_SET(,&fd_all);
FD_SET(,&fd_all);
FD_SET(,&fd_all);
for(;;)
{
...
select(,&fd_all,,NULL,NULL);
...
}

在进行选择之后,有些套接字可能暂时没有数据收发就被select函数过滤掉了,所以要保持套接字的状态,模式如下:

//保持套接字状态的方法示例

    while(true)
{
fd_read=fd_all;
fd_write=fd_all;
select(,&fd_read,,NULL,NULL);//①阻塞; for(UINT i=;i<fd_all.fd_count;i++)
{
if(FD_ISSET(fd_all.fd_array[i],&fd_read))
{
if(fd_all.fd_array[i]==sServer)//②
{
sockaddr_in addrClient;
int addrClientlen = sizeof(addrClient);
sClient = accept(sServer,(sockaddr FAR*)&addrClient, &addrClientlen);//③ FD_SET(sClient,&fd_all);//④
}
else //⑤ 收发数据
{
ZeroMemory(buf, BUF_SZIE);
retVal = recv(fd_all.fd_array[i], buf, BUF_SZIE-, );//⑥
if (SOCKET_ERROR == retVal)
{
printf("recv failed!\n");
closesocket(sServer);
closesocket(sClient); //关闭套接字
WSACleanup();
return -;
}
printf("%s\n", buf);
}
}
}

版本一(有点问题,需要修正):

/**************************************************************************************************************

2018/10/9号进行修正:

原因:

1. 在select轮询时,如果没有连接返回-1
2. 在while循环时,需要Sleep(100),不然CPU空转太厉害导致CPU使用率上升!

/**************************************************************************************************************

#include <WinSock2.h>
#include <iostream> #include <stdio.h> #pragma comment(lib,"ws2_32.lib") #define PORT 8000
#define MSGSIZE 255
#define SRV_IP "127.0.0.1" int g_nSockConn = ; //请求连接的数目 struct ClientInfo
{
SOCKET sockClient; //客户端套接字
SOCKADDR_IN addrClient; //客户端地址
}; ClientInfo g_Client[FD_SETSIZE]; //客户端套接字集合;
DWORD WINAPI WorkThread(LPVOID lpParameter); int main(int argc, char *argv[])
{
WSADATA wsd;
WSAStartup(MAKEWORD(, ), &wsd); SOCKET sockListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//创建套接字 SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr(SRV_IP);
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(PORT);
bind(sockListen, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));//绑定 listen(sockListen, );//监听 DWORD dwThreadIDRecv = ;
DWORD dwThreadIDWrite = ; HANDLE hand = CreateThread(NULL, , WorkThread, NULL, , &dwThreadIDRecv);//工作线程
if (hand == NULL)
{
std::cout <<"创建线程失败!"<<std::endl;
return -;
} SOCKET sockClient;
SOCKADDR_IN addrClient;
int nLenAddrClient = sizeof(SOCKADDR_IN); while (true)
{
sockClient = accept(sockListen, (SOCKADDR*)&addrClient, &nLenAddrClient);
if (sockClient != INVALID_SOCKET)
{
g_Client[g_nSockConn].addrClient = addrClient;//保存客户端地址信息
g_Client[g_nSockConn].sockClient = sockClient;
g_nSockConn++;
}
}
closesocket(sockListen);
WSACleanup();
return ;
} DWORD WINAPI WorkThread(LPVOID lpParameter)
{
std::cout << "线程开始执行...." << std::endl;
FD_SET fdRead;
int nRet = ; //记录发送或者接受的字节数 TIMEVAL tv; //设置超时等待时间
tv.tv_sec = ;
tv.tv_usec = ; char buf[MSGSIZE] = { '\0' }; while (true)
{
FD_ZERO(&fdRead);
for (int i = ; i < g_nSockConn; i++)
{
FD_SET(g_Client[i].sockClient, &fdRead);
} //只处理read事件,不过后面还是会有读写消息发送的
nRet = select(, &fdRead, NULL, NULL, &tv);
std::cout << "g_nSockConn=" << g_nSockConn << std::endl;
std::cout << "nRet=" << nRet << std::endl; if (nRet == -1)//没有连接或者没有读事件
{
       Sleep(100);  //需睡眠
continue;
}
for (int i = ; i < g_nSockConn; i++)
{
std::cout << "已连接的套接字数目g_nSockConn=" << g_nSockConn << std::endl;
Sleep();
if (FD_ISSET(g_Client[i].sockClient, &fdRead))
{
nRet = recv(g_Client[i].sockClient, buf, sizeof(buf), ); if (nRet == || (nRet == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET))
{
std::cout << "Client " << inet_ntoa(g_Client[i].addrClient.sin_addr) << "closed" <<std::endl;
closesocket(g_Client[i].sockClient); if (i < g_nSockConn - )
{
//将失效的sockClient剔除,用数组的最后一个补上去
g_Client[i--].sockClient = g_Client[--g_nSockConn].sockClient;
}
}
else
{
std::cout <<"客户端地址为:"<<inet_ntoa(g_Client[i].addrClient.sin_addr) << ": " << std::endl;
std::cout << buf <<std::endl;
strcpy_s(buf, "Hello!");
nRet = send(g_Client[i].sockClient, buf, strlen(buf) + , );
g_nSockConn--;
}
}
}
}
return ;
}

版本二(正常运行):

#include <stdio.h>
#include "initSocket.h"
CInitSock theSock;
int main()
{
USHORT nPort = ;
SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(nPort);
sin.sin_addr.S_un.S_addr = INADDR_ANY;
if (::bind(sListen, (sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR)
{
printf(" Failed bind() \n");
return -;
}
::listen(sListen, ); fd_set fdSocket;
FD_ZERO(&fdSocket);
FD_SET(sListen, &fdSocket);
while (TRUE)
{
fd_set fdRead = fdSocket;
int nRet = ::select(, &fdRead, NULL, NULL, NULL);
if (nRet > )
{
for (int i = ; i < (int)fdSocket.fd_count; i++)
{
if (FD_ISSET(fdSocket.fd_array[i], &fdRead))
{
if (fdSocket.fd_array[i] == sListen)
{
if (fdSocket.fd_count < FD_SETSIZE)
{
sockaddr_in addrRemote;
int nAddrLen = sizeof(addrRemote);
SOCKET sNew = ::accept(sListen, (SOCKADDR*)&addrRemote, &nAddrLen);
FD_SET(sNew, &fdSocket);
printf("接收到连接(%s)\n", ::inet_ntoa(addrRemote.sin_addr));
}
else
{
printf(" Too much connections! \n");
continue;
}
}
else
{
char szText[];
int nRecv = ::recv(fdSocket.fd_array[i], szText, strlen(szText), );
if (nRecv > ) // (2)可读
{
szText[nRecv] = '\0';
printf("接收到数据:%s \n", szText);
}
else
{
printf("连接断开...!");
::closesocket(fdSocket.fd_array[i]);
FD_CLR(fdSocket.fd_array[i], &fdSocket);
}
}
}
}
}
else
{
printf(" Failed select() \n");
break;
}
}
return ;
}

WinSockets编程(六)select模式的更多相关文章

  1. select模式

    在很多比较各种网络模型的文章中,但凡提到select模型时,都会说select受限于轮询的套接字数量,这个 数量也就是系统头文件中定义的FD_SETSIZE值(例如64).但事实上这个算不上真的限制. ...

  2. JDBC编程六部曲

    今天初学jdbc,明白了大致的编程流程,在此总结一下: JDBC编程可以分为六步——六部曲: * 第一步:注册驱动. * 1.1 获取驱动对象 * 1.2 注册驱动 * 第二步:获取数据库连接 * 第 ...

  3. Java多线程编程中Future模式的详解

    Java多线程编程中,常用的多线程设计模式包括:Future模式.Master-Worker模式.Guarded Suspeionsion模式.不变模式和生产者-消费者模式等.这篇文章主要讲述Futu ...

  4. Java多线程编程中Future模式的详解<转>

    Java多线程编程中,常用的多线程设计模式包括:Future模式.Master-Worker模式.Guarded Suspeionsion模式.不变模式和生产者-消费者模式等.这篇文章主要讲述Futu ...

  5. WPF InkCanvas EditingMode为Select时 在其选择时各种事件中撤销Select模式的方法

    InkCanvas有多种输入模式. 通过InkCanvasEditingMode来进行对其调整 分别是 None=0// 忽略鼠标和手写笔输入 Ink = 1// 允许用户绘制批注,默认模式.使用鼠标 ...

  6. 【C++自我精讲】基础系列六 PIMPL模式

    [C++自我精讲]基础系列六 PIMPL模式 0 前言 很实用的一种基础模式. 1 PIMPL解释 PIMPL(Private Implementation 或 Pointer to Implemen ...

  7. javascript 面向对象编程(工厂模式、构造函数模式、原型模式)

      javascript 面向对象编程(工厂模式.构造函数模式.原型模式) CreateTime--2018年3月29日17:09:38 Author:Marydon 一.工厂模式 /** * 工厂模 ...

  8. 【并发编程】Future模式添加Callback及Promise 模式

    Future Future是Java5增加的类,它用来描述一个异步计算的结果.你可以使用 isDone 方法检查计算是否完成,或者使用 get 方法阻塞住调用线程,直到计算完成返回结果.你也可以使用  ...

  9. restapi(7)- 谈谈函数式编程的思维模式和习惯

    国庆前,参与了一个c# .net 项目,真正重新体验了一把搬砖感觉:在一个多月时间好像不加任何思考,不断敲键盘加代码.我想,这也许是行业内大部分中小型公司程序猿的真实写照:都是坐在电脑前的搬砖工人.不 ...

随机推荐

  1. Spring事务<tx:annotation-driven/>的理解

    在使用Spring的时候,配置文件中我们经常看到 annotation-driven 这样的注解,其含义就是支持注解,一般根据前缀 tx.mvc 等也能很直白的理解出来分别的作用. <tx:an ...

  2. shell 脚本编写基础

    在进行Linux测试时编写脚本是必不可少的,Shell脚本的名称可以随便定义,也不要什么后缀名,例如可以写abc,smartzip这类名称,运行时只要键入 ./smartzip就能运行脚本了.. 每行 ...

  3. jquery关于attr和prop的差异

    转自:http://www.jb51.net/article/88068.htm 处理像checkbox,radio和select这样的元素时,经常会发现明明使用了attr设置了selected或ch ...

  4. 12.Mysql存储过程和函数

    12.存储过程和函数12.1 什么是存储过程和函数存储过程和函数是事先经过编译并存储在数据库中的一段SQL语句的集合,调用存储过程和函数简化应用开发人员的工作,减少数据在数据库和应用服务器之间的传输, ...

  5. Oracle_高级功能(10) 备份恢复

    备份与恢复Oracle数据库有三种标准的备份方法,分别是导出/导入(EXP/IMP).热备份和冷备份.导出/导入是一种逻辑备份,冷备份和热备份是物理备份.一.导出/导入(Export/Import)利 ...

  6. sqlserver自带的导入导出工具,分别导入大批量mysql和oracle数据时的感受

    sqlserver自带的导入导出工具,分别导入大批量mysql和oracle数据时,mysql经常出现格式转换出错,不好导入  导入的数据量比较大时,还不如自己写个工具导入 今天在导oracle时,想 ...

  7. SqlServer添加触发器不让删除数据

    触发器是:instead of delete 类型,注意了:instead类型的触发器相当于: DELETE命令过来后,直接走触发器中的代码,再往下,没有了…… 就是说,这个触发器会屏蔽掉你所有的DE ...

  8. UFT12 更新模式

    一. 更新测试(非常规运行模式).  如果您知道应用程序已更改, 请以下列某种模式运行测试以相应更新测试: 维护运行模式.如果预计UFT 无法识别测试中的对象, 则使用此模式.当测试运行时, UFT ...

  9. Java.FamousBlogs

    本文总结在工作/学习中发现的Java相关的blogs. 1. java code geeks (jcg) http://www.javacodegeeks.com/join-us/jcg/ 2. ht ...

  10. iOS.Debug.Simulator

    1. iOS Simulator Tips & Tricks http://code.tutsplus.com/tutorials/ios-simulator-tips-tricks--mob ...