Programming This 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.
Are you new to LinuxQuestions.org? Visit the following links:
Site Howto |
Site FAQ |
Sitemap |
Register Now
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.
 |
GNU/Linux Basic Guide
This 255-page guide will provide you with the keys to understand the philosophy of free software, teach you how to use and handle it, and give you the tools required to move easily in the world of GNU/Linux. Many users and administrators will be taking their first steps with this GNU/Linux Basic guide and it will show you how to approach and solve the problems you encounter.
Click Here to receive this Complete Guide absolutely free. |
|
 |
01-27-2013, 01:48 AM
|
#1
|
|
LQ Newbie
Registered: Jan 2009
Distribution: Fedora
Posts: 27
Rep:
|
Select system call usage
Hi all,
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.
|
|
|
|
01-27-2013, 07:09 AM
|
#2
|
|
Member
Registered: Dec 2012
Location: Washington DC area
Distribution: Fedora, CentOS, Slackware
Posts: 643
Rep: 
|
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.
|
|
|
|
01-27-2013, 11:19 PM
|
#3
|
|
LQ Newbie
Registered: Jan 2009
Distribution: Fedora
Posts: 27
Original Poster
Rep:
|
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdlib.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};
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("10.0.2.15");
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);
while(1) {
char ch;
int fd;
int nread;
sfds = serverfds;
cfds = clientfds;
sleep (5);
printf("server waiting\n");
result = select(FD_SETSIZE, &sfds, &cfds, NULL, NULL);
if(result < 1) {
perror("select:");
exit(1);
}
for(fd = 0; fd < FD_SETSIZE; fd++) {
if(FD_ISSET(fd,&sfds)) {
if(fd == server_sockfd) {
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);
}
else if (FD_ISSET(fd, &cfds)) {
for (j=0; j< maxclients; j++)
if (fd == clientfd[j]) {
recv(fd, &ch, 1, 0);
printf("serving client on fd %d\n", clientfd[j]);
ch++;
printf ("%c\n",ch);
}
}
}
}
}
}
Hi here is the complete code ... Thanks in Advance.
|
|
|
|
01-27-2013, 11:30 PM
|
#4
|
|
Moderator
Registered: Aug 2001
Location: Fargo, ND
Distribution: SuSE AMD64
Posts: 15,733
|
Moved: This thread is more suitable in Programming and has been moved accordingly to help your question get the exposure it deserves.
Also please place your code inside of [ code ] ... [ / code ] brackets to preserve the indentation. Spaces in particular can be difficult to notice.
|
|
|
|
01-28-2013, 03:48 AM
|
#5
|
|
Member
Registered: Dec 2012
Location: Washington DC area
Distribution: Fedora, CentOS, Slackware
Posts: 643
Rep: 
|
First a short note on the use of select
-- 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).
What system are you running this on?
|
|
|
|
01-28-2013, 03:50 AM
|
#6
|
|
Member
Registered: Dec 2012
Location: Washington DC area
Distribution: Fedora, CentOS, Slackware
Posts: 643
Rep: 
|
I added a few things to the program:
Code:
#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.
|
|
|
|
01-28-2013, 04:18 AM
|
#7
|
|
Member
Registered: Dec 2012
Location: Washington DC area
Distribution: Fedora, CentOS, Slackware
Posts: 643
Rep: 
|
Got it.
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:
Code:
#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);
}
}
}
}
}
}
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...
|
|
|
|
01-28-2013, 07:36 AM
|
#8
|
|
Member
Registered: Dec 2012
Location: Washington DC area
Distribution: Fedora, CentOS, Slackware
Posts: 643
Rep: 
|
As a last note, I think the problem was being caused an ambiguous/dangling else...
specifically the part around:
Code:
if(FD_ISSET(fd,&sfds)) {
if(fd == server_sockfd) {
...
}
} else if (FD_ISSET(fd, &cfds)) {
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.
|
|
|
|
01-29-2013, 07:51 PM
|
#9
|
|
LQ Newbie
Registered: Jan 2009
Distribution: Fedora
Posts: 27
Original Poster
Rep:
|
Hi pollard,
Thanks a lot for all your inputs.
|
|
|
|
01-29-2013, 11:33 PM
|
#10
|
|
LQ Newbie
Registered: Jan 2009
Distribution: Fedora
Posts: 27
Original Poster
Rep:
|
Hi pollard,
If socket descriptor can be made to non-blocking fd then hang can be prevented I think so.
|
|
|
|
01-30-2013, 06:22 AM
|
#11
|
|
Member
Registered: Dec 2012
Location: Washington DC area
Distribution: Fedora, CentOS, Slackware
Posts: 643
Rep: 
|
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.
|
|
|
|
| Thread Tools |
Search this Thread |
|
|
|
Posting Rules
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is Off
|
|
|
All times are GMT -5. The time now is 07:13 AM.
|
|
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.
|
Latest Threads
LQ News
|
|