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 项目,真正重新体验了一把搬砖感觉:在一个多月时间好像不加任何思考,不断敲键盘加代码.我想,这也许是行业内大部分中小型公司程序猿的真实写照:都是坐在电脑前的搬砖工人.不 ...
随机推荐
- go语言中的strings常用函数和格式化输出
package main; import ( "fmt" "strings" ) type person struct { name string; age i ...
- SQL truncate 、delete与drop区别[z]
[z]https://www.cnblogs.com/8765h/archive/2011/11/25/2374167.html 相同点: 1.truncate和不带where子句的delete.以及 ...
- Linux netstat
一.简介 二.语法 三.实例 1)查看TCP连接数 netstat -n | awk '/^tcp/ {++S[$NF]} END {for (a in S) print a, S[a]}'
- L1-025 正整数A+B(15)(思路+测试点分析)
L1-025 正整数A+B(15 分) 题的目标很简单,就是求两个正整数A和B的和,其中A和B都在区间[1,1000].稍微有点麻烦的是,输入并不保证是两个正整数. 输入格式: 输入在一行给出A和B, ...
- AFNetworking HTTP响应头返回数据
//发送验证码 NSLog(@"发送验证码"); AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationM ...
- UOJ 274 温暖会指引我们前进 - LCT
Solution 更新掉路径上温暖度最小的边就可以了~ Code #include<cstdio> #include<cstring> #include<algorith ...
- Netty 源码 ChannelHandler(四)编解码技术
Netty 源码 ChannelHandler(四)编解码技术 Netty 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html) 一.拆包与粘 ...
- maven mirror , profile , snapshot 和release
1. settings.xml 配置的mirror <mirrors> <mirror> <id>Nexus</id> <name>nexu ...
- mysql 查询 所有 父节点 单表
SELECT T2.* FROM ( SELECT @r AS _id, ( SELECT @r := parent_id FROM tp_module_rel WHERE REL_ID = _id ...
- STL基础2:vector中使用结构体
#include <iostream> #include <vector> #include <numeric> #include <algorithm> ...