WinSockets编程(六)select模式
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模式的更多相关文章
- select模式
在很多比较各种网络模型的文章中,但凡提到select模型时,都会说select受限于轮询的套接字数量,这个 数量也就是系统头文件中定义的FD_SETSIZE值(例如64).但事实上这个算不上真的限制. ...
- JDBC编程六部曲
今天初学jdbc,明白了大致的编程流程,在此总结一下: JDBC编程可以分为六步——六部曲: * 第一步:注册驱动. * 1.1 获取驱动对象 * 1.2 注册驱动 * 第二步:获取数据库连接 * 第 ...
- Java多线程编程中Future模式的详解
Java多线程编程中,常用的多线程设计模式包括:Future模式.Master-Worker模式.Guarded Suspeionsion模式.不变模式和生产者-消费者模式等.这篇文章主要讲述Futu ...
- Java多线程编程中Future模式的详解<转>
Java多线程编程中,常用的多线程设计模式包括:Future模式.Master-Worker模式.Guarded Suspeionsion模式.不变模式和生产者-消费者模式等.这篇文章主要讲述Futu ...
- WPF InkCanvas EditingMode为Select时 在其选择时各种事件中撤销Select模式的方法
InkCanvas有多种输入模式. 通过InkCanvasEditingMode来进行对其调整 分别是 None=0// 忽略鼠标和手写笔输入 Ink = 1// 允许用户绘制批注,默认模式.使用鼠标 ...
- 【C++自我精讲】基础系列六 PIMPL模式
[C++自我精讲]基础系列六 PIMPL模式 0 前言 很实用的一种基础模式. 1 PIMPL解释 PIMPL(Private Implementation 或 Pointer to Implemen ...
- javascript 面向对象编程(工厂模式、构造函数模式、原型模式)
javascript 面向对象编程(工厂模式.构造函数模式.原型模式) CreateTime--2018年3月29日17:09:38 Author:Marydon 一.工厂模式 /** * 工厂模 ...
- 【并发编程】Future模式添加Callback及Promise 模式
Future Future是Java5增加的类,它用来描述一个异步计算的结果.你可以使用 isDone 方法检查计算是否完成,或者使用 get 方法阻塞住调用线程,直到计算完成返回结果.你也可以使用 ...
- restapi(7)- 谈谈函数式编程的思维模式和习惯
国庆前,参与了一个c# .net 项目,真正重新体验了一把搬砖感觉:在一个多月时间好像不加任何思考,不断敲键盘加代码.我想,这也许是行业内大部分中小型公司程序猿的真实写照:都是坐在电脑前的搬砖工人.不 ...
随机推荐
- Struts2框架的数据封装一之属性封装(属性封装的第一种方式:对参数进行封装)
request带着参数来,aciton对其进行处理.在学习action之前,使用的是servlet对request进行处理.request请求时会带有参数,所以我们要对这些参数进行封装. 1. 为什么 ...
- [z]一分钟教你知道乐观锁和悲观锁的区别
悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁.传统的关系型数据 ...
- mysql数据库存储经度纬度
使用float或者double会自动四舍五入,用decimal(20,17)当然你用varchar也是可以的
- linux C程序中获取shell脚本输出(如获取system命令输出)
转载自 http://blog.csdn.net/hjxhjh/article/details/7909518 1. 前言 Unix 界有一句名言:“一行shell脚本胜过万行C程序”,虽然这句话有些 ...
- table-layout 显示规则以及其他一些零碎的东西
首先对中文显示的不够好 对中文失效 auto是表格的宽和高都会随着内容增多而改变 而fixed只会增加表格的高度 宽度不会发生改变 table中的td的宽,高会根据内容的多少而变化: fix ...
- Github上删除仓库
1.先进入到工程里面,然后选择“Settings” 2.将页面拉到最下面,然后点击“Delete this repository”,接着在弹出的窗口中输入需要删除的仓库名. 弹出的窗口如下:
- Bowtie2的安装与使用
Bowtie2的安装与使用 2017-06-15 18:58:52 342 0 0 Bowtie2用来快速比对短reads(50-100bp)与参考基因组,与常规的比对软件不 ...
- p值还是 FDR ?
p值还是 FDR ? 差异分析 如何筛选显著性差异基因,p value, FDR 如何选 经常有同学询问如何筛选差异的基因(蛋白).已经计算了表达量和p value值,差异的基因(蛋白)太多了,如何筛 ...
- SQL Server 2008数据库连接错误
以Windows身份连接SQL Server 2008数据库时,连接不上,出现如下报错画面: 解决办法:打开services窗口,找到名字类似于SQL Server (xxx)的服务,启动服务. 注: ...
- c# 反编译后窗口控件在vs中打不开的修改方法
DialogResult.Cancel System.Windows.Forms.DialogResult.Cancel DialogResult.OK System.Windows.Forms.Di ...