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. git 分支的创建和切换

    每次提交,GIT 都会将他们串成一个时间线,截止到目前,只有一个时间线,GIT里叫这个分支为主分支,叫master,HEAD指向master,master指向提交,HEAD指向当前的分支. 一开始的时 ...

  2. Android开发之自定义Dialog简单实现

    本文着重研究了自定义对话框,通过一下步骤即可清晰的理解原理,通过更改界面设置和style类型,可以应用在各种各样适合自己的App中. 首先来看一下效果图: 首先是activity的界面 点击了上述图片 ...

  3. Liunx 重定向,管道符(转)

    原作网址:http://blog.csdn.net/qq_16811963/article/details/52997178 输出重定向 >代表以覆盖的方式将命令的正确输出输出到指定的文件或设备 ...

  4. firefox打开链接自动跳转至新页面设置

    Firefox打开新页面时,活动页面会自动跳转到刚刚打开的页面,用着很不舒服,想打开新页面标签时,页面依然会停留在之前的页面. 在网上找了一下,设置方法如下: 在地址栏里输入about:config, ...

  5. Pandas选择数据

    1.简单筛选 >>> dates = pd.date_range(', periods=6) >>> df = pd.DataFrame(np.arange(24) ...

  6. linux中的设备类型

    loop设备 loop设备 一.参考命令[root@localhost a]# losetup usage:  losetup loop_device                          ...

  7. spec文件写作规范

    spec文件写作规范 2008-09-28 11:52:17 分类: LINUX 1.The RPM system assumes five RPM directories BUILD:rpmbuil ...

  8. pthon 反转嵌套的list

    def rev_recursive(li): try: iterator = iter(li) except TypeError: return li l = [] for item in itera ...

  9. 异常Throwable

    1.有效处理java异常三原则 java中异常提供了一种识别及响应错误情况的一致性机制,有效地异常处理能使程序更加健壮,易于调试.异常之所以是一种强大的调试手段,在于其回答了以下三个问题: 什么出了错 ...

  10. Mac虚拟机

    2018-06-21 需要的Mac静像是ios或是cdr的,如果是dmg,可以参考这个转换http://blog.sina.com.cn/s/blog_60b45f230101kkbf.html 或  ...