Select
select系统调用是用来让我们的程序监视多个文件描述符的状态变化的;
程序会停在select这里等待,直到被监视的文件描述符有一个或多个发生了状态改变;
select函数原型
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
参数nfds是需要监视的最大的文件描述符值+1;
rdset,wrset,exset分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集 合及异常文件描述符的集合;
参数timeout为结构timeval,用来设置select()的等待时间
fd_set的接口
void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);
Select模拟实现
select.hpp
#include <cstdio>
#include <vector>
#include <sys/select.h>
#include "tcpsocket.hpp"
#define MAX_TIMEOUT 3000
class Select{
public:
Select():_maxfd(-1){
FD_ZERO(&_rfds);
}
bool Add(TcpSocket &sock) {
int fd = sock.GetFd();
FD_SET(fd, &_rfds);
_maxfd = _maxfd > fd ? _maxfd : fd;
return true;
}
bool Del(TcpSocket &sock) {
int fd = sock.GetFd();
FD_CLR(fd, &_rfds);
for (int i = _maxfd; i >= 0; i--) {
if (FD_ISSET(i, &_rfds)) {
_maxfd = i;
break;
}
}
return true;
}
bool Wait(std::vector<TcpSocket> *list, int outtime = MAX_TIMEOUT) {
struct timeval tv;
tv.tv_sec = outtime / 1000;
tv.tv_usec = (outtime % 1000) * 1000;
fd_set set;
set = _rfds;
int ret = select(_maxfd+1, &set, NULL, NULL, &tv);
if (ret < 0) {
perror("select error");
return false;
}if (ret == 0) {
printf("wait timeout\n");
return true;
}
for (int i = 0; i < _maxfd+1; i++) {
if (FD_ISSET(i, &set)) {
TcpSocket sock;
sock.SetFd(i);
list->push_back(sock);
}
}
return true;
}
private:
fd_set _rfds;
int _maxfd;
};
tcpsocket.hpp
#include <cstdio>
#include <string>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define MAX_LISTEN 5
#define CHECK_RET(q) if((q)==false){return -1;}
class TcpSocket {
public:
TcpSocket ():_sockfd(-1){}
int GetFd() {
return _sockfd;
}
void SetFd(int fd) {
_sockfd = fd;
}
bool Socket() {
_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (_sockfd < 0) {
perror("socket error");
return false;
}
return true;
}
bool Bind(const std::string &ip, uint16_t port) {
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip.c_str());
socklen_t len = sizeof(struct sockaddr_in);
int ret = bind(_sockfd, (struct sockaddr*)&addr, len);
if (ret < 0) {
perror("bind error");
return false;
}
return true;
}
bool Listen(int backlog = MAX_LISTEN) {
int ret = listen(_sockfd, backlog);
if (ret < 0) {
perror("listen error");
return false;
}
return true;
}
bool Accept(TcpSocket *new_sock, std::string *ip=NULL, uint16_t *port=NULL) {
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
int new_fd = accept(_sockfd, (struct sockaddr*)&addr, &len);
if (new_fd < 0) {
perror("accept error");
return false;
}
new_sock->_sockfd = new_fd;
if (ip != NULL) {
(*ip) = inet_ntoa(addr.sin_addr);
}
if (port != NULL) {
*port = ntohs(addr.sin_port);
}
return true;
}
bool Recv(std::string *buf) {
char tmp[4096] = {0};
int ret = recv(_sockfd, tmp, 4096, 0);
if (ret < 0) {
perror("recv error");
return false;
}else if (ret == 0) {
printf("connection broken\n");
return false;
}
buf->assign(tmp, ret);
return true;
}
bool Send(const std::string &data) {
int ret = send(_sockfd, data.c_str(), data.size(), 0);
if (ret < 0) {
perror("send error");
return false;
}
return true;
}
bool Close() {
if (_sockfd > 0) {
close(_sockfd);
_sockfd = -1;
}
return true;
}
bool Connect(const std::string &ip, uint16_t port) {
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip.c_str());
socklen_t len = sizeof(struct sockaddr_in);
int ret = connect(_sockfd, (struct sockaddr *)&addr, len);
if (ret < 0) {
perror("connect error");
return false;
}
return true;
}
private:
int _sockfd;
};
测试 select.cpp
#include <iostream>
#include "select.hpp"
int main (int argc, char *argv[])
{
if (argc != 3) {
std::cout << "Usage: ./main ip port\n";
return -1;
}
std::string ip = argv[1];
uint16_t port = std::stoi(argv[2]);
TcpSocket lst_sock;
CHECK_RET(lst_sock.Socket());
CHECK_RET(lst_sock.Bind(ip, port));
CHECK_RET(lst_sock.Listen());
Select s;
s.Add(lst_sock);
while(1) {
std::vector<TcpSocket> list;
bool ret = s.Wait(&list);
if (ret == false) {
continue;
}
for (auto sock : list) {
if (sock.GetFd() == lst_sock.GetFd()){
TcpSocket new_sock;
ret = lst_sock.Accept(&new_sock);
if (ret == false) {
continue;
}
s.Add(new_sock);
}else {
std::string buf;
ret = sock.Recv(&buf);
if (ret == false) {
sock.Close();
s.Del(sock);
continue;
}
std::cout << "client say: " << buf << std::endl;
std::cout << "server say: ";
buf.clear();
std::cin >> buf;
ret = sock.Send(buf);
if (ret == false) {
sock.Close();
s.Del(sock);
}
}
}
}
lst_sock.Close();
return 0;
}