Hi I have a problem with my function, which responsible for contact between client and server:
#define MAX 1024
void connection(int sock)
{
char buffer[MAX];
int newsock;
int n;
int r;
if(write(sock,"Hello!\n", 6) < 0)
{
perror("Error: ");
}
do {
if(write(sock, "\n> ",3) < 0)
{
perror(" Error: ");
}
memset(buffer,'0',MAX); // fill buffer
n = read(sock,buffer,MAX -1 );
if (strncmp("get",buffer,3) == 0)
{
execl("/usr/bin/top","/usr/bin/top","-n 1");
}
else if (strncmp("quit",buffer,4) == 0)
{
write(sock, "Exit from program\n",17);
close(sock);
}
else
{
write(sock,"Wrong order!\n", 12);
}
}
while(n);
}
When client send "get" the program should sends him view from "top" order, unfortunately it does not work in my program.
Secondly, please judge this code. This is my first server program. I will be very grateful
And finally, how to change function to give clients possibility to action in program after send "get" order.
Regards and Happy New Year!
You are calling exec without calling fork. So you are replacing your entire server process with a copy of top. This is really unlikely to do what you want.
Very likely, you could accomplish your aims by opening a suitable pseudo-file from the /proc file system, reading the information, and sending it into your socket.
If you really want to use top, you have to use pipe, fork and exec(l) to run top, read it's output from a pipe, and then send that output to the client.
It occurs to me that you might be running in an environment in which the server automatically forks you (like some sort of CGI gateway), in which case your problem is that you need to fdopen to move the socket to be descriptor #1 before exec-ing. It would really help if you would tell us all about your environment by editing your question.
The output of "top" goes to the server's stdout, not out through the socket to the client. You'd have to adjust the stdout of the "top" process for this to work.
Related
I am trying to create a very basic client server communication between two processes using IPC via named pipes.
I have 2 pipes, namely fifo_client and fifo_server
I have the following two classes fifoclient.c and fifoserver.c that has the following lines of code to open the two pipes.
fifoclient.c
int client = open("fifo_client",O_WRONLY);
int server = open("fifo_server",O_RDONLY);
fifoserver.c
int client = open("fifo_client",O_RDONLY);
int server = open("fifo_server",O_WRONLY);
However, on simply changing the order of opening the client and server pipes in fifoserver.c, the program freezes.
This is how the code is written when it freezes:
fifoserver.c
int server = open("fifo_server",O_WRONLY);
int client = open("fifo_client",O_RDONLY);
Notice that the server pipe is opened before the client pipe in this case. This leads to the program not responding (possible race condition?).
Can someone explain what is happening and why?
EDIT:
Here's the entire code for both the classes:
fifoserver.c
#define BUFSIZE 20
#include<stdio.h>
#include<fcntl.h>
int main()
{
char buf[BUFSIZE];
int client = open("fifo_client",O_RDONLY);
int server = open("fifo_server",O_WRONLY);
if( server<0 || client < 0)
{
printf("Couldn't open file\n");
exit(1);
}
read(client,buf,BUFSIZE*sizeof(char));
printf("Client Says: %s\n",buf);
write(server,"Fine, Thank You!",BUFSIZE*sizeof(char));
close(server);
close(client);
return 0;
}
fifoclient.c
#define BUFSIZE 20
#include<stdio.h>
#include<fcntl.h>
int main()
{
char buf[BUFSIZE];
int client = open("fifo_client",O_WRONLY);
int server = open("fifo_server",O_RDONLY);
if(client <0 || server <0)
{
printf("ERROR! Couldn't open file!\n");
exit(1);
}
write(client,"Hello! How are you?",BUFSIZE*sizeof(char));
read(server,buf,BUFSIZE*sizeof(char));
printf("Server Says: %s\n",buf);
close(server);
close(client);
return 0;
}
From man 7 fifo:
The kernel maintains exactly one pipe object for each FIFO special file that is
opened by at least one process. The FIFO must be opened on both ends (reading and
writing) before data can be passed. Normally, opening the FIFO blocks until the
other end is opened also.
In other words, your open() call will block until there is a process on the other end of the pipe. This is not a race condition -- rather, it is a deadlock. If the processes do not open the pipes in the same order, they will wait forever on one another. So, as you noticed, the solution is that they must both open the fifos in the same order.
fifoclient.c
int client = open("fifo_client",O_WRONLY);
This open in the client will block until the FIFO is opened for reading.
fifoserver.c
int client = open("fifo_client",O_RDONLY);
This open, in the server, will unblock the previous open in the client.
Now, when you swap the lines in the server to look like
int server = open("fifo_server",O_WRONLY);
int client = open("fifo_client",O_RDONLY);
the client is blocked opening the client FIFO but the server is trying to open the server FIFO for writing (which will block until somebody opens it for reading). None of them can proceed to the line which will unblock the other.
I am using the preforking concept.
When I establish a socket in the server, the file descriptor value returned is 7. I know that it will allocate a available number for the file descriptor.
When I made the same child process to accept for new connections on the same socket, It wont accept() the connection.
But when I reset the value of file descriptor to 7, then It starts accepting the connections.
I am not finding the reason behind it. Can anyone through some light on this.
My code looks similar to this
for (;;)
{
int session_fd=accept(server_fd,0,0);
if (session_fd==-1)
{
if (errno==EINTR) continue;
die("failed to accept connection (errno=%d)",errno);
}
handle_session(session_fd);
close(session_fd);
server_fd = 7;
}
When I do some read and write operations, the value of file descriptor goes on increasing.
This doesn't make sense, reading or writing should be performed on the same socket for which the system returned a handle.
So I am just curious to know about the significance of value of socket file descriptor.
There is no significance, it's a process specific handle which is represented by an integer, usually it increases by 1 each time you open and/or create a new socket, etc.
I'm going to go out on a limb and assume your code looks something like this, where you bind server_fd and then fork off a child to deal with each connection.
If so, the value 7 doesn't mean anything. It just so happens that at that stage in your program, the lower numbers are already in use. If you opened some other files or sockets before calling listen, server_fd would have a different number.
A socket is just its number, so when you reset the value of the file descriptor, you're reading from your original, bound socket, not the new one that results from the accept call. And I suspect you don't want to be listening on the same socket twice, though I've never tried.
if (listen(server_fd,SOMAXCONN)) {
die("failed to listen for connections (errno=%d)",errno);
}
for (;;) {
int session_fd=accept(server_fd,0,0);
if (session_fd==-1) {
if (errno==EINTR) continue;
die("failed to accept connection (errno=%d)",errno);
}
pid_t pid=fork();
if (pid==-1) {
die("failed to create child process (errno=%d)",errno);
} else if (pid==0) {
close(server_fd);
handle_session(session_fd);
close(session_fd);
_exit(0);
} else {
close(session_fd);
}
}
Sorry for bothering you all. I found the reason behind the issue with change of session_fd value.
session_fd is a global variable. Since it's a huge legacy code, I was not aware that the value of session_fd was changed by some other socket.
Thanks all for providing your inputs :)
This is a question about socket programming for multi-client.
While I was thinking how to make my single client and server program
to multi clients,I encountered how to implement this.
But even if I was searching for everything, kind of confusion exists.
I was thinking to implement with select(), because it is less heavy than fork.
but I have much global variables not to be shared, so I hadn`t considered thread to use.
and so to use select(), I could have the general knowledge about FD_functions to utilize, but here I have my question, because generally in the examples on websites, it only shows multi-client server program...
Since I use sequential recv() and send() in client and also in server program
that work really well when it`s single client and server, but
I have no idea about how it must be changed for multi cilent.
Does the client also must be unblocking?
What are all requirements for select()?
The things I did on my server program to be multi-client
1) I set my socket option for reuse address, with SO_REUSEADDR
2) and set my server as non-blocking mode with O_NONBLOCK using fctl().
3) and put the timeout argument as zero.
and proper use of FD_functions after above.
But when I run my client program one and many more, from the second client,
client program blocks, not getting accepted by server.
I guess the reason is because I put my server program`s main function part
into the 'recv was >0 ' case.
for example with my server code,
(I`m using temp and read as fd_set, and read as master in this case)
int main(void)
{
int conn_sock, listen_sock;
struct sockaddr_in s_addr, c_addr;
int rq, ack;
char path[100];
int pre, change, c;
int conn, page_num, x;
int c_len = sizeof(c_addr);
int fd;
int flags;
int opt = 1;
int nbytes;
fd_set read, temp;
if ((listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
perror("socket error!");
return 1;
}
memset(&s_addr, 0, sizeof(s_addr));
s_addr.sin_family = AF_INET;
s_addr.sin_addr.s_addr = htonl(INADDR_ANY);
s_addr.sin_port = htons(3500);
if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)) == -1)
{
perror("Server-setsockopt() error ");
exit(1);
}
flags = fcntl(listen_sock, F_GETFL, 0);
fcntl(listen_sock, F_SETFL, flags | O_NONBLOCK);
//fcntl(listen_sock, F_SETOWN, getpid());
bind(listen_sock, (struct sockaddr*) &s_addr, sizeof(s_addr));
listen(listen_sock, 8);
FD_ZERO(&read);
FD_ZERO(&temp);
FD_SET(listen_sock, &read);
while (1)
{
temp = read;
if (select(FD_SETSIZE, &temp, (fd_set *) 0, (fd_set *) 0,
(struct timeval *) 0) < 1)
{
perror("select error:");
exit(1);
}
for (fd = 0; fd < FD_SETSIZE; fd++)
{
//CHECK all file descriptors
if (FD_ISSET(fd, &temp))
{
if (fd == listen_sock)
{
conn_sock = accept(listen_sock, (struct sockaddr *) &c_addr, &c_len);
FD_SET(conn_sock, &read);
printf("new client got session: %d\n", conn_sock);
}
else
{
nbytes = recv(fd, &conn, 4, 0);
if (nbytes <= 0)
{
close(fd);
FD_CLR(fd, &read);
}
else
{
if (conn == Session_Rq)
{
ack = Session_Ack;
send(fd, &ack, sizeof(ack), 0);
root_setting();
c = 0;
while (1)
{
c++;
printf("in while loop\n");
recv(fd, &page_num, 4, 0);
if (c > 1)
{
change = compare_with_pre_page(pre, page_num);
if (change == 1)
{
page_stack[stack_count] = page_num;
stack_count++;
}
else
{
printf("same as before page\n");
}
} //end of if
else if (c == 1)
{
page_stack[stack_count] = page_num;
stack_count++;
}
printf("stack count:%d\n", stack_count);
printf("in page stack: <");
for (x = 0; x < stack_count; x++)
{
printf(" %d ", page_stack[x]);
}
printf(">\n");
rq_handler(fd);
if (logged_in == 1)
{
printf("You are logged in state now, user: %s\n",
curr_user.ID);
}
else
{
printf("not logged in.\n");
c = 0;
}
pre = page_num;
} //end of while
} //end of if
}
} //end of else
} //end of fd_isset
} //end of for loop
} //end of outermost while
}
if needed for code explanation : What I was about to work of this code was,
to make kind of web pages to implement 'browser' for server.
I wanted to make every client get session for server to get login-page or so.
But the execution result is, as I told above.
Why is that?
the socket in the client program must be non-blocking mode too
to be used with non-blocking Server program to use select()?
Or should I use fork or thread to make multi client and manage with select?
The reason I say this is, after I considered a lot about this problem,
'select()' seems only proper for multi client chatting program... that many
'forked' or 'threaded' clients can pend to, in such as chat room.
how do you think?...
Is select also possible or proper thing to use for normal multi-client program?
If there something I missed to let my multi client program work fine,
please give me some knowledge of yours or some requirements for the proper use of select.
I didn`t know multi-client communication was not this much easy before :)
I also considered to use epoll but I think I need to understand first about select well.
Thanks for reading.
Besides the fact you want to go from single-client to multi-client, it's not very clear what's blocking you here.
Are you sure you fully understood how does select is supposed to work ? The manual (man 2 select on Linux) may be helpful, as it provides a simple example. You can also check Wikipedia.
To answer your questions :
First of all, are you sure you need non-blocking mode for your sockets ? Unless you have a good reason to do so, blocking sockets are also fine for multi-client networking.
Usually, there are basically two ways to deal with multi-clients in C: fork, or select. The two aren't really used altogether (or I don't know how :-) ). Models using lightweight threads are essentially asynchronous programming (did I mention it also depends on what you mean by 'asynchronous' ?) and may be a bit overkill for what you seem to do (a good example in C++ is Boost.Asio).
As you probably already know, the main problem when dealing with more than one client is that I/O operations, like a read, are blocking, not letting us know when there's a new client, or when a client has said something.
The fork way is pretty straighforward : the server socket (the one which accepts the connections) is in the main process, and each time it accepts a new client, it forks a whole new process just to monitor this new client : this new process will be dedicated to it. Since there's one process per client, we don't care if i/o operations are blocking or not.
The select way allows us to monitor multiple clients in one same process : it is a multiplexer telling us when something happens on the sockets we give it. The base idea, on the server side, is first to put the server socket on the read_fds FD_SET of the select. Each time select returns, you need to do a special check for it : if the server socket is set in the read_fds set (using FD_ISSET(...)), it means you have a new client connecting : you can then call accept on your server socket to create the connection.
Then you have to put all your clients sockets in the fd_sets you give to select in order to monitor any change on it (e.g., incoming messages).
I'm not really sure of what you don't understand about select, so that's for the big explaination. But long story short, select is a clean and neat way to do single-threaded, synchronous networking, and it can absolutely manage multiple clients at the same time without using any fork or threads. Be aware though that if you absolutely want to deal with non-blocking sockets with select, you have to deal extra error conditions that wouldn't be in a blocking way (the Wikipedia example shows it well as they have to check if errno isn't EWOULDBLOCK). But that's another story.
EDIT : Okay, with a little more code it's easier to know what's wrong.
select's first parameter should be nfds+1, i.e. "the highest-numbered file descriptor in any of the three sets, plus 1" (cf. manual), not FD_SETSIZE, which is the maximum size of an FD_SET. Usually it is the last accept-ed client socket (or the server socket at beginning) who has it.
You shouldn't do the "CHECK all file descriptors" for loop like that. FD_SETSIZE, e.g. on my machine, equal to 1024. That means once select returns, even if you have just one client you would be passing in the loop 1024 times ! You can set fd to 0 (like in the Wikipedia example), but since 0 is stdin, 1 stdout and 2 stderr, unless you're monitoring one of those, you can directly set it to your server socket's fd (since it is probably the first of the monitored sockets, given socket numbers always increase), and iterate until it is equal to "nfds" (the currently highest fd).
Not sure that it is mandatory, but before each call to select, you should clear (with FD_ZERO for example) and re-populate your read fd_set with all the sockets you want to monitor (i.e. your server socket and all your clients sockets). Once again, inspire yourself of the Wikipedia example.
server side code:
dirp=opendir(path);
if(dirp==NULL)
{
strcpy(err,"error:");
strcat(err,strerror(errno));
send(fd,err,sizeof(err),0);
}
else
{
printf("\nstream opened\n");
while((dp=readdir(dirp))!= NULL)
{
r=send(fd,dp->d_name,100,MSG_MORE);
if(r<0)
perror("\nsend:");
printf("\n%s",dp->d_name);
}
}
client:
while(recv(mainsocket,lsbuf,100,0)>0)
{
printf("\n %s",lsbuf);
bzero(lsbuf,sizeof(lsbuf));
}
the server side is printing all the filenames on the standard output,but on the client side the client is not receiving the last filename and program is getting blocked at that point
The problem is with the send syscall. You call it with MSG_MORE flag that means the more data will follow and send waits for more data without actually sending. The last chunk of data you should send without this flag. Thus your server side should look like:
dp = readdir(dirp);
if (dp != NULL)
{
/* each time check whether there are more data */
while((dp_next = readdir(dirp))!= NULL)
{
r = send(fd, dp->d_name, 100, MSG_MORE);
if (r < 0) {
perror("\nsend");
}
printf("\n%s",dp->d_name);
dp = dp_next;
}
/* send the last or the only record */
r = send(fd, dp->d_name, 100, 0);
if (r < 0) {
perror("\nsend");
}
}
Another posibility to fix the problem is to close the conenction with the close(fd) syscall. It send all data in the buffer before closing the connection. It's a less clean, but more simple solution.
Your client prints the newline before lsbuf, hence everything since the previous newline is lost in your output buffer.
Four solutions:
use printf("%s\n", lsbuf); instead of ..."\n %s"...
use puts(lsbuf);, which has the same effect, but is slightly more appropriate
use fflush(stdout) after your client loop to flush the output buffer
use unbuffered output, see setvbuf() for details
Note that this problem doesn't seem to be networking-related (although I'd substitute MSG_MORE with 0), it's merely a problem with output buffering.
On a sidenote, I strongly suggest to send strlen(dp->d_name) + 1 bytes instead of 100 bytes. This way, you won't send more bytes than necessary, and on the other hand you won't truncate the output if your directory entries happen to be larger than 100 bytes.
Also, neither your client nor your server checks whether send()/recv() returns 0, which means that the connection has been closed by the remote end.
UPDATE: i updated the code and problem description to reflect my changes.
I know now that i'm trying a Socket operation on nonsocket. or that my fd_set is not valid since:
select returns -1 and
WSAGetLastError()returns 10038.
But i can't seem to figure out what it is. Platform is Windows. I have not posted the WSAStartup part.
int loop = 0;
FILE *output
int main()
{
fd_set fd;
output = _popen("tail -f test.txt","r");
while(forceExit == 0)
{
FD_ZERO(&fd);
FD_SET(_fileno(output),&fd);
int returncode = select(_fileno(output)+1,&fd,NULL,NULL,NULL);
if(returncode == 0)
{
printf("timed out");
}
else if (returncode < 0)
{
printf("returncode: %d\n",returncode);
printf("Last Error: %d\n",WSAGetLastError());
}
else
{
if(FD_ISSET(_fileno(output),&fd))
{
if(fgets(buff, sizeof(buff), output) != NULL )
{
printf("Output: %s\n", buff);
}
}
else
{
printf(".");
}
}
Sleep(500);
}
return 0;
}
The new outcome now is of course the print out of the returncode and the last error.
You have some data ready to be read, but you are not actually reading anything. When you poll the descriptor next time, the data will still be there. Drain the pipe before you continue to poll.
As far as I can tell, Windows anonymous pipes cannot be used with non-blocking calls like select. So, while your _popen and select code looks good independently, you can't join the two together.
Here's a similar thread elsewhere.
It's possible that calling SetNamedPipeHandleState with the PIPE_NOWAIT flag might work for you, but MSDN is more than a little cryptic on the subject.
So, I think you need to look at other ways of achieving this. I'd suggest having the reading in a separate thread, and use normal blocking I/O.
First of all, as yourself and others have pointed out, select() is only valid for sockets under Windows. select() does not work on streams which is what _popen() returns. Error 10038 clearly identifies this.
I don't get what the purpose of your example is. If you simply want to spawn a process and collect it's stdout, just do this (which comes directly from the MSDN _popen page):
int main( void )
{
char psBuffer[128];
FILE *pPipe;
if( (pPipe = _popen("tail -f test.txt", "rt" )) == NULL )
exit( 1 );
/* Read pipe until end of file, or an error occurs. */
while(fgets(psBuffer, 128, pPipe))
{
printf(psBuffer);
}
/* Close pipe and print return value of pPipe. */
if (feof( pPipe))
{
printf( "\nProcess returned %d\n", _pclose( pPipe ) );
}
else
{
printf( "Error: Failed to read the pipe to the end.\n");
}
}
That's it. No select required.
And I'm not sure how threads will help you here, this will just complicate your problem.
The first thing that I notice is wrong is that you are calling FD_ISSET on your exceptfds in each conditional. I think that you want something like this:
if (FD_ISSET(filePointer,&fd))
{
printf("i have data\n");
}
else ....
The except field in the select is typically used to report errors or out-of-band data on a socket. When one of the descriptors of your exception is set, it doesn't mean an error necessarily, but rather some "message" (i.e. out-of-band data). I suspect that for your application, you can probably get by without putting your file descriptor inside of an exception set. If you truly want to check for errors, you need to be checking the return value of select and doing something if it returns -1 (or SOCKET_ERROR on Windows). I'm not sure of your platform so I can't be more specific about the return code.
select() first argument is the highest number file descriptor in your set, plus 1. (i.e. output+1)
select(output+1, &fd, NULL, &exceptfds, NULL);
The first FD_ISSET(...) should be on the fd_set fd.
if (FD_ISSET(filePointer, &fd))
Your data stream has data, then you need to read that data stream. Use fgets(...) or similar to read from the data source.
char buf[1024];
...
fgets(buf, sizeof(buf) * sizeof(char), output);
The first argument to select needs to be the highest-numbered file descriptor in any of the three sets, plus 1:
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
Also:
if(FD_ISSET(filePointer,&exceptfds))
{
printf("i have data\n");
}
Should be:
if(FD_ISSET(filePointer,&fd))
{
printf("i have data\n");
}
You should check the return code from select().
You also need to reset the fdsets each time you call select().
You don't need timeout since you're not using it.
Edit:
Apparently on Windows, nfds is ignored, but should probably be set correctly, just so the code is more portable.
If you want to use a timeout, you need to pass it into the select call as the last argument:
// Reset fd, exceptfds, and timeout before each select()...
int result = select(maxFDPlusOne, &fd, NULL, &exceptfds, &timeout);
if (result == 0)
{
// timeout
}
else if (result < 0)
{
// error
}
else
{
// something happened
if (FD_ISSET(filePointer,&fd))
{
// Need to read the data, otherwise you'll get notified each time.
}
}
since select doesn't work i used threads, specifically _beginthread , _beginthreadex.