WinSocket的select函数的用法(windows套接字比较研究)
来源:互联网 发布:淘宝店铺主图视频教程 编辑:程序博客网 时间:2024/05/22 16:44
总体上来说select函数的作用:
确定一个或多个套接口的状态,本函数用于确定一个或多个套接口的状态,对每一个套接口,调用者可查询它的可读性、可写性及错误状态信息,用fd_set结构来表示一组等待检查的套接口,在调用返回时,这个结构存有满足一定条件的套接口组的子集,并且select()返回满足条件的套接口的数目。
简单来说select用来填充一组可用的socket句柄,当满足下列之一条件时:
1.可以读取的sockets。当这些socket被返回时,在这些socket上执行recv/accept等操作不会产生阻塞;
2.可以写入的sockets。当这些socket被返回时,在这些socket上执行send等不会产生阻塞;
3.返回有错误的sockets。
select()的机制中提供一fd_set的数据结构,实际上市一long类型的数组,每一个数组元素都能与一打开的文件句柄(或者是其他的socket句柄,文件命名管道等)建立联系,建立联系的工作实际上由程序员完成,当调用select()的时候,由内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程那一socket或文件可读。
函数原型
int select(int nfds, fd_set FAR* readfds, fd_set FAR* writefds, fd_set FAR* exceptfds, const struct timeval FAR* timeout);
select的处理函数一般有:
同时和select配对使用的还有:
FD_CLR(s, *set)
Removes the descriptor s from set.
FD_ISSET(s, *set)
Nonzero if s is a member of the set. Otherwise, zero.
FD_SET(s, *set)
Adds descriptor s to set.
FD_ZERO(*set)
Initializes the set to the null set.
应该都知道,accept()函数是一个阻塞的函数,当有客户端请求连接服务端时才返回。如下面的例子
server端:
<span style="font-family:SimHei;font-size:18px;">#include <iostream>#include <Windows.h>using namespace std;#define PORT 4000#define IP_ADDRESS "127.0.0.1"#pragma comment(lib, "WS2_32.lib")int main(int argc, char* argv[]){WSADATA Ws;SOCKET ServerSocket, CientSocket;struct sockaddr_in LocalAddr, ClientAddr;int Ret = 0;int AddrLen = 0;HANDLE hThread = NULL;//Init Windows Socketif (WSAStartup(MAKEWORD(2, 2), &Ws) != 0){cout << "Init Windows Socket Failed::" << GetLastError() << endl;return -1;}ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (ServerSocket == INVALID_SOCKET){cout << "Create Socket Failed::" << GetLastError() << endl;return -1;}LocalAddr.sin_family = AF_INET;LocalAddr.sin_addr.s_addr = inet_addr(IP_ADDRESS);LocalAddr.sin_port = htons(PORT);memset(LocalAddr.sin_zero, 0x00, 8);Ret = bind(ServerSocket, (struct sockaddr*)&LocalAddr, sizeof(LocalAddr));if (Ret != 0){cout << "Bind Socket Failed::" << GetLastError() << endl;return -1;}Ret = listen(ServerSocket, 10);if (Ret != 0){cout << "listen Socket Failed::" << GetLastError() << endl;return -1;}cout << "服务端已经启动" << endl;char RecvBuffer[MAX_PATH];while (true){AddrLen = sizeof(ClientAddr);CientSocket = accept(ServerSocket, (struct sockaddr*)&ClientAddr, &AddrLen);//阻塞的if (CientSocket == INVALID_SOCKET){cout << "Accept Failed::" << GetLastError() << endl;break;}cout << "客户端连接::" << inet_ntoa(ClientAddr.sin_addr) << ":" << ClientAddr.sin_port << endl;int Ret = 0;while (true){memset(RecvBuffer, 0x00, sizeof(RecvBuffer));Ret = recv(CientSocket, RecvBuffer, MAX_PATH, 0);if (Ret == 0 || Ret == SOCKET_ERROR){cout << "客户端退出!" << endl;break;}cout << "接收到客户信息为:" << RecvBuffer << endl;}CloseHandle(hThread);}closesocket(ServerSocket);closesocket(CientSocket);WSACleanup();return 0;}</span>
client端:
<span style="font-family:SimHei;font-size:18px;">#include <iostream>#include <Windows.h>#pragma comment(lib, "ws2_32.lib")using namespace std;#define PORT 4000#define IP_ADDRESS "127.0.0.1"int main(int argc, char* argv[]){WSADATA Ws;SOCKET CientSocket;struct sockaddr_in ServerAddr;int Ret = 0;int AddrLen = 0;HANDLE hThread = NULL;char SendBuffer[MAX_PATH];//Init Windows Socketif (WSAStartup(MAKEWORD(2, 2), &Ws) != 0){cout << "Init Windows Socket Failed::" << GetLastError() << endl;return -1;}//Create SocketCientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (CientSocket == INVALID_SOCKET){cout << "Create Socket Failed::" << GetLastError() << endl;return -1;}ServerAddr.sin_family = AF_INET;ServerAddr.sin_addr.s_addr = inet_addr(IP_ADDRESS);ServerAddr.sin_port = htons(PORT);memset(ServerAddr.sin_zero, 0x00, 8);Ret = connect(CientSocket, (struct sockaddr*)&ServerAddr, sizeof(ServerAddr));if (Ret == SOCKET_ERROR){cout << "Connect Error::" << GetLastError() << endl;return -1;}else{cout << "连接成功!" << endl;}while (true){cin.getline(SendBuffer, sizeof(SendBuffer));Ret = send(CientSocket, SendBuffer, (int)strlen(SendBuffer), 0);if (Ret == SOCKET_ERROR){cout << "Send Info Error::" << GetLastError() << endl;break;}}closesocket(CientSocket);WSACleanup();return 0;}</span>上面的例子一个客户端只能接收处理一个客户端的请求!
加上select的应用,就能让一个server处理来自多个客户端的请求:
改写后的server端
<span style="font-family:SimHei;font-size:18px;">#include <WinSock2.h>#include <stdio.h>#pragma comment(lib,"ws2_32.lib")#define PORT 4000bool InitAndListern(SOCKET &sListen){WSADATA wsaData;sockaddr_in local;WORD version = MAKEWORD(2, 0);int ret = WSAStartup(version, &wsaData);if (ret != 0){printf("WSAStarup failed\n");return 0;}local.sin_family = AF_INET;local.sin_addr.s_addr = INADDR_ANY;local.sin_port = htons((u_short)PORT);sListen = socket(AF_INET, SOCK_STREAM, 0);if (sListen == INVALID_SOCKET){printf("Initial socket failed\n");return 0;}if (bind(sListen, (sockaddr*)&local, sizeof(local)) != 0){printf("Bind socket failed\n");return 0;}if (listen(sListen, 10) != 0){printf("Listen socket failed\n");return 0;}return 1;}int main(){SOCKET sListen;if (InitAndListern(sListen) == 0)return 0;printf("Server wait for client connect...\n");fd_set fdSocket;FD_ZERO(&fdSocket);FD_SET(sListen, &fdSocket);//将sListen添加进该集合while (true){fd_set fdRead = fdSocket;int nRet = select(NULL, &fdRead, NULL, NULL, NULL);//if (nRet <= 0)break;for (int i = 0; i < (int)fdSocket.fd_count; ++i){if (FD_ISSET(fdSocket.fd_array[i], &fdRead)){if (fdSocket.fd_array[i] == sListen){sockaddr_in addrRemote;int nAddrLen = sizeof(addrRemote);SOCKET sNew = ::accept(sListen, (sockaddr*)&addrRemote, &nAddrLen);FD_SET(sNew, &fdSocket);printf("Clietn %s connected\n", inet_ntoa(addrRemote.sin_addr));}else{char buffer[1024];memset(buffer, 0, 1024);int nRecev = recv(fdSocket.fd_array[i], buffer, 1024, 0);if (nRecev > 0){printf("Received Client Msg:%s\n", buffer);send(fdSocket.fd_array[i], buffer, strlen(buffer), 0);}else{closesocket(fdSocket.fd_array[i]);FD_CLR(fdSocket.fd_array[i], &fdSocket);}}}}}return 0;}</span>
select()的socket只有在有东西的时候才会读入,其实现过程不过如下:
....
int sockfd;
fd_set fdR;
struct timeval timeout=...;
...
for(;;){
FD_ZERO(&fdR);
FD_SET(&fdR);
switch(select(sockfd+1, &fdR, NULL, &timeout){
case -1:
error;
case 0:
error;
default:
if(FDD_ISSET(sockfd)){
accept();
...
}
}
}
select()方法中,所有文件描述符都是阻塞的。使用select判断一组文件描述符中是否有一个可读(写),如果没有就阻塞,直到有一个的时候就唤醒。
- WinSocket的select函数的用法(windows套接字比较研究)
- winsocket select函数笔记
- 套接字的select模型
- select函数的用法
- windows的select函数
- CSocket以及winsocket套接字的使用步骤(网络通信的重点知识)
- winsocket<一> 套接字基础
- select套接字准备好的条件
- C# 套接字的select选择模型
- 套接字的select、WsaAsyncSelect、WsaEventSelect模型
- 套接字的select、WsaAsyncSelect、WsaEventSelect模型
- C# select()函数的用法
- windows上解决select不能同时等待键盘和套接字输入的问题
- windows下的select函数
- LoadRunner WinSocket协议的扩展关联函数
- select函数是否阻塞与套接字的阻塞状态无关!!
- WinSocket模型的探讨——select模型
- WinSocket模型的探讨——select模型
- mysql的binlog日志
- ACM——01——1003: 【入门】求任意三位数各个数位上数字的和 【运算符】
- VS之双缓冲技术
- opencv2 threshod函数例程
- API of AsyncTask
- WinSocket的select函数的用法(windows套接字比较研究)
- 正则表达式
- Fiddler
- 用PHP编写CAPTCHA(验证码)
- opencv实现车牌识别之车牌号定位_2
- Java EE 7 教程 第二部分 平台基础 第3章 创建资源 第3.1节 资源和JNDI命名
- 第19天: stutct
- Android异步任务类分析
- service小结(一)