Here is the code i am using. Whenever i write something to the Stdin, it works, but it is not working for socket. It's not able to enter the loop for Socket. I am new to socket programming.
void HandleConnection(int socket)
{
fd_set rfd;
struct timeval tv;
int retval;
printf("%d",socket);
MakeNonBlocking(socket);
/* Watch stdin (fd 0) to see when it has input. */
FD_ZERO(&rfd);
while(1)
{
FD_SET(STDIN, &rfd);
FD_SET(socket, &rfd);
/* Wait up to five seconds. */
tv.tv_sec = 50;
tv.tv_usec = 0;
retval = select(2, &rfd,NULL, NULL, &tv);
if(retval == 0)
{
printf("No data within fifty seconds.\n");
exit(1);
}
if(FD_ISSET(socket,&rfd))
{
printf("socket wala\n");
recieve_message(&socket);
send_message(&socket);
}
if(FD_ISSET(STDIN,&rfd))
{
printf("stdin wala\n");
recieve_message(&socket);
send_message(&socket);
}
}
}
FDZERO must go before FDSET inside the loop
select(2, ...) should be select(highest filedescriptor +1, ...).
when select returns you should check for negative value in case of errors
you should consider using pselect instead of select.
Clear tv before you reinitialize it.
It appears that you don't understand how the nfds argument to select() is used. The man page addresses this explicitly:
The first nfds
descriptors are checked in each set; i.e., the descriptors from 0 through nfds-1 in the descriptor sets are examined. (Example:
If you have set two file descriptors "4" and "17", nfds should not be "2", but rather "17 + 1" or "18".)
So here is how you should rewrite your code.
int maxfd = (socket > STDIN ? socket : STDIN) + 1; /* select() requires the number of FDs to scan, which is max(fds)+1 */
while(1){
FD_ZERO(&rfd); /* This needs to be done each time through the loop */
/* Watch stdin (fd 0) to see when it has input. */
FD_SET(STDIN, &rfd);
FD_SET(socket, &rfd);
/* Wait up to five seconds. */
tv.tv_sec = 50;
tv.tv_usec = 0;
retval = select(maxfd, &rfd,NULL, NULL, &tv);
if(retval == 0)
{
printf("No data within fifty seconds.\n");
exit(1);
}
if(retval == -1) /* Check for error */
{
perror("Error from select");
exit(2);
}
if(FD_ISSET(socket,&rfd))
{
printf("socket wala\n");
recieve_message(&socket);
send_message(&socket);
}
if(FD_ISSET(STDIN,&rfd))
{
printf("stdin wala\n");
recieve_message(&socket);
send_message(&socket);
}
}
Related
I need to use select system call where I have to open two files (file descriptors) and do read operation on those files which are ready, I need to use some timeout after every 5ms and read from those files
Here is my sample code:
int main()
{
fd_set readfds,writefds;
ssize_t nbytes,bytes_read;
char buf[20];
int fd_1,fd_2,retval;
struct timeval tv;
fd_1 = open("test1.txt", O_RDWR);
if(fd_1 < 0) {
printf("Cannot open file...\n");
return 0;
}
fd_2 = open("test2.txt", O_RDWR);
if(fd_2 < 0) {
printf("Cannot open file_2...\n");
return 0;
}
/* Wait up to five seconds. */
tv.tv_sec = 5;
tv.tv_usec = 0;
for(;;)
{
retval = select(FD_SETSIZE, &readfds, &writefds, NULL, &tv);
printf("select");
perror("select");
exit(EXIT_FAILURE);
//}
for(int i=0; i < FD_SETSIZE; i++)
{
FD_ZERO(&readfds);
if (FD_ISSET(i, &readfds))
{
// read call happens here //
nbytes = sizeof(buf);
bytes_read = read(i, buf, nbytes);
printf("%ld", bytes_read);
}
else
{
perror("read");
exit(EXIT_FAILURE);
}
}
}
You need to initialize the file descriptors sets before calling select(). With your current code select ends up with EBADF.
Something like this should do it:
FD_ZERO (&writefds);
for(;;)
{
FD_ZERO (&readfds);
FD_SET(fd_1, &readfds);
FD_SET(fd_2, &readfds);
retval = select(FD_SETSIZE, &readfds, &writefds, NULL, &tv);
if (retval < 0) {
perror("select");
exit(EXIT_FAILURE);
}
for(int i=0; i<FD_SETSIZE; i++)
{
if (FD_ISSET(i, &readfds))
{
// read call happens here //
printf("reading from file: %d\n", i);
nbytes = sizeof(buf);
bytes_read = read(i, buf, nbytes);
printf("read %ld bytes\n", bytes_read);
}
}
}
And also you may want to check that for(;;) loop and only exit on error. Once your select behaves properly you can proceed with debugging inside your second for loop. You don't seem to use writefds here, so you may also just set it to NULL in select.
One more note: since you only have two files in the read set you could simply check FD_ISSET(fd_1) / FD_ISSET(fd_2) instead of iterating over all FD_SETSIZE entries.
I'm initializing a daemon in C in a Debian:
/**
* Initializes the daemon so that mcu.serial would listen in the background
*/
void init_daemon()
{
pid_t process_id = 0;
pid_t sid = 0;
// Create child process
process_id = fork();
// Indication of fork() failure
if (process_id < 0) {
printf("Fork failed!\n");
logger("Fork failed", LOG_LEVEL_ERROR);
exit(1);
}
// PARENT PROCESS. Need to kill it.
if (process_id > 0) {
printf("process_id of child process %i\n", process_id);
exit(0);
}
//unmask the file mode
umask(0);
//set new session
sid = setsid();
if(sid < 0) {
printf("could not set new session");
logger("could not set new session", LOG_LEVEL_ERROR);
exit(1);
}
// Close stdin. stdout and stderr
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
}
The main daemon runs in the background and monitors a serial port to communicate with a microcontroller - it reads peripherals (such as button presses) and passes information to it. The main functional loop is
int main(int argc, char *argv[])
{
// We need the port to listen to commands writing
if (argc < 2) {
fprintf(stderr,"ERROR, no port provided\n");
logger("ERROR, no port provided", LOG_LEVEL_ERROR);
exit(1);
}
int portno = atoi(argv[1]);
// Initialize serial port
init_serial();
// Initialize server for listening to socket
init_server(portno);
// Initialize daemon and run the process in the background
init_daemon();
// Timeout for reading socket
fd_set setSerial, setSocket;
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
char bufferWrite[BUFFER_WRITE_SIZE];
char bufferRead[BUFFER_READ_SIZE];
int n;
int sleep;
int newsockfd;
while (1)
{
// Reset parameters
bzero(bufferWrite, BUFFER_WRITE_SIZE);
bzero(bufferRead, BUFFER_WRITE_SIZE);
FD_ZERO(&setSerial);
FD_SET(fserial, &setSerial);
FD_ZERO(&setSocket);
FD_SET(sockfd, &setSocket);
// Start listening to socket for commands
listen(sockfd,5);
clilen = sizeof(cli_addr);
// Wait for command but timeout
n = select(sockfd + 1, &setSocket, NULL, NULL, &timeout);
if (n == -1) {
// Error. Handled below
}
// This is for READING button
else if (n == 0) {
// This timeout is okay
// This allows us to read the button press as well
// Now read the response, but timeout if nothing returned
n = select(fserial + 1, &setSerial, NULL, NULL, &timeout);
if (n == -1) {
// Error. Handled below
} else if (n == 0) {
// timeout
// This is an okay tiemout; i.e. nothing has happened
} else {
n = read(fserial, bufferRead, sizeof bufferRead);
if (n > 0) {
logger(bufferRead, LOG_LEVEL_INFO);
if (strcmp(stripNewLine(bufferRead), "ev b2") == 0) {
//logger("Shutting down now", LOG_LEVEL_INFO);
system("shutdown -h now");
}
} else {
logger("Could not read button press", LOG_LEVEL_WARN);
}
}
}
// This is for WRITING COMMANDS
else {
// Now read the command
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0 || n < 0) logger("Could not accept socket port", LOG_LEVEL_ERROR);
// Now read the command
n = read(newsockfd, bufferWrite, BUFFER_WRITE_SIZE);
if (n < 0) {
logger("Could not read command from socket port", LOG_LEVEL_ERROR);
} else {
//logger(bufferWrite, LOG_LEVEL_INFO);
}
// Write the command to the serial
write(fserial, bufferWrite, strlen(bufferWrite));
sleep = 200 * strlen(bufferWrite) - timeout.tv_usec; // Sleep 200uS/byte
if (sleep > 0) usleep(sleep);
// Now read the response, but timeout if nothing returned
n = select(fserial + 1, &setSerial, NULL, NULL, &timeout);
if (n == -1) {
// Error. Handled below
} else if (n == 0) {
// timeout
sprintf(bufferRead, "err\r\n");
logger("Did not receive response from MCU", LOG_LEVEL_WARN);
} else {
n = read(fserial, bufferRead, sizeof bufferRead);
}
// Error reading from the socket
if (n < 0) {
logger("Could not read response from serial port", LOG_LEVEL_ERROR);
} else {
//logger(bufferRead, LOG_LEVEL_INFO);
}
// Send MCU response to client
n = write(newsockfd, bufferRead, strlen(bufferRead));
if (n < 0) logger("Could not write confirmation to socket port", LOG_LEVEL_ERROR);
}
close(newsockfd);
}
close(sockfd);
return 0;
}
But the CPU usages is always at 100%. Why is that? What can I do?
EDIT
I commented out the entire while loop and made the main function as simple as:
int main(int argc, char *argv[])
{
init_daemon();
while(1) {
// All commented out
}
return 0;
}
And I'm still getting 100% cpu usage
You need to set timeout to the wanted value on every iteration, the struct gets modified on Linux so I think your loop is not pausing except for the first time, i.e. select() is only blocking the very first time.
Try to print tv_sec and tv_usec after select() and see, it's modified to reflect how much time was left before select() returned.
Move this part
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
inside the loop before the select() call and it should work as you expect it to, you can move many delcarations inside the loop too, that would make your code easier to maintan, you could for example move the loop content to a function in the future and that might help.
This is from the linux manual page select(2)
On Linux, select() modifies timeout to reflect the amount of time not slept; most other implementations do not do this. (POSIX.1-2001 permits either behavior.) This causes problems both when Linux code which reads timeout is ported to other operating systems, and when code is ported to Linux that reuses a struct timeval for multiple select()s in a loop without reinitializing it. Consider timeout to be undefined after select() returns.
I think the bold part in the qoute is the important one.
I am trying handle 4 different named unix pipes in a single listener process.
I tried to use the select to handle the file descriptor of the pipes.I opened all the named pipes in a non blocking mode
I am having a issue, select is not at all sleeping. Continuously running in a loop.
I dont know where is the problem in my code. I pasted my code below.
Always retaining the last file descriptor in select call eventhough it doesnt have a content in pipe.
Please suggest what is wrong in code?
Code
Pipe Open call(Invoked in Constructor)
diag_fd = open(DIAG_PIPE , O_RDONLY | O_NONBLOCK );
Main Loop
while(1)
{
FD_ZERO(&fds);
FD_SET(cp_fd, &fds);
FD_SET(diag_fd, &fds);
FD_SET(err_fd, &fds);
FD_SET(perf_fd, &fds);
if (select(cp_fd+1, &fds, NULL, NULL, &tv) < 0)
{
perror("select");
return ;
}
for (int fd = diag_fd; fd <= cp_fd; fd++)
{
if (FD_ISSET(fd, &fds))
{
if (fd == diag_fd)
{
ProcessDiagLogs();
}
else if (fd == err_fd)
{
ProcessErrLogs();
}
else if (fd == perf_fd)
{
ProcessPerfLogs();
}
else if (fd == cp_fd)
{
ProcessCPLogs();
}
}
}
Read call in One File Descriptor:
ProcessDiagLogs()
do
{
if ((num = read(diag_fd, s, BUF_LENGTH)) == -1)
perror("read");
else {
s[num] = '\0';
fputs(s, filed);
fflush(filed);
}
} while (num > 0);
select() allows you to monitor one or more file descriptors, waiting until one of them becomes "ready", i.e. data is available for.
The const struct timespec *timeout, tv, in your case specifies the timeout period, or how log he select waits for data until it returns ( this behaves like a sleep ). if the timeout is 0, the select return immediately, if it's NULL it can block indefinitely.
You don't show in your code how you initialized tv, but I'm going to guess it's zero, hence the behavior you are seeing.
Try initializing the timeout before you call select and see if that helps:
tv.tv_sec = 1;//or any other value you see fit
tv.tv_usec= 0;
I create one executable file named as "readmsg". Its source code is as below. The select() works if I only perform readmsg in shell (I can see the output of timeout).
But if I create a FIFO file via command: mknod /tmp/message p, and perform readmsg < /tmp/message in shell. In result, the select() can't return if I don't write something in /tmp/message. My question is: Why I can't get the timeout output?
the source code of "readmsg":
#define STDIN 0
fd_set fds;
struct timeval tv;
while (1) {
FD_ZERO(&fds);
FD_SET(STDIN, &fds);
tv.tv_sec = 1;
tv.tv_usec = 0;
ret = select(STDIN + 1, &fds, NULL, NULL, &tv);
if (ret > 0) {
printf("works\n");
if (FD_ISSET(STDIN, &fds)) {
// read ...
}
} else if (ret == 0) {
printf("timeout!!\n");
} else {
printf("interrupt\n");
}
}
Thanks #Mat. After adding printf() close to main(), there is not output either. Even there is not the process id of readmsg when perform ps.
So it proves the process of readmsg < /tmp/message is blocked before the FIFO is ready to be writen.
There isn't any error. In fact, the readmsg works well when reading messages from redirected FIFO file.
I have two nodes communicating with a socket. Each node has a read thread and a write thread to communicate with the other. Given below is the code for the read thread. The communication works fine between the two nodes with that code. But I am trying to add a select function in this thread and that is giving me problems (the code for select is in the comments. I just uncomment it to add the functionality). The problem is one node does not receive messages and only does the timeout. The other node gets the messages from the other node but never timesout. That problem is not there (both nodes send and receive messages) without the select (keeping the comments /* */).
Can anyone point out what the problem might be? Thanks.
void *Read_Thread(void *arg_passed)
{
int numbytes;
unsigned char *buf;
buf = (unsigned char *)malloc(MAXDATASIZE);
/*
fd_set master;
int fdmax;
FD_ZERO(&master);
*/
struct RWThread_args_template *my_args = (struct RWThread_args_template *)arg_passed;
/*
FD_SET(my_args->new_fd, &master);
struct timeval tv;
tv.tv_sec = 2;
tv.tv_usec = 0;
int s_rv = 0;
fdmax = my_args->new_fd;
*/
while(1)
{
/*
s_rv = -1;
if((s_rv = select(fdmax+1, &master, NULL, NULL, &tv)) == -1)
{
perror("select");
exit(1);
}
if(s_rv == 0)
{
printf("Read: Timed out\n");
continue;
}
else
{
printf("Read: Received msg\n");
}
*/
if( (numbytes = recv(my_args->new_fd, buf, MAXDATASIZE-1, 0)) == -1 )
{
perror("recv");
exit(1);
}
buf[numbytes] = '\0';
printf("Read: received '%s'\n", buf);
}
pthread_exit(NULL);
}
You must set up master and tv before each call to select(), within the loop. They are both modified by the select() call.
In particular, if select() returned 0, then master will now be empty.