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 项目,真正重新体验了一把搬砖感觉:在一个多月时间好像不加任何思考,不断敲键盘加代码.我想,这也许是行业内大部分中小型公司程序猿的真实写照:都是坐在电脑前的搬砖工人.不 ...
随机推荐
- 纯css实现div中未知尺寸图片的垂直居中
1.淘宝的方法 在曾经的"淘宝UED招聘"中有这样一道题目: “使用纯CSS实现未知尺寸的图片(但高宽都小于200px)在200px的正方形容器中水平和垂直居中.” 当然出题并不是 ...
- Oracle 表空间和数据文件之间的关系
首先,你需要明白的一点是:数据库的物理结构是由数据库的操作系统文件所决定,每一个Oracle数据库是由三种类型的文件组成:数据文件.日志文件和控制文件.数据库的文件为数据库信息提供真正的物理存储. 每 ...
- IDEA安装小配置
1. view-->toolbar+toolbuttons 2. 根据大小写IDEA能准确提示 配置自动导入包 定义代码模板 提示忽略大小写 配置虚拟机内存,修改idea64.exe.vmopt ...
- Ajax cookie session form组件
. Cookie是什么 保存在浏览器端的键值对 为什么要有Cookie? 因为HTTP请求是无状态的 Cookie的原理? 服务端可以在返回响应的时候 做手脚 在浏览器上写入键值对(Cookie) 浏 ...
- PAT 1089 狼人杀-简单版(20 分)(代码+测试点分析)
1089 狼人杀-简单版(20 分) 以下文字摘自<灵机一动·好玩的数学>:"狼人杀"游戏分为狼人.好人两大阵营.在一局"狼人杀"游戏中,1 号玩家 ...
- 哪些intel 网卡支持SR-IOV
哪些英特尔®以太网适配器和控制器支持 SR-IOV? 英特尔®以太网融合网络适配器 X710 系列 英特尔®以太网聚合网络适配器 X710-da2 英特尔®以太网聚合网络适配器 X710-da4 英特 ...
- iOS.Thread.OSAtomic
1. 原子操作 (Atomic Operations) 编写多线程代码最重要的一点是:对共享数据的访问要加锁. Shared data is any data which more than one ...
- 担心后端代码泄露?用delphi做后端,模板扣出来,随时可以变化。
担心后端代码泄露?用delphi做后端,模板扣出来,随时可以变化. 本项目不是intraweb, unigui等类似的拖拉项目,只是一个简单 的模板引擎,理论上可以结合任何后端. 要就下载源码,作者保 ...
- ps教程分享:一定要记住这20种PS技术!
一定要记住这20种PS技术!会让你的照片美的不行! 一种简单的数码照片后期润饰 1)打开图片,执行色像/饱和度(-40)降低饱和度. 2)新建一图层,将图层模式改为柔光,用画笔工具将需要润饰的部分画几 ...
- Hive 系列(一)安装部署
Hive 系列(一)安装部署 Hive 官网:http://hive.apache.org.参考手册 一.环境准备 JDK 1.8 :从 Oracle 官网下载,设置环境变量(JAVA_HOME.PA ...