Hello everybody!

I've wrote a very simple echo server, based on a book about LInux socket programming example and it works just fine. My only problem is that i want to use select() function to handle several connections at one time. I've read lots of reference concerning that function and tried several implementations (the one from IBM website and the one from one russian wiki) but they aren't working :(

As far as i understand, to use select() i must create two structures - one for timeout and one for select() internal variables, just like this:
struct timeval timeout;
struct fd_set master_set, working_set;
Then we send our socket to non-blocking mode (using fcntl()).
Then... Well, that's the place i've stuck. Simply could't understand how this should work.

The code now looks like

#include <stdio.h>
#include <sys/socket.h>
#include <resolv.h>
#include <arpa/inet.h>
#include <errno.h>
#include <sys/types.h>

#define MAXBUF		1024

int main(int Count, char *Strings[])
{   
	int sockfd;
	int listen_sd, max_sd, new_sd;
	struct sockaddr_in self;
	char buffer[MAXBUF];

    if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
	{
		perror("Socket");
		exit(errno);
	}

	memcpy(&self, 0, sizeof(self));
	self.sin_family = AF_INET;
	self.sin_port = htons(Strings[1]);
	self.sin_addr.s_addr = INADDR_ANY;

   	if ( bind(sockfd, (struct sockaddr*)&self, sizeof(self)) != 0 )
	{
		perror("socket--bind");
		exit(errno);
	}
	if ( listen(sockfd, 20) != 0 )
	{
		perror("socket--listen");
		exit(errno);
	}

	while (1)
	{	
		int clientfd;
		struct sockaddr_in client_addr;
		int addrlen=sizeof(client_addr);

		clientfd = accept(sockfd, (struct sockaddr*)&client_addr, &addrlen);
		printf("%s:%d connected\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

		send(clientfd, buffer, recv(clientfd, buffer, MAXBUF, 0), 0);

		close(clientfd);
	}

	close(sockfd);
	return 0;
}

So, i'm calling for help - a good reference, some simple examples, anything would be useful.
Maybe someone will show me the place in this code, where i should place select() and what it's arguments would be.
Of cource the main goal is to learn how to use the function.

Recommended Answers

All 10 Replies

Well, it seems i'd understand... We make socket non-blocking wia fcntl, then we use FD_ZERO for initializing descriptors and FD_SET to set them. Then in the main loop i use select and handle -1, 0 and >0 variants. Then i should determine whether socket is readable or writeable.
So, i'll try to make it this far and see what happens. Anyway, please make some replies am i right or not. Any tips concerning my code are appreciated.

You should not set a non-blocking node.
Select returns when a requested operation will not block. If a socket is in a non-blocking node, select returns immediately. That defeats the very purpose of select.

http://beej.us/guide/bgnet/

> send(clientfd, buffer, recv(clientfd, buffer, MAXBUF, 0), 0);
How many different return states do send() and recv() have between them?

How many are you checking? - ZERO!

> memcpy(&self, 0, sizeof(self));
Do you know the difference between memset() and memcpy()?


> struct timeval timeout;
But only if you're interested in timeouts.

> struct fd_set master_set, working_set;
Yes -

> Then we send our socket to non-blocking mode (using fcntl()).
AFAIK, this isn't necessary.

>If a socket is in a non-blocking node, select returns immediately. That >defeats the very purpose of select.
Sounds strange, as in all examples their authors use either fcntl or icntl (or something like that)... But thank you, i'll try this myself.

>Do you know the difference between memset() and memcpy()?
I know, this appears to be a mistake. I used bzero before, but changed it on mem*** because it doesn't work properly on SunOS 2.6.

>How many different return states do send() and recv() have between >them?
Well, i dunno. What i should do?

Salem, many thanks for your link! I've implemented that things in my code, so now it works. I'll post it here, maybe someone will find weak spots or things i can improve.

#include <stdio.h>
#include <sys/socket.h>
#include <resolv.h>
#include <arpa/inet.h>
#include <errno.h>

#define MY_PORT		9999
#define MAXBUF		1024

int main(int Count, char *Strings[])
{   	
	int sockfd;
	struct sockaddr_in self;
	char buffer[MAXBUF];
	fd_set master;
	fd_set read_fds;
	int fdmax;
	int addrlen;
	int newfd, i, j, rv;
	struct sockaddr_in client_addr;
	int nbytes;

	FD_ZERO(&master);
	FD_ZERO(&read_fds);

    if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
	{
		perror("Socket");
		exit(errno);
	}

	memset(&self,0, sizeof(self));
	self.sin_family = AF_INET;
	self.sin_port = htons(MY_PORT);
	self.sin_addr.s_addr = INADDR_ANY;

    if ( bind(sockfd, (struct sockaddr*)&self, sizeof(self)) != 0 )
	{
		perror("socket--bind");
		exit(errno);
	}

	if ( listen(sockfd, 20) != 0 )
	{
		perror("socket--listen");
		exit(errno);
	}
	FD_SET (sockfd, &master);
	fdmax=sockfd;

	for (;;){	
		int clientfd;		
		
		read_fds=master;
		if (select(fdmax+1, &read_fds, NULL, NULL, NULL)==-1){
		perror("select");
		exit(errno);
		}
		
		for(i=0;i<=fdmax;i++){
			if (FD_ISSET(i, &read_fds)){
				if (i==sockfd){
					addrlen=sizeof(client_addr);
					newfd=accept(sockfd, (struct sockaddr*)&client_addr, &addrlen);
					
					if (newfd == -1){
						perror("accept");
					}else {
						FD_SET(newfd, &master);
						if (newfd>fdmax){
							fdmax=newfd;
						}
						printf("selectserver: new connection on socket %d\n", newfd);
					}
				}else {
					if ((nbytes = recv(i, buffer, sizeof(buffer), 0))<=0){
						if (nbytes==0){
							printf("selectserver: socket %d hung up\n", i);
						}else{
							perror("recv");
						}
						close(i);
						FD_CLR(i, &master);
					}else {
						for(j=0;j<=fdmax;j++){
							if (FD_ISSET(j, &master)){
								if (j != sockfd && j!=i){
									if (send(j,buffer, nbytes, 0)==-1){
										perror("send");
									}
								}
							}
						}
					}
				}
			}
		}
	}
					
	return 0;
}

I see two problem with this code.

The first one is that send may also block. You need to send data only to those sockets which select reported as being writable. In other words, prepare and pass a write_fds as well, and analyze it in a similar manner.

The second one is that send is not guaranteed that all requested nbytes will be sent. You need to check how many bytes actually went out, and queue the rest of them for the next round.

>The first one is that send may also block. You need to send data only >to those sockets which select reported as being writable. In other >words, prepare and pass a write_fds as well, and analyze it in a >similar manner.

You mean create a for(i=0;i<=fdmax;i++){ loop №2 or if (FD_ISSET(i, &write_fds)){? I think that it should be second if there, 'cause this is the place where we exactly check the descriptors.

>The second one is that send is not guaranteed that all requested >nbytes will be sent. You need to check how many bytes actually >went out, and >queue the rest of them for the next round.

Yeah, i've read about this but completely forget to use. Thank you.

You mean create a for(i=0;i<=fdmax;i++){ loop №2 or if (FD_ISSET(i, &write_fds)){? I think that it should be second if there, 'cause this is the place where we exactly check the descriptors.

for(i = 0; i < fdmax; i++) {
    if(FD_ISSET(i, &read_fds) {
        handle_read(i);
    }
    if(FD_ISSET(i, &write_fds) {
        handle_write(i);
    }
}

The separate writing loop is OK as well. It's a matter of taste, really.

> memset(&self,0, sizeof(self));
I saw this again and gave up caring....

good luck with your future segfaults

Salem, in the tutorial you linked, the guy uses memset(&hints, 0, sizeof hints); so i copied this to my prog. As for me bzero() does the same and i always use it. Anyway, no sigfaults. Maybe you'll explain tour point?

Oh, finally... I made it to work. Thanks all of you!

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.