ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
I am trying to write application pgm using select system call. I was creating three server sockets in a single process and each server socket keeps listening on the corresponding port then later added these three sockfd's to the FD_SET for the use of select call such that which sever/client fd is unblocked it will serve the fd .
Ex:
//create three server fd's
// add to FD set
FD_SET (server1, sockfds);
FD_SET (server2, sockfds);
FD_SET (server3, sockfds);
testfds = sockfds;
while (select (FD_SETSIZE, &testfds, 0))
{
loop for all fd's
{
if it is server fd then
{
// accept client
// add clientfd to sockfds
}
else {
// read message from client
}
}
testfds = sockfds;
}
But when I try to connect to the corresponding servers using client pgm, server process is throwing "Bus error". what could be the pbm?
Also, did anyone tried scenario as given below
Say three servers s1,s2, s3 are created in a single process and listening on ports x1,x2,x3. Now the clients should be connected to the servers s1,s2 and s3 on the corresponding ports and the server should serve both accepting client connections and also should serve clients by reading their messages. And the code should be written using select call.
Without the real code, the only thing I see is that the clientfd is never removed from sockfds - which would leave it with fds being closed but still in the list of valid fds. That in turn would cause errors when trying to read from them.
-- making the number of fds waiting on FD_SETSIZE works... but is slow.
The usual way of doing this is to use the largest fd you are waiting for + 1.
The usual limit on FD_SETSIZ is 1024, so for every loop (if it only has 1 fd) will be 1024. Where using the actual number would be closer to 4 or 5... It does mean that the actual usage of the fds is a bit more work.
Now some apparent bugs:
-- use of clientfd[i++] is problematical - Each connection increments i, but closing an fd does not decrement. There may only be 3 connections at one time, but repeated connections will overflow the array.
--- It looks like it should work... until a connection closes. The closed socket is not removed from the fd_set so it will alway be presented to the select, even though it is invalid. You should be getting a EBADF error from select.
--- The use of the readfd set and write fdset. Your code does a recv on a client fd... but the client fd is only ready for writing. It isn't in the readfdset to determine if data is available for the recv. Most of the time, the writefdset will always be set when a connection exists.
Right now, I'm not seeing any bus errors or segfaults (program itself doesn't work, but still looking into it - my symptom is that select returns something ready, but is neither in the read set or write set).
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main()
{
int server_sockfd, client_sockfd;
int server_len, client_len;
struct sockaddr_in server_address;
struct sockaddr_in client_address;
int result, i = 0, j, maxclients=3;
int clientfd[10] = {0};
int fdmax = 0;
fd_set serverfds, clientfds, sfds, cfds;
server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = inet_addr("127.0.0.1");
server_address.sin_port = htons(9734);
server_len = sizeof(server_address);
bind(server_sockfd, (struct sockaddr *)&server_address, server_len);
if (listen(server_sockfd, 3) < 0)
{
perror("listen");
exit(EXIT_FAILURE);
}
FD_ZERO(&serverfds);
FD_ZERO(&clientfds);
FD_ZERO(&sfds);
FD_ZERO(&cfds);
FD_SET(server_sockfd, &serverfds);
fdmax = server_sockfd; /* the only one so far */
while(1) {
char ch;
int fd;
int nread;
sfds = serverfds;
cfds = clientfds;
sleep (5);
printf("server waiting\n");
result = select(fdmax+1, &sfds, &cfds, NULL, NULL);
printf("select result=%d\n",result);
if(result < 1) {
perror("select:");
exit(1);
}
for(fd = 0; fd < fdmax+1; fd++) {
if(FD_ISSET(fd,&sfds)) {
if(fd == server_sockfd) {
printf("server getting client\n");
client_len = sizeof(client_address);
client_sockfd = accept(server_sockfd,
(struct sockaddr *)&client_address, &client_len);
clientfd[i++] = client_sockfd;
FD_SET(client_sockfd, &clientfds);
printf("adding client on fd %d\n", client_sockfd);
fdmax = client_sockfd > fdmax ?client_sockfd:fdmax;
}
else if (FD_ISSET(fd, &cfds)) {
printf("client ready\n");
for (j=0; j< maxclients; j++)
if (fd == clientfd[j]) {
printf("processing client\n");
recv(fd, &ch, 1, 0);
printf("serving client on fd %d\n", clientfd[j]);
ch++;
printf ("%c\n",ch);
}
}
}
}
}
Just for my testing. The include <stdio.h> was to eliminate a warning about printf not being defined. The other printfs are there to identify some goings on.
There seems a bit of an issue with how nesting worked.
Note the bugs are still there. I added braces for all if statements which seems to have allowed the handling to go through.
I was using telent utility for testing, so it used line mode (meaning you have to hit enter before any data is sent. You can set character mode if you want).
The server hangs on the recv while processing a client connection. The write fdset is always ready... and the server isn't waiting for data, so once it enters client handling, it hangs until data is provided.
Things get confused when there are two clients because of this. The first client will hang the server, and it won't matter what the second client does (in fact, the server won't even see the connection until the first client is given data...)
The final code I used has the localhost IP number, and a few additional prints, and a primitive fdmax to reduce the number of iterations:
To get a better example you need to put the client connections in the read fdset so that you know when data is available for reading. For this program, you can assume that you can always write (it is not always true, but things get much more complicated trying to handle multiplexing within a single threaded process).
Last edited by jpollard; 01-28-2013 at 04:21 AM.
Reason: missed a } in pasting...
In your original, you didn't have the braces on the else. This then associated the else part with the if(fd == server_sockfd), which is the wrong association. This meant that the only FD_ISSET tested was on the read socket set, which only had the server_sockfd set... and the client test never happens. Since the client set was always writable, the server just looped testing the listening socket.
Yes, it can. And easily too - just use the MSG_DONTWAIT for the flags parameter to recv. If no data is available you will get the EAGAIN or EWOULDBLOCK messages (different systems provide different errors.. either is valid).
But that makes it harder to identify when data is present. You can use the FIONREAD ioctl to determine how many bytes are present too. The FIONREAD is nice because you know how many bytes you have to process, before actually trying to process any - otherwise you have to depend on the return status from the read to say how many actually occurred. I tend to use read rather than recv for stream sockets; there is no other data to be obtained with a stream, as the accept gets the source address. Either one works so it may just be up to personal preference as to which to use.
This is also why network protocols are a pain - you always end up with a state diagram for a connection where the new data + current state determine what the application is going to do. It is also why many servers use threads - creating a thread to process a connection allows the thread to represent the state - and that eliminates having to track it separately.
The state/thread is needed to prevent a single threaded server from starving some connections - usually (for simplicity) you have a processing loop:
Code:
while more input
read input
generate buffer
output buffer
The problem with it is that if the client connection provides data fast enough, no other connection gets processed.
With this simple loop can be subsumed into the select loop, it presents problems when the "more input" is only partial - a packet in transit may get split, so not all of it arrives... With non-blocking I/O you don't know if the entire data is available (based on size of the expected packet). And that means you have to also include repacking operations. With blocking reads, you will get the entire data... but at the expense of not handling other connections that are ready to be processed.
So the simple case is more like (without states..., and only using the read fdset on select):
Code:
loop
wait for input via select
or handle errors...
for each possible stream
if stream ready
if server_fd,
accept connection
add connection to input fdset
else
get input
if end of file,
close stream
remove from input fdset
else
generate data buffer for
output data buffers to stream
if end of file
close stream
remove from input fdset
end loop
This is close to what you have...
Now the changes for non-blocking I/O:
Code:
loop
wait for input via select
or handle errors...
for each possible stream
if stream ready
if server_fd,
accept connection
add connection to input fdset
else
get input
if error,
if end of file,
close stream
remove from input fdset
else assume EWOULDBLOCK or EAGAIN
else
generate data buffer
output data buffer to stream
if error,
if end of file
close stream
remove from input fdset
else assume EWOULDBLOCK or EAGAIN
end loop
And that is only for a simple processing loop.
NOTE: The above will loose output data if the output cannot be sent (the error handling on the output to the stream).
To prevent this, you end up using the write fdset, and a buffer associated with the stream (don't forget to handle what to do when/if the buffer is full...). And this is part of the state information needed for a stream.
At least no stream will get starved for attention.
Because of the very simple processing (read a byte, increment it, write a byte) I don't expect any data losses. Not impossible, just hard to cause without writing a special client program that deliberately doesn't read the data.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.