Select loop in linux - how to make it better? - c

I have select with N pipe, my problem:
After catch ,,one" pipe - select don't work. Probably I don't remove i
descriptor. And here is my questions:
1) Do I really have to make dynamic array like vector in C and insert and remove i
2) How to make my code better? How Can I solve problem with select?
My code:
while(1)
{
sel = select(val+1,&set,NULL,NULL,&timeout);
if(sel < 0)
perror("Blad funkcji select");
else if(sel == 0)
printf("Brak komunikatow \n");
else{
for(i = 0; i < val; i++)
{
if(FD_ISSET(fd[i][0],&set))
{
while(read(fd[i][0],&buf,rozmiar) > 0)
write(1,&buf,rozmiar);
} // check if exist and write to stdout
} // end SELECT
for(i = 0, j =0; i< val; i++)
{
FD_SET(fd[i][0], &set);
}
timeout.tv_sec = 4;
timeout.tv_usec = 0;
}

RETURN VALUE top
On success, select() and pselect() return the number of file
descriptors contained in the three returned descriptor sets (that is,
the total number of bits that are set in readfds, writefds,
exceptfds) which may be zero if the timeout expires before anything
interesting happens. On error, -1 is returned, and errno is set to
indicate the error; the file descriptor sets are unmodified, and
timeout becomes undefined.
if(sel ==-1)
perror("Blad funkcji select");

Related

TCP Socket Multiplexing Send Large Data

Got some trouble with TCP socket multiplexing.
//socket is non-blocking
const int MAX = 4096;
char *buff[MAX];
char *p = buff;
int fd, rvalue;
rvalue = 0;
if ( (fd = open(path, O_RDONLY)) < 0 ) {
return errno;
} else {
int didsend, didread;
int shouldsend;
while ((didread = read(fd, buff, MAX)) > 0) {
p = buff;
shouldsend = didread;
while ( 1 ) {
didsend = send(sockfd, p, shouldsend, 0);
//if send succeeds and returns the number of bytes fewer than asked for then try to send rest part in next time.
if (didsend < shouldsend) {
p += didsent;
shouldsend -= didsend;
continue;
}
//if there is no place for new data to send, then wait a brief time and try again.
if ( didsend < 0 && (errno == EWOULDBLOCK || errno == EAGAIN) ) {
usleep(1000);
continue;
}
//if all data has been sent then sending loop is over.
if (didsend == shouldsend) {
break;
}
//send error
if ( didsend < 0 ) {
rvalue = errno;
break;
}
}
}
close(fd);
if (didread == -1) {
return errno;
}
return rvalue;
}
Assume I use an I/O Multiplexing function poll() or kqueue(), and non-blocking socket, then if there are only some small data like send a short message, it works fine.
But if it comes to large data, I mean larger than send()'s buffer size, since using non-blocking socket, send() will just send a portion of data, and return how much data it sends, the rest part of data can only be sent in another call of send(), but it takes time, and can't tell how long it will takes. So the second while() is actually a blocking send which using non-blocking socket.
Equivalent to:
//socket is blocking
const int MAX = 4096;
char *buff[MAX];
int fd, n;
if ( (fd = open(path, O_RDONLY)) < 0 ) {
return errno;
} else {
while ((n = read(fd, buff, MAX)) > 0) {
if (send(sockfd, buff, n, 0) < 0) {
return errno;
}
}
close(fd);
return 0;
}
So, what is the solution to this, multithreading might work but that's kind of wasting resource maybe.
This is the general pattern for a single-threaded server that works with multiple connections and non-blocking sockets.
It's primarily pseudo-code in C and doesn't do the necessary error checking. But it gives you an idea that for each accepted connection, you keep a struct instance that maintains the socket handle, request parsing state, response stream, and any other "state" members of that connection. Then you just loop using "select" to wait or having multiple threads doing this same thing.
Again this is only pseudo-code and uses select/poll as an example. You can get even more scalability with epoll.
while (1)
{
fd_set readset = {};
fd_set writeset = {};
for (int i = 0; i < number_of_client_connections; i++)
{
if (client_connections[i].reading_request)
FD_SET(client_connection.sock, &readset);
else
FD_SET(client_connection.sock, &writeset);
}
// add the listen socket to the read set
FD_SET(listen_socket, &readset);
select(n + 1, &readset, &writeset, &timeout); // wait for a socket to be ready (not shown - check for errors and return value)
if (FD_ISSET(listen_socket, &readset))
{
int new_client_socket = accept(listen_socket, &addr, &addrlength);
// create a struct that keeps track of the connection state data
struct ConnectionData client_connection = {};
client_connection.sock = new_client_socket;
client_connection.reading_request = 1; // awaiting for all the request bytes to come in
client_connections[number_of_client_connections++] = client_connection; // pseudo code, add the client_connection to the list
}
for (int i = 0; i < number_of_client_connections; i++)
{
if (client_connections[i].reading_request)
{
if (FD_ISSET(client_connections[i], &readset))
{
char buffer[2000];
int len = recv(client_connections[i].sock, buffer, 2000, 0);
// not shown - handle error case when (recv < 0)
// not shown - handle case when (recv == 0)
ProcessIncomingData(client_connections[i], buffer, len); // do all the request parsing here. Flip the client_connections[i].reading_request to 0 if ready to respond
}
}
else if (client_connections[i].reading_request == 0)
{
if (FD_ISSET(client_connections[i], &writeset))
{
client_connection* conn = &client_connections[i];
int len = send(conn->sock, conn->response_buffer + conn->txCount, conn->response_size - conn->txCount, 0);
conn->txCount += len;
if (conn->txCount == conn->response_size)
{
// done sending response - we can close this connection or change it to back to the reading state
}
}
}
}

Sockets in C, Non-blocking send() and recv() with select() method

I want to make a non-blocking send() and recv() with select() and FD_ISSET(). I run into this behavior in which FD_ISSET() will be true for the first socket, and all other sockets are always not ready. I am confused why that is happening and I am wondering if I am using select() properly.
If I sent 100 requests, eventually one of the socket other than the first will be ready to recv(), but I am not getting that behavior.
for(int i = 0; i < P - 1; i++) {
sockArr[i] = GetSocket(server, port);
if (sockArr[i] < 0) {
// handle error
}
fcntl(sockArr[i], F_SETFL, O_NONBLOCK);
if(sockArr[i] > maxfd) {
maxfd = sockArr[i];
}
}
fd_set sockSet;
for(int i = 0; i < P - 1; i++) {
numBytes = send(sockArr[i], request, requestLen, 0);
if (numBytes < 0 || numBytes != requestLen) {
// handle error
}
// read from other stackoverflow post you need to rest
// per select() call
FD_ZERO(&sockSet);
for(int i = 0; i < P - 1; i++) {
FD_SET(sockArr[i], &sockSet);
}
if(select(maxfd+1, &sockSet, NULL, NULL, NULL)) {
for(int j = 0; j < i; j++) {
if(FD_ISSET(sockArr[j], &sockSet)) { // <------ only true for j = 0
numBytes = recv(sockArr[j], buffer, MAXBUFLEN - 1, 0);
if (numBytes < 0) {
// handle error
}
}
}
}
}
Your first for loop needs to terminate after the send() calls, and you should then start another to handle the selects and receives.
You need to check for recv() returning zero as well as -1, and close the socket and remove it from the FD set in either case.
You also need to loop while j <= P.

Waiting for child processes when using select() for multiplexing

I am facing some trouble dealing with zombie processes. I wrote a simple server which creates tic tac toe matches between players. I am using select() to multiplex between multiple connected clients. Whenever there are two clients, the server will fork another process which execs a match arbiter program.
The problem is that select() blocks. So therefore, say if there is a match arbiter program running as a child process and it exits, the parent will never wait for the child if there are no incoming connections because select() is blocking.
I have my code here, apologies since it is quite messy.
while(1) {
if (terminate)
terminate_program();
FD_ZERO(&rset);
FD_SET(tcp_listenfd, &rset);
FD_SET(udpfd, &rset);
maxfd = max(tcp_listenfd, udpfd);
/* add child connections to set */
for (i = 0; i < MAXCLIENTS; i++) {
sd = tcp_confd_lst[i];
if (sd > 0)
FD_SET(sd, &rset);
if (sd > maxfd)
maxfd = sd;
}
/* Here select blocks */
if ((nready = select(maxfd + 1, &rset, NULL, NULL, NULL)) < 0) {
if (errno == EINTR)
continue;
else
perror("select error");
}
/* Handles incoming TCP connections */
if (FD_ISSET(tcp_listenfd, &rset)) {
len = sizeof(cliaddr);
if ((new_confd = accept(tcp_listenfd, (struct sockaddr *) &cliaddr, &len)) < 0) {
perror("accept");
exit(1);
}
/* Send connection message asking for handle */
writen(new_confd, handle_msg, strlen(handle_msg));
/* adds new_confd to array of connected fd's */
for (i = 0; i < MAXCLIENTS; i++) {
if (tcp_confd_lst[i] == 0) {
tcp_confd_lst[i] = new_confd;
break;
}
}
}
/* Handles incoming UDP connections */
if (FD_ISSET(udpfd, &rset)) {
}
/* Handles receiving client handles */
/* If client disconnects without entering their handle, their values in the arrays will be set to 0 and can be reused. */
for (i = 0; i < MAXCLIENTS; i++) {
sd = tcp_confd_lst[i];
if (FD_ISSET(sd, &rset)) {
if ((valread = read(sd, confd_handle, MAXHANDLESZ)) == 0) {
printf("Someone disconnected: %s\n", usr_handles[i]);
close(sd);
tcp_confd_lst[i] = 0;
usr_in_game[i] = 0;
} else {
confd_handle[valread] = '\0';
printf("%s\n", confd_handle); /* For testing */
fflush(stdout);
strncpy(usr_handles[i], confd_handle, sizeof(usr_handles[i]));
for (j = i - 1; j >= 0; j--) {
if (tcp_confd_lst[j] != 0 && usr_in_game[j] == 0) {
usr_in_game[i] = 1; usr_in_game[j] = 1;
if ((child_pid = fork()) == 0) {
close(tcp_listenfd);
snprintf(fd_args[0], sizeof(fd_args[0]), "%d", tcp_confd_lst[i]);
snprintf(fd_args[1], sizeof(fd_args[1]), "%d", tcp_confd_lst[j]);
execl("nim_match_server", "nim_match_server", usr_handles[i], fd_args[0], usr_handles[j], fd_args[1], (char *) 0);
}
close(tcp_confd_lst[i]); close(tcp_confd_lst[j]);
tcp_confd_lst[i] = 0; tcp_confd_lst[j] = 0;
usr_in_game[i] = 0; usr_in_game[j] = 0;
}
}
}
}
}
}
Is there a method which allows wait to run even when select() is blocking? Preferably without signal handling since they are asynchronous.
EDIT: Actually, I found out that select has a timeval data structure which we can specify the timeout. Would using that be a good idea?
I think your options are:
Save all your child descriptors in a global array and call wait() from a signal handler. If you don't need the exit status of your children in your main loop, I think this is the easiest.
Instead of select, use pselect -- it will return upon receiving a specified (set of) signal(s), in your case, SIGCHLD. Then call wait/WNOHANG on all child PIDs. You will need to block/unblock SIGCHLD at the right moments before/after pselect(), see here: http://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html
Wait on/cleanup child PIDs from a secondary thread. I think this is the most complicated solution (re. synchronization between threads), but since you asked, it's technically possible.
If you just want to prevent zombie processes, you could set up a SIGCHLD signal handler. If you want to actually wait for the return status, you could write bytes into a pipe (non-blocking, just in case) from the signal handler and then read those bytes in the select loop.
For how to handle SIGCHLD, see http://www.microhowto.info/howto/reap_zombie_processes_using_a_sigchld_handler.html -- you want to do something like while (waitpid((pid_t)(-1), 0, WNOHANG) > 0) {}
Perhaps the best approach is sending a single byte from the SIGCHLD signal handler to the main select loop (non-blocking, just in case) and doing the waitpid loop in the select loop when bytes can be read from the pipe.
You could also use a signalfd file descriptor to read the SIGCHLD signal, although that works only on Linux.

select works only once

/* Wait up to 1 min */
tv.tv_sec = 60;
tv.tv_usec = 0;
FD_ZERO(&readfd);
FD_ZERO(&writefd);
for(i=0;i<3;i++)
{
FD_SET(my_rdfd[i], &readfd);
FD_SET(my_wrfd[i], &writefd);
}
for(int i=0;i<10;i++)
{
retval = select((NRFDS)+1, &readfd, &writefd, (fd_set*)0, &tv);//NRFDS is a macro
if (retval == -1)
printf("select() error");
else if (retval)
{
printf("data found\n");
//do something with the data recieved
}
else
printf("Timeout.\n");
}
The thing is that for the first time select() inside the for loop works as expected, fine, for subsequent loops, it doesn't work. What am I doing wrong?
Thank you for your help.
Try putting this within the for loop:
for(i=0;i<10;i++)
{
tv.tv_sec = 60;
tv.tv_usec = 0;
FD_ZERO(&readfd);
FD_ZERO(&writefd);
for(j=0;j<3;j++)
{
FD_SET(my_rdfd[j], &readfd);
FD_SET(my_wrfd[j], &writefd);//Note that you should use a different inside forloop
}
//select statement here
}
When select() returns, it changes the sets to show which file descriptors have become ready for read/write/exception. All other flags would have been cleared.
It's important that you re-enable the file descriptors that were cleared prior to starting another select, otherwise, you will no longer be waiting on those file descriptors.

Socket programming problem in C

From the below piece of code, why I am getting Reading Socket for response
int Read(int sock, char *p, int size)
{
int remain, read=0;
remain = size;
while (remain > 0 ) {
if ((read = recv(sock, p, remain, 0)) < 0) {
/* Error */
return(read);
} else if (read == 0 || *p == 0x0a) {
/* EOF */
break;
}
remain -= read;
p += read;
}
return(size - remain);
}
while (!done)
{
printf("***Reading Socket for response***");
rsplen= Read(myVsHandle.sock,(char *)encXMLResponse,MAX_RSP_LEN);
if (rsplen < 0 )
{
printf("Internal Communication Error");
return -1;
}
else if (rsplen >0)
printf("Revieved response");
done++;
return 0;
else if (rsplen == 0)
{
printf("Reading socket");
}
You are waiting for MAX_RSP_LEN bytes to be read - is there that many bytes to be read? Maybe your process is stuck in a blocking read().
Also depending on the sort of socket you are recv()ing from, there is no guarantee on the amount of data you will read, so specifically looking for a value 0x0a may not work.
Your problem could be that you are not ending your output with a newline. Try ending your outputs with a newline (\n). stdout is line buffered, so you may not see anything for a long time if you don't output a newline.
Another possibility is that you don't return from Read() unless you read the specified number of bytes. Depending upon the value of MAX_RSP_LEN, and the amount of data available, Read() may wait forever.
Also, your test: *p == 0x0a looks suspicious. What are you testing here?
Edit: There is another "bug":
else if (rsplen >0)
printf("Revieved response");
done++;
return 0;
else...
You are missing curly braces. In the current form, the code shouldn't compile. Please post actual code.
This:
if ((read = recv(sock, p, remain, 0)) < 0) {
Should be
if ((read = recv(sock, p, remain, 0)) > 0) { // Greater then 0, because recv returns the number of bytes received if successful, if it fails -1.
You're missing curly braces around the:
else if(rsplen > 0)
... statements
It should be:
...
}else if (rsplen >0){
printf("Revieved response");
done++;
return 0;
} ...

Resources