Select
select(maxfd+1,&rset,&wset,&eset,timeval* tv)
select函数成功返回时会将未准备好的描述符位清零
fd_set就是比特位的设置
五个参数分别: 1.fd_set的长度 2.关注哪些fd_set集合收到数据(recv) 3.关注哪些fd_set集合能写(send) 4.关注哪些fd_set 集合有错误 5.多长时间select轮询一次io
流程:
fd_set fdread;//一个io的数组,如果有数据来对应io下标(即socket)的数组就置1,无数据则置0
FD_ZERO(&fdread);//置空
FD_SET(sockfd,&fdread);//将fdread的对应比特位置为1
timeval tv;
tv.tv_sec=5;//5s
tv.tv_usec=0;
while(1)
{
int selection=select(socked+1,&fread,NULL,NULL,&tv);//返回多少个io
if(!selection||!FD_ISSET(sockfd,&fdread))//判断io是否为零或者sockfd是否存在于fdread(即对应fd_set是否被置为1)
{
break;
}else
{
memset(buffer,0,BUFFERSIZE);
int len=recv(sockfd,buffer,BUFFERSIZE,0);//可能出现一次recv无法接收全部包,因此需要定义一个变量接收全部的recv
}
}
#include<iostream>
#include<list>
#include<memory>
#if defined(__linux__)
#include<unistd.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#define closesocket close
#else
#include<WinSock2.h>
#define socklen_t int
#endif
#include<functional>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
int main()
{
#if defined(__WIN32)
WSADATA ws;
WSAStartup(MAKEWORD(2, 2), &ws);
#endif
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_ANY);
sin.sin_port = htons(9999);
int ret = ::bind(sockfd, (sockaddr*)&sin, sizeof(sin));
if ( ret == -1)
{
cout << "绑定失败" << endl;
}
listen(sockfd, 10);
fd_set rfd,wfd,rset,wset;
FD_ZERO(&rfd);
FD_SET(sockfd, &rfd);
FD_ZERO(&wfd);
FD_SET(sockfd, &wfd);
int maxfd = sockfd;
char buf[1024] = { 0 };
int len = 0;
while (1)
{
rset = rfd;
wset = wfd;
int nready = select(maxfd + 1, &rset, &wset, NULL, NULL);
#if 1
if (FD_ISSET(sockfd, &rset))
{
sockaddr_in clsin;
socklen_t len=sizeof(clsin);
int clientfd = accept(sockfd, (sockaddr*)&clsin,&len );
if (clientfd != -1)cout << "连接成功" << endl;
FD_SET(clientfd, &rfd);
maxfd = max(maxfd, clientfd);
send(clientfd, "yes", 4, 0);
}
for (int i = sockfd + 1; i <= maxfd; i++)
{
if (FD_ISSET(i, &rset)) {
len = recv(i, buf, sizeof(buf), 0);
if (len == 0) {
closesocket(i);
FD_CLR(i, &rfd);
}
else if (len > 0)
{
cout << buf << endl;
FD_SET(i, &wfd);
}
}
else if (FD_ISSET(i, &wset)) {
send(i, buf, len, 0);
//FD_SET(i, &rfd);
FD_CLR(i, &wfd);
}
}
#endif // 0
}
return 0;
}
fcntl.h
设置阻塞头文件 fcntl.h
Linux设置非阻塞fcntl(socket,F_SETFL,O_NONBLOCK)
windows设置非阻塞
int flag = 1;//flag为0阻塞,flag为1非阻塞
ioctlsocket(sockfd, FIONBIO, (unsigned long*)&flag);
文章参考与<零声教育>的C/C++linux服务器高级架构系统教程学习:Linux服务器高级架构师