c++ select issue - c++

So I'm trying to build a async server... Here is a summary of what I have so far:
int sockfd;
int max;
fd_set socks;
set<int> conns;
bind();
listen(sockfd);
while(1){
FD_ZERO(&socks);
max = sockfd;
FD_SET(sockfd, &socks);
for(set<int>::iterator it=conns.begin(); it!=conns.end(); it++){
FD_SET(*it, &socks);
if(max < *it){
max = *it;
}
}
int res = select(max+1, &socks, NULL, NULL, NULL);
if(res < 0){
cerr << "ERROR with select" << endl;
break;
}else if(res){
if(FD_ISSET(sockfd, &socks)){
//new connection
int new_sockfd = accept();
conns.insert(new_sockfd);
}else{
for(set<int>::iterator it=conns.begin(); it!=conns.end(); it++){
if(FD_ISSET(*it, &socks){
char buffer[256];
read(buffer, 256, *it);
cout << buffer << endl;
close(*it);
conns.erase(*it);
}
}
}
}
}
What ends up happening is... If I connect a client-1, and then client-2. And then I try and send data using Client-2 and then Client-1... it works...
However, If I connect client-1 and then connect client-2... and then try to send data using client-1. Select() returns a -1...
Help?

Take a look into man pages for select. The important part is :
Under the following conditions, pselect() and select() shall fail and set errno to:
EBADF
One or more of the file descriptor sets specified a file descriptor that is not a valid open file descriptor.
EINTR
The function was interrupted before any of the selected events occurred and before the timeout interval expired.
If SA_RESTART has been set for the interrupting signal, it is implementation-defined whether the function restarts or returns with [EINTR].
EINVAL
An invalid timeout interval was specified.
EINVAL
The nfds argument is less than 0 or greater than FD_SETSIZE.
EINVAL
One of the specified file descriptors refers to a STREAM or multiplexer that is linked (directly or indirectly) downstream from a multiplexer.
errno should tell you what is wrong.
This is just a quess, but when you close the connection, your file descriptor becomes invalid. I guess the error from select should be EBADF

I think your code erasing from the set is suspect. once you call conns.erase(*it), your iterator is invalid (and incrementing it leads to undefined behavior).
Changing your loop to something like the following should resolve the issue:
for(set<int>::iterator it=conns.begin(); it!=conns.end();)
{
set<int>::iterator cur = it++;
if(FD_ISSET(*cur, &socks)){
char buffer[256];
read(buffer, 256, *cur);
cout << buffer << endl;
close(*cur);
conns.erase(*cur);
}
}

Related

Writing Data to Poll Invalid Socket Causes Uncatchable Exception

I am working on a game server that uses sockets and implemented a polling function that sends the message "[POLL]" over all player sockets in a lobby every second to notify the player clients that their connection is still alive.
If I disconnect on the client-side the socket is still polled with no errors, however, if I create a new connection with the same client (Gets a new FD and is added to the map as a second player), the whole server crashes without any exceptions/warnings/messages when it attempts to write to the previous socket FD. My call to Write on the socket is wrapped in a try/catch that doesn't catch any exceptions and, when debugging using gdb, I am not given any error messaging.
This is the Socket Write function:
int Socket::Write(ByteArray const& buffer)
{
if (!open)
{
return -1;
}
// Convert buffer to raw char array
char* raw = new char[buffer.v.size()];
for (int i=0; i < buffer.v.size(); i++)
{
raw[i] = buffer.v[i];
}
// Perform the write operation
int returnValue = write(GetFD(), raw, buffer.v.size()); // <- Crashes program
if (returnValue <= 0)
{
open = false;
}
return returnValue;
}
And this is the Poll function (Players are stored in a map of uint -> Socket*):
/*
Polls all connected players to tell them
to keep their connections alive.
*/
void Lobby::Poll()
{
playerMtx.lock();
for (auto it = players.begin(); it != players.end(); it++)
{
try
{
if (it->second != nullptr && it->second->IsOpen())
{
it->second->Write("[POLL]");
}
}
catch (...)
{
std::cout << "Failed to write to " << it->first << std::endl;
}
}
playerMtx.unlock();
}
I would expect to see the "Failed to write to " message but instead the entire server program exits with no messaging. What could be happening here?
I was unable to find a reason for the program crashing in the call to write but I was able to find a workaround.
I perform a poll operation on the file descriptor prior to calling write and I query the POLLNVAL event. If I receive a nonzero value, the FD is now invalid.
// Check if FD is valid
struct pollfd pollFd;
pollFd.fd = GetFD();
pollFd.events = POLLNVAL;
if (poll(&pollFd, 1, 0) > 0)
{
open = false;
return -1;
}

epoll_wait doesn't wake up until pressing on enter

I'm new with epoll.
My code is working fine. The epoll is storing my file-descriptor and wait until file-descriptor is "ready".
But, for some reason it will not wake up until I will press on Enter (even though data has already received to fd, and after enter I will immediately see all data that has been sent before).
After one enter it will work as expected (no enters needed and when fd is ready again it will wake up again).
Here is am essence of my code:
int nEventCountReady = 0;
epoll_event event, events[EPOLL_MAX_EVENTS];
int epoll_fd = epoll_create1(0);
if(epoll_fd == -1)
{
std::cout << "Error: Failed to create EPoll" << std::endl;
return ;
}
event.events = EPOLLIN;
event.data.fd = myfd;
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, 0, &event))
{
fprintf(stderr, "Failed to add file descriptor to epoll\n");
close(epoll_fd);
return ;
}
while(true)
{
std::cout << "Waiting for messages" << std::endl;
nEventCountReady = epoll_wait(epoll_fd, events, EPOLL_MAX_EVENTS, 30000); << Stuck until Enter will be pressed (at first while loop)
for(int i=0; i<nEventCountReady; i++)
{
msgrcv(events[i].data.fd, oIpCMessageContent, sizeof(SIPCMessageContent), 1, 0);
std::cout << oIpCMessageContent.buff << std::endl;
}
}
This
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, 0, &event))
should probably be
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, myfd, &event))
In the first line you tell epoll to monitor fd 0 which is typically the standard input. That's why it waits for it, e.g. for your Enter.
Note that your original code works only by coincidence. It just happens that when you Enter there is data in your myfd (and even if there's none msgrcv blocks). And once you pressed Enter it will wake up all the time since epoll knows that STDIN is ready but you didn't read from it.
Thanks to kamilCuk, I noticed that msgget doesn't return a file descriptor as I thought.
It returns a "System V message queue identifier".
And as freakish said before, System V message queues don't work with selectors like epoll.

How to clear a POSIX Message Queue?

I am currently working on a program that does IPC via Posix Message Queues. I now need a function that removes every message of that queue. The problem is: My code deadlocks. Currently I am trying the following:
void clear_mq(std::string queue_name)
{
struct mq_attr mq_attrs = {0, 10, sizeof(uint8_t), 0};
mqd_t mq = ::mq_open(queue_name.c_str(), O_WRONLY | O_CREAT, 00644, &mq_attrs);
if (mq < 0)
{
std::cout << "Error opening Queue" << std::endl;
exit(-1);
}
struct mq_attr num_messages;
if (mq_getattr(mq, &num_messages) == -1)
{
std::cout << "Error!" << std::endl;
exit(-1);
}
while (num_messages.mq_curmsgs > 0)
{
uint8_t buf;
mq_receive(mq, (char *)&buf, sizeof(uint8_t), NULL);
if (mq_getattr(mq, &num_messages) == -1)
{
std::cout << "Error!" << std::endl;
exit(-1);
}
}
mq_close(mq);
}
Can anyone point out what I am doing wrong? I do not understand why the receive is blocking... At that moment when I call clear_mq noone else is in the receive block...
Could be that mq_receive() fails and you end up in a endless loop.
mq_receive() can fail for various reasons, e.g. the buffer provided must at least have the size of the mq-maxsize.
You should check the return value of mq_receive() and exit the loop if it fails.
You IMHO have no deadlock. However, the mq_receive blocks until it receives a message (man mq_receive) because the queue is not open with O_NONBLOCK parameter while mq_open.
Please also ensure you do not neglect the return value of the mq_receive in the loop.
In case someone else has the problem.
When printing the errno I get error 9 (Bad file descriptor), which makes sense cause the message queue is only opened for write but you are trying to read from it. When you open the queue with O_RDWR see mq_open it should work.
A tip for debugging use mq_timedreceive so that you can check the error.

'pcap_loop' is not recording packets and isn't even running

I'm trying to do some simple packet capturing with pcap, and so I've created a handle to listen through eth0. My issue is with the pcap_loop(handle, 10, myCallback, NULL); line near the end of my code. I'm trying to use pcap_loop.
The expected output is supposed to be:
eth0
Activated!
1
2
3
...
10
Done processing packets!
Current output is missing the increments:
eth0
Activated!
Done processing packets!
Currently it's just skipping right through to "Done processing packets!" and I have no idea why. Even if it doesn't go to the callback, it should still be waiting on packets as the ;count' parameter (see documentation for pcap_loop) is set to 10.
#include <iostream>
#include <pcap.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
void myCallback(u_char *useless, const struct pcap_pkthdr* hdr, const u_char*packet){
static int count = 1;
std::cout <<count<<std::endl;
count ++;
}
int main(){
char errbuf[PCAP_ERRBUF_SIZE];
char * devName;
char* net;
char* mask;
const u_char*packet;
struct in_addr addr;
struct pcap_pkthdr hdr;
bpf_u_int32 netp;
bpf_u_int32 maskp;
pcap_if_t *devs;
pcap_findalldevs(&devs, errbuf);
devName = pcap_lookupdev(errbuf);
std::cout <<devName<<std::endl;
int success = pcap_lookupnet(devName, &netp, &maskp, errbuf);
if(success<0){
exit(EXIT_FAILURE);
}
pcap_freealldevs(devs);
//Create a handle
pcap_t *handle = pcap_create(devName, errbuf);
pcap_set_promisc(handle, 1);
pcap_can_set_rfmon(handle);
//Activate the handle
if(pcap_activate(handle)){
std::cout <<"Activated!"<<std::endl;
}
else{
exit(EXIT_FAILURE);
}
pcap_loop(handle, 10, myCallback, NULL);
std::cout <<"Done processing packets!"<<std::endl;
//close handle
pcap_close(handle);
}
pcap_findalldevs(&devs, errbuf);
That call isn't doing anything useful, as you're not doing anything with devs other than freeing it. (You also aren't checking whether it succeeds or fails.) You might as well remove it unless you have some need to know what all the devices on which you can capture are.
pcap_can_set_rfmon(handle);
That all isn't doing anything useful, as you're not checking its return value. If you are capturing on a Wi-Fi device, and you want to capture in monitor mode, you call pcap_set_rfmon() - not pcap_can_set_rfmon() - on the handle after creating and before activating the handle.
//Activate the handle
if(pcap_activate(handle)){
std::cout <<"Activated!"<<std::endl;
}
else{
exit(EXIT_FAILURE);
}
To quote the pcap_activate() man page:
RETURN VALUE
pcap_activate() returns 0 on success without warnings, PCAP_WARN-
ING_PROMISC_NOTSUP on success on a device that doesn't support promis-
cuous mode if promiscuous mode was requested, PCAP_WARNING on success
with any other warning, PCAP_ERROR_ACTIVATED if the handle has already
been activated, PCAP_ERROR_NO_SUCH_DEVICE if the capture source speci-
fied when the handle was created doesn't exist, PCAP_ERROR_PERM_DENIED
if the process doesn't have permission to open the capture source,
PCAP_ERROR_RFMON_NOTSUP if monitor mode was specified but the capture
source doesn't support monitor mode, PCAP_ERROR_IFACE_NOT_UP if the
capture source is not up, and PCAP_ERROR if another error occurred. If
PCAP_WARNING or PCAP_ERROR is returned, pcap_geterr() or pcap_perror()
may be called with p as an argument to fetch or display a message
describing the warning or error. If PCAP_WARNING_PROMISC_NOTSUP,
PCAP_ERROR_NO_SUCH_DEVICE, or PCAP_ERROR_PERM_DENIED is returned,
pcap_geterr() or pcap_perror() may be called with p as an argument to
fetch or display an message giving additional details about the problem
that might be useful for debugging the problem if it's unexpected.
This means that the code above is 100% wrong - if pcap_activate() returns a non-zero value, it may have failed, and if it returns 0, it succeeded.
If the return value is negative, it's an error value, and it has failed. If it's non-zero but positive, it's a warning value; it has succeeded, but, for example, it might not have turned promiscuous mode on, as the OS or device might not let promiscuous mode be set.
So what you want is, instead:
//Activate the handle
int status;
status = pcap_activate(handle);
if(status >= 0){
if(status == PCAP_WARNING){
// warning
std:cout << "Activated, with warning: " << pcap_geterror(handle) << std::endl;
}
else if (status != 0){
// warning
std:cout << "Activated, with warning: " << pcap_statustostr(status) << std::endl;
}
else{
// no warning
std::cout <<"Activated!"<<std::endl;
}
}
else{
if(status == PCAP_ERROR){
std:cout << "Failed to activate: " << pcap_geterror(handle) << std::endl;
}
else{
std:cout << "Failed to activate: " << pcap_statustostr(status) << std::endl;
}
exit(EXIT_FAILURE);
}

select() behaviour for writeability?

I have a fd_set "write_set" which contains sockets that I want to use in a send(...) call. When I call select(maxsockfd+1, NULL, &write_set, NULL, &tv) there it always returns 0 (timeout) although I haven't sent anything over the sockets in the write_set yet and it should be possible to send data.
Why is this? Shouldn't select return instantly when it's possible to send data over the sockets in write_set?
Thanks!
Edit: My code..
// _read_set and _write_set are the master sets
fd_set read_set = _read_set;
fd_set write_set = _write_set;
// added this for testing, the socket is a member of RemoteChannelConnector.
std::list<RemoteChannelConnector*>::iterator iter;
for (iter = _acceptingConnectorList->begin(); iter != _acceptingConnectorList->end(); iter++) {
if(FD_ISSET((*iter)->getSocket(), &write_set)) {
char* buf = "a";
int ret;
if ((ret = send((*iter)->getSocket(), buf, 1, NULL)) == -1) {
std::cout << "error." << std::endl;
} else {
std::cout << "success." << std::endl;
}
}
}
struct timeval tv;
tv.tv_sec = 10;
tv.tv_usec = 0;
int status;
if ((status = select(_maxsockfd, &read_set, &write_set, NULL, &tv)) == -1) {
// Terminate process on error.
exit(1);
} else if (status == 0) {
// Terminate process on timeout.
exit(1);
} else {
// call send/receive
}
When I run it with the code for testing if my socket is actually in the write_set and if it is possible to send data over the socket, I get a "success"...
I don't believe that you're allowed to copy-construct fd_set objects. The only guaranteed way is to completely rebuild the set using FD_SET before each call to select. Also, you're writing to the list of sockets to be selected on, before ever calling select. That doesn't make sense.
Can you use poll instead? It's a much friendlier API.
Your code is very confused. First, you don't seem to be setting any of the bits in the fd_set. Secondly, you test the bits before you even call select.
Here is how the flow generally works...
Use FD_ZERO to zero out your set.
Go through, and for each file descriptor you're interested in the writeable state of, use FD_SET to set it.
Call select, passing it the address of the fd_set you've been calling the FD_SET function on for the write set and observe the return value.
If the return value is > 0, then go through the write set and use FD_ISSET to figure out which ones are still set. Those are the ones that are writeable.
Your code does not at all appear to be following this pattern. Also, the important task of setting up the master set isn't being shown.