c++ - C++ TCP 服务器:FD_ISSET() 并不总是在基本服务器中工作
问题描述
这是我即将实现的 HTTP 服务器的实现。
void Webserv::resetFdSets()
{
int fildes;
FD_ZERO(&to_read);
FD_ZERO(&to_write);
max_fd = -1;
Server_map::iterator it;
for (it = servers.begin(); it != servers.end(); ++it) //set server sockets to be read
{
fildes = it->second.getFd();
FD_SET(fildes, &to_read);
if (fildes > max_fd)
max_fd = fildes;
}
std::list<int>::iterator iter;
for (iter = accepted.begin(); iter != accepted.end(); ++iter) // set client sockets if any
{
fildes = (*iter);
FD_SET(fildes, &to_read);
FD_SET(fildes, &to_write);
if (fildes > max_fd)
max_fd = fildes;
}
}
接受()
void Webserv::acceptConnections()
{
int client_fd;
sockaddr_in cli_addr;
socklen_t cli_len = sizeof(sockaddr_in);
Server_map::iterator it;
for (it = servers.begin(); it != servers.end(); ++it)
{
ft_bzero(&cli_addr, sizeof(cli_addr));
if (FD_ISSET(it->second.getFd(), &to_read)) // if theres data in server socket
{
client_fd = accept(it->second.getFd(), reinterpret_cast<sockaddr*>(&cli_addr), &cli_len);
if (client_fd > 0) // accept and add client
{
fcntl(client_fd, F_SETFL, O_NONBLOCK);
accepted.push_back(client_fd);
}
else
throw (std::runtime_error("accept() failed"));
}
}
}
接收()
void Webserv::checkReadSockets()
{
char buf[4096];
std::string raw_request;
ssize_t bytes;
static int connections;
std::list<int>::iterator it;
for (it = accepted.begin(); it != accepted.end(); ++it)
{
if (FD_ISSET(*it, &to_read))
{
++connections;
std::cout << "Connection counter : " << connections << std::endl;
while ((bytes = recv(*it, buf, sizeof(buf) - 1, MSG_DONTWAIT)) > 0)
{
buf[bytes] = '\0';
raw_request += buf;
}
std::cout << raw_request << std::endl;
}
}
}
发送()
void Webserv::checkWriteSockets()
{
char buf[8096];
char http_success[] = {
"HTTP/1.1 200 OK\r\n"
"Server: This op server\r\n"
"Content-Length: 580\r\n"
"Content-Type: text/html\r\n"
"Connection: Closed\r\n\r\n"
};
std::list<int>::iterator it = accepted.begin();
while (it != accepted.end())
{
int cliSock = (*it);
if (FD_ISSET(cliSock, &to_write))
{
int response_fd = open("hello.html", O_RDONLY);
int bytes = read(response_fd, buf, sizeof(buf));
send(cliSock, http_success, sizeof(http_success) - 1, MSG_DONTWAIT); // header
send(cliSock, buf, bytes, MSG_DONTWAIT); // hello world
close(cliSock);
close(response_fd);
it = accepted.erase(it);
}
else
++it;
}
}
主循环:
void Webserv::operate()
{
int rc;
while (true)
{
resetFdSets(); // add server and client fd for select()
if ((rc = select(max_fd + 1, &to_read, &to_write, NULL, NULL)) > 0) // if any of the ports are active
{
acceptConnections(); // create new client sockets with accept()
checkReadSockets(); // parse and route client requests recv() ...
checkWriteSockets(); // send() response and delete client
}
else if (rc < 0)
{
std::cerr << "select() failed" << std::endl;
exit(1);
}
}
}
在 pastebin 上突出显示相同的代码 + Webserv 类定义:https ://pastebin.com/9B8uYumF
目前算法是这样的:
- 重置 fd_sets 以进行读写。
- 如果任何文件描述符触发选择,我们检查是否设置了服务器 FD,接受连接并将它们存储在整数列表中。
- 如果任何客户端 FD 是 FD_ISSET() 来读取 - 我们读取他们的请求并打印它。
- 最后,如果任何客户端 FD 准备好接收 - 我们在其中转储一个 html 页面并关闭连接。
在检查所有客户端描述符时,FD_ISSET() 在 80% 的情况下不应该返回 false。因此,我无法始终如一地获得客户请求。奇怪的是,它更频繁地使用 fd_set 返回 true 来写入套接字。这是一个演示,其中 10 次成功读取中有 2 次使用上述代码:https ://imgur.com/a/nzc21LV
我知道新客户端总是被接受,因为它总是在 acceptConnections() 中输入 if 条件。换句话说,listen FD 被正确初始化。
编辑:这是它的初始化方式:
void Server::init()
{
if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
throw (std::runtime_error("socket() failed"));
}
fcntl(listen_fd, F_SETFL, O_NONBLOCK);
int yes = 1;
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
if (bind(listen_fd, reinterpret_cast<sockaddr*>(&server_addr), sizeof(server_addr)))
{
throw (std::runtime_error("bind() failed"));
}
if (listen(listen_fd, 0))
throw (std::runtime_error("listen() failed"));
}
解决方案
推荐阅读
- python-3.x - 对于两个版本的 python,我的 pip 都不起作用
- ios - 无法在共享扩展中调用 CoreData 实体
- excel - 朱莉娅 Excel 对象
- python - 如果在 django 中设置了标志,则在模板中为 id 添加前缀
- sql-server - 在 SQL Server 中生成脚本时如何删除列的括号
- javascript - 错误:在 angular8 的 mat 表中找不到带有 arrayname => formarray 路径的控件?
- javascript - PHP在mysql SELECT中获取URL值
- graphql - 如何在 Lighthouse GraphQL 中打乱结果(随机排序)?
- opengl - 深度测试的问题
- php - Ajax 可以逐行返回从服务器接收到的数据吗?