select
The select function determines the status of one or more sockets, waiting if necessary, to perform synchronous I/O.
Syntax
int select( _In_ int nfds,
_Inout_ fd_set *readfds,
_Inout_ fd_set *writefds,
_Inout_ fd_set *exceptfds,
_In_ const struct timeval *timeout);
Parameters
nfds [in]
Ignored. The nfds parameter is included only for compatibility with Berkeley sockets.
Windows下忽略竖般。Linux才有意義。
readfds [in, out]
An optional pointer to a set of sockets to be checked for readability.
writefds [in, out]
An optional pointer to a set of sockets to be checked for writability.
exceptfds [in, out]
An optional pointer to a set of sockets to be checked for errors.
timeout [in]
The maximum time for select to wait, provided in the form of a TIMEVAL structure. Set the timeout parameter to null for blocking operations.
select函數(shù)最大等待時(shí)間颈嚼。
設(shè)置為NULL時(shí)阻塞。
Return value
The select function returns the total number of socket handles that are ready and contained in the fd_set structures, zero if the time limit expired, or SOCKET_ERROR if an error occurred. If the return value is SOCKET_ERROR, WSAGetLastError can be used to retrieve a specific error code.
正常返回fd_set結(jié)構(gòu)中準(zhǔn)備好的(可讀、可寫或者發(fā)生異常)socket句柄的總個(gè)數(shù)遣妥。
發(fā)生錯(cuò)誤時(shí)品姓,返回SOCKET_ERROR。
timeout時(shí)蜕猫,返回0
/*
* Structure used in select() call, taken from the BSD file sys/time.h.
*/
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* and microseconds */
};
tv_usec單位為微秒寂曹,即一秒的1000000分之一。
FD_SET
FD_SET是什么
...\Microsoft SDKs\Windows\v7.0A\include\winsock2.h
#define FD_SETSIZE 64
typedef struct fd_set {
u_int fd_count; /* how many are SET? */
SOCKET fd_array[FD_SETSIZE]; /* an array of SOCKETs */
} fd_set;
typedef struct fd_set FD_SET;
可看出FD_SET包含兩部分:
fd_count
SOCKET隊(duì)列的長(zhǎng)度回右。fd_array
SOCKET隊(duì)列隆圆。最大為FD_SETSIZE。
FD_SET操作宏
FD_ISSET(s, *set)
用于檢測(cè)一個(gè)描述符是否是fd_set集合的可讀或者可寫成員翔烁,不在返回0匾灶,是返回非0.
select使用方法
在調(diào)用select函數(shù)對(duì)套接字進(jìn)行監(jiān)視之前,必須將要監(jiān)視的套接字分配給上述三個(gè)數(shù)組中的一個(gè)租漂。然后調(diào)用select函數(shù)阶女,再次判斷需要監(jiān)視的套接字是否還在原來(lái)的集合中。就可以知道該集合是否正在發(fā)生IO操作哩治。
例如:應(yīng)用程序想要判斷某個(gè)套接字是否存在可讀的數(shù)據(jù)秃踩,需要進(jìn)行如下步驟:
1:將該套接字加入到readfds集合。
2:以readfds作為第二個(gè)參數(shù)調(diào)用select函數(shù)业筏。
3:當(dāng)select函數(shù)返回時(shí)憔杨,應(yīng)用程序判斷該套接字是否仍然存在于readfds集合。
4:如果該套接字存在與readfds集合蒜胖,則表明該套接字可讀消别。此時(shí)就可以調(diào)用recv函數(shù)接收數(shù)據(jù)。否則台谢,該套接字不可讀寻狂。
http://blog.csdn.net/ithzhang/article/details/8363951
調(diào)用select函數(shù)后,留在set中的socket須指定條件朋沮。
In summary, a socket will be identified in a particular set when select returns if:
readfds:
- If listen has been called and a connection is pending, accept will succeed. (新的連接請(qǐng)求)
- Data is available for reading (includes OOB data if SO_OOBINLINE is enabled).
- Connection has been closed/reset/terminated. (連接關(guān)閉/重置等)
writefds:
- If processing a connect call (nonblocking), connection has succeeded.(當(dāng)前socket通過connect連接其他tcp服務(wù)器成功)
- Data can be sent.
exceptfds:
- If processing a connect call (nonblocking), connection attempt failed.
- OOB data is available for reading (only if SO_OOBINLINE is disabled).
select優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
int select( _In_ int nfds,
_Inout_ fd_set *readfds,
_Inout_ fd_set *writefds,
_Inout_ fd_set *exceptfds,
_In_ const struct timeval *timeout);
- 跨平臺(tái)性比較好
- 可通過timeout設(shè)置超時(shí)時(shí)間
- 可同時(shí)等待多個(gè)套接字
缺點(diǎn):
- 句柄數(shù)目受限
select的句柄數(shù)目受限蛇券,在linux/posix_types.h頭文件有這樣的聲明:
#define __FD_SETSIZE 1024
表示select最多同時(shí)監(jiān)聽1024個(gè)fd
- 隨著FD的數(shù)目增長(zhǎng)而效率降低
select() 接口并不是實(shí)現(xiàn)“事件驅(qū)動(dòng)”的最好選擇。
因?yàn)楫?dāng)需要探測(cè)的句柄值較大時(shí),select() 接口本身需要消耗大量時(shí)間去輪詢各個(gè)句柄纠亚。
很多操作系統(tǒng)提供了更為高效的接口塘慕,如 linux 提供了 epoll
http://www.open-open.com/lib/view/open1346029754786.html
select調(diào)用小demo
// pxSelect.cpp : 定義控制臺(tái)應(yīng)用程序的入口點(diǎn)。
// Zhaoliang Guo
// 20170617
// A demo for select
#include "stdafx.h"
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")
FD_SET fdReadSet;
#include <iostream>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsaData;
WORD wVersionRequested;
wVersionRequested = MAKEWORD(2, 2);
int ret = WSAStartup(wVersionRequested, &wsaData); //加載套接字庫(kù)
if(ret!=0)
{
printf("WSAStartup() failed!\n");
return -1;
}
// 確認(rèn)WinSock DLL支持版本2.2
if(LOBYTE(wsaData.wVersion)!=2 || HIBYTE(wsaData.wVersion)!=2)
{
WSACleanup();
printf("Invalid WinSock version!\n");
return -1;
}
SOCKET sockServer = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addrServ;
memset(&addrServ, 0, sizeof(sockaddr_in));
int nSockAddrLen = sizeof(SOCKADDR);
addrServ.sin_family = AF_INET;
addrServ.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
addrServ.sin_port = htons(6666);
bind(sockServer, (sockaddr *)&addrServ, nSockAddrLen);
listen(sockServer, 5);
// 超時(shí)時(shí)間
timeval tvTimeout;
tvTimeout.tv_sec = 2;
tvTimeout.tv_usec = 0;
SOCKET sockConnected;
int nAddrLen = sizeof(sockaddr_in);
char szSendBuffer[1024] = {0};
char szRecvBuffer[1024] = {0};
cout<<"waiting connection..."<<endl;
while (true)
{
FD_ZERO(&fdReadSet);
FD_SET(sockServer, &fdReadSet);
int nRet = select(0, &fdReadSet, NULL, NULL, &tvTimeout);
if (-1 == nRet)
{
cout<<"error. break..."<<endl;
break;
}
else if (0 == nRet) // timeout
{
cout<<"timeout. select again..."<<endl;
continue;
}
else
{
cout<<"connection coming..."<<endl;
/*
如果套接字句柄還在fd_set里
說明客戶端已經(jīng)有connect的請(qǐng)求發(fā)過來(lái)了
馬上可以accept成功
*/
if (FD_ISSET(sockServer, &fdReadSet)) /*有新的客戶端連接到來(lái)*/
{
sockaddr_in addrClient; // 用于保存客戶端的網(wǎng)絡(luò)節(jié)點(diǎn)的信息
sockConnected = accept(sockServer, (sockaddr *)&addrClient, &nSockAddrLen);
if(sockConnected != INVALID_SOCKET) //創(chuàng)建成功
{
ZeroMemory(szSendBuffer, 1024);
//inet_ntoa將結(jié)構(gòu)轉(zhuǎn)換為十進(jìn)制的IP地址字符串
sprintf_s(szSendBuffer, 1024, "Welcome %s!", inet_ntoa(addrClient.sin_addr));
//成功建立連接后向客戶端發(fā)送數(shù)據(jù)蒂胞,結(jié)果將顯示在客戶端上
send(sockConnected, szSendBuffer, strlen(szSendBuffer) + 1, 0);
ZeroMemory(szRecvBuffer, 1024);
recv(sockConnected, szRecvBuffer, 1024, 0);
cout<<szRecvBuffer<<endl;
closesocket(sockConnected);
}
else
{
int nErrorCode = WSAGetLastError();
printf("the err code is:%d/n", nErrorCode);
}
}
}
}
closesocket(sockServer);
WSACleanup();
return 0;
}
References:
http://blog.csdn.net/kikilizhm/article/details/8201512
https://msdn.microsoft.com/en-us/library/windows/desktop/ms740141(v=vs.85).aspx
http://blog.csdn.net/ithzhang/article/details/8363951
http://blog.csdn.net/ysu108/article/details/7570571