你的
select()
调用仅要求可读套接字,因此退出时它将修改
readfds
删除所有不可读的套接字。因此,您只需遍历
clients
列表调用
FD_ISSET()
在每个插座上,就像您使用
master_socket
。你不应该在
else
阻止,因为侦听套接字可能正在接收新的入站客户端,而已建立的客户端也在接收数据。
一旦确定给定的客户端是否可读,就可以
recv()
来自该客户端的数据,如果
recv
调用返回-1(错误)或0(对等端正常断开),
close()
并将其从
客户
列表否则,根据需要对数据进行操作。
其他需要考虑的事项:
-
你的
客户
列表中不应包含值为-1的项。如果是这样,那么您的代码就有更大的问题需要解决。
-
不使用
clients.at()
在您的循环中,这只是浪费了开销。使用列表的
operator[]
相反
-
如果要修改
客户
循环时列出,不要递增
j
在每次循环迭代中,或者在每次擦除客户机时跳过客户机。否则,请使用迭代器而不是索引,如
erase()
将迭代器返回给列表中的下一个元素。无论如何,请考虑使用迭代器,因为您要删除的是迭代器,而不是索引。
-
您没有处理以下情况:
recv()
错误时可能返回-1。你需要
关闭()
并删除失败的客户端,而不仅仅是断开连接的客户端。
-
你在假设
recv()
返回以null结尾的数据,即使发送方实际发送以null结尾的数据,也不能保证返回。TCP是一种流式传输,任何给定的读取返回的字节数都可能少于请求的字节数。您必须注意
recv()
要知道实际接收了多少字节,否则可能会超出缓冲区的界限。
请尝试以下操作:
bool socks::start() {
if (listen(master_socket, backlog) < 0) {
std::cerr << "Failed to start listening." << std::endl;
return false;
}
std::cout << "Listening for connections on port " << listening_port << std::endl;
fd_set readfds;
char buf[256];
while (true) {
//clear the socket set
FD_ZERO(&readfds);
//add master socket to set
FD_SET(master_socket, &readfds);
int max_sd = master_socket;
// Add child sockets to set
for (size_t i = 0; i < clients.size(); ++i) {
//socket descriptor
int sd = clients[i];
FD_SET(sd, &readfds);
//highest file descriptor number, need it for the select function
if (sd > max_sd)
max_sd = sd;
}
// Wait indefinitely for an activity on one of the sockets
int activity = select(max_sd + 1, &readfds, NULL, NULL, NULL);
if (activity < 0) {
if (errno == EINTR) continue;
std::cerr << "select() failed" << std::endl;
return false;
}
// Handle incoming connections
if (FD_ISSET(master_socket, &readfds)) {
sockaddr_in address;
socklen_t addrlen = sizeof(address);
int new_socket = accept(master_socket, (sockaddr *) &address, &addrlen);
if (new_socket < 0) {
std::cerr << "Failed to accept incoming connection." << std::endl;
return false;
}
// Information about the new connection
std::cout << "New connection : "
<< "[SOCKET_FD : " << new_socket
<< " , IP : " << inet_ntoa(address.sin_addr)
<< " , PORT : " << ntohs(address.sin_port)
<< "]" << std::endl;
// Add connection to vector
clients.push_back(new_socket);
}
// Handle client disconnections / incoming data?
size_t j = 0;
while (j < clients.size()) {
int sd = clients[j];
if (FD_ISSET(sd, &readfds)) {
ssize_t rc = recv(sd, buf, sizeof(buf), 0);
if (rc <= 0) {
std::cout << "Client " << (rc < 0) ? "read error" : "disconnected" << "! [SOCKET_FD: " << sd << "]" << std::endl;
close(sd);
clients.erase(clients.begin() + j);
continue;
}
std::cout << "Client " << sd << " sent: ";
std::cout.write(buf, rc);
std::cout << std::endl;
}
++j;
}
}
return true;
}
请注意
选择()
具有一次可以处理的最大套接字数。如果你的客户数量超过
选择()
如果无法处理,则必须将列表拆分为多个调用
选择()
(可能在工作线程中调用它们以进行并行处理),或切换到
(e)poll()
而是:
bool socks::start() {
if (listen(master_socket, backlog) < 0) {
std::cerr << "Failed to start listening." << std::endl;
return false;
}
std::cout << "Listening for connections on port " << listening_port << std::endl;
std::vector<pollfd> readfds;
char buf[256];
pollfd pfd;
//add master socket to set
pfd.fd = master_socket;
pfd.events = POLLIN;
pfd.revents = 0;
readfds.push_back(pfd);
while (true) {
// Wait indefinitely for an activity on one of the sockets
int activity = poll(&readfds[0], readfds.size(), -1);
if (activity < 0) {
if (errno == EINTR) continue;
std::cerr << "poll() failed" << std::endl;
return false;
}
// Handle incoming connections, client disconnections, and incoming data
size_t j = 0;
while (j < readfds.size()) {
if (readfds[j].revents == 0) {
++j;
continue;
}
int sd = readfds[j].fd;
if (readfds[j].revents & POLLIN) {
if (sd == master_socket) {
sockaddr_in address;
socklen_t addrlen = sizeof(address);
int new_socket = accept(master_socket, (struct sockaddr *) &address, &addrlen);
if (new_socket < 0) {
std::cerr << "Failed to accept incoming connection." << std::endl;
return false;
}
// Information about the new connection
std::cout << "New connection : "
<< "[SOCKET_FD : " << new_socket
<< " , IP : " << inet_ntoa(address.sin_addr)
<< " , PORT : " << ntohs(address.sin_port)
<< "]" << std::endl;
// Add connection to vectors
clients.push_back(new_socket);
pfd.fd = new_socket;
pfd.events = POLLIN | POLLRDHUP;
pfd.revents = 0;
readfds.push_back(pfd);
}
else {
ssize_t rc = recv(sd, buf, sizeof(buf), 0);
if (rc > 0) {
std::cout << "Client " << sd << " sent: ";
std::cout.write(buf, rc);
std::cout << std::endl;
}
else if (rc == 0) {
readfds[j].revents |= POLLHUP;
} else {
readfds[j].revents |= POLLERR;
}
}
}
if (readfds[j].revents != POLLIN) {
if (sd == master_socket) {
...
}
else {
std::cout << "Client " << (readfds[j].revents & POLLERR) ? "read error" : "disconnected" << "! [SOCKET_FD: " << sd << "]" << std::endl;
close(sd);
clients.erase(std::find(clients.begin(), clients.end(), sd));
readfds.erase(readfds.begin() + j);
continue;
}
}
++j;
}
}
return true;
}