Use of select with multiple sockets - c

I would like to manage the sockets so that I can only accept connections once and then listen to the file descriptor of each socket and intercept the messages sent.
After a lot of trouble, I don't understand why my code systematically goes into the FD_ISSET() function while I put the file descriptor in my fd_set.
From what I've seen on other topics, it seems that you have to accept the connection systematically? Except that if I do that my code goes into an infinite wait.
int main(int argc, char **argv) {
int socket = 0;
// My client 1 socket
int pOneSocket = 0;
// My client 2 socket
int pTwoSocket = 0;
struct sockaddr_in serv_addr;
int test = 0;
// Create the server socket to listen
createSocket(&socket,1337, &serv_addr);
fd_set readfds;
FD_ZERO(&readfds);
//Enter the server socket to readfs
FD_SET(socket, &readfds);
while (1) {
// Wait for changes on the server socket
test = select(1024, &readfds, NULL, NULL, NULL);
// If the socket fd is not on the readfs, then accept a new connecion
if (FD_ISSET(test, &readfds) == 0)
accept_connection(pOneSocket ? &pTwoSocket : &pOneSocket,
&socket, &serv_addr, &readfds);
// Handle the connection and retreive command from file descriptor
handleConnection(&test, &pOneSocket);
}
return 0;
}
On my handleConnection, I have all the code related to retreive commands:
void accept_connection(int *new_socket, int *socket, struct sockaddr_in *address, fd_set *readfds) {
int addrlen = sizeof(*address);
if((*new_socket = accept(*socket, (struct sockaddr *) address, (socklen_t *) &addrlen)) < 0) {
fprintf(stderr, "\n Accept failed \n");
exit(84);
}
FD_SET(*new_socket, readfds);
}
Currently, my code works to accept a connection, but gets stuck on the first command sent from the client.

select() modifies the fd_set object(s) you pass to it, so each loop iteration needs to make a copy of them, not pass the original fd_sets you're using to track your connections. Something like this:
FD_SET(socket, &readfds);
while (1) {
// Wait for changes on the sockets we want to read
fd_set ready = readfds;
test = select(1024, &ready, NULL, NULL, NULL);
// If the socket fd is ready, then accept a new connecion
if (FD_ISSET(socket, &ready))
accept_connection(pOneSocket ? &pTwoSocket : &pOneSocket,
&socket, &serv_addr, &readfds);
... check the other fds that have previously been accepted to see if they're ready

Related

How to read GET request from client C

My program establishes an HTTP server. After calling listen() and accept(), how do I read a GET request like this:
GET /path HTTP/1.1\r\n\r\n
Assuming you are using blocking socket, first you need to know if data is available to read. That you can do using select api.
Next accept returns one socket FD to you. Using that socket FD you can receive data.
Accept code example
struct sockaddr_in client_addr;
int addr_len;
int new_fd;
addr_len = sizeof(struct sockaddr_in);
new_fd = accept(socket_fd, (struct sockaddr *)&client_addr,(socklen_t *)&addr_len);
Select and read example
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 100;
ret_value = select(max_fd + 1, READ_FD_SETS, NULL , NULL , &timeout);
if ((ret_value <= 0) && (errno!=EINTR))
{
//error
}
else
{
//read data now
rc = recv(new_fd, buffer, buffer length, 0)
}
Note: If you want better performance you may like R&D on epoll sockets.

print value of fds in FD_SET

Is there anyway of printing the state of a socket in a fd_set?
Say i have this code:
int main(int argc, char * argv[]) {
int sockfd, newfd, i;
struct sockaddr_un sv_addr, cli_addr;
int sv_len, cli_len;
fd_set testmask, mask;
if ((sockfd = socket(AF_UNIX,SOCK_STREAM,0))<0) {
perror("Error creating socket");
exit(-1);
}
bzero((char*)&sv_addr,sizeof(sv_addr));
sv_addr.sun_family = AF_UNIX;
strcpy(sv_addr.sun_path,UNIXSTR_PATH);
sv_len=sizeof(sv_addr.sun_family)+strlen(sv_addr.sun_path);
unlink(UNIXSTR_PATH);
if(bind(sockfd,(struct sockaddr*)&sv_addr,sv_len)<0) {
perror("Error binding socket");
exit(-1);
}
listen(sockfd, 15);
FD_ZERO(&testmask);
FD_SET(sockfd,&testmask);
for(;;) {
mask = testmask;
select(MAXSOCKS,&mask,0,0,0);
if(FD_ISSET(sockfd,&mask)) {
cli_len = sizeof(cli_addr);
newfd = accept(sockfd, (struct sockaddr*)&cli_addr, &cli_len);
echo(newfd);
close(newfd);
}
for(i=0;i<MAXSOCKS;i++) {
if (FD_ISSET(i, &mask)) {
close(i);
FD_CLR(i, &mask);
}
}
}
close(sockfd);
return 0;
}
Everything is working in my program (its an echo server, the client sends a line and the server just echos it back).
I would like to, after the select call, print in the server terminal something like;
00011011011
This means, print the socks that are ready to be handled.
Is there anyway i could do this?
Also, what should i do in the end of the for loop? I know i have to somehow clear the fd_set. The way i did it (the small for loop closing and FD_CLR the fd_set) its correct? Or i should i do it another way?
PS: Sorry for my english or any mistakes. :)
[This does not answer your question, but refers to a comment to the OP and is too long for another comment]
From man select:
nfds is the highest-numbered file descriptor in any of the three sets, plus 1.
nfds is not a constant! The man-pages does not read:
[...] the highest-possible-numbered file descriptor [...]
nfds dynamically has to describe the fd_sets passed to select().
int nfds = sockfd + 1;
for(;;) {
mask = testmask;
select(nfds, &mask, 0, 0, 0);
if(FD_ISSET(sockfd,&mask)) {
cli_len = sizeof(cli_addr);
newfd = accept(sockfd, (struct sockaddr*)&cli_addr, &cli_len);
echo(newfd);
close(newfd);
}
for(i = 0; i < nfds; ++i) {
if (FD_ISSET(i, &mask)) {
close(i);
FD_CLR(i, &mask);
}
}
}
Adjust nfds for every socket descriptor being add to fd_set passed to select().
After select call you checked the sockfd. If that's true, means that a client try to connect your server. Then you accept the connection.
newfd = accept(sockfd, (struct sockaddr*)&cli_addr, &cli_len);
newfd is the fd number you know, between client and server. Here, you don't still read the clientfd(newfd) data. After the connection accepted, you read data on the clentfd like that
read(newfd,buffer,sizeof(buffer))
Your data sent from client, now in buffer. Then maybe you can echo or write() in clientfd.
Your code sent to client , fd number between server and client.
Also if listen always the client(s), after the accept connection, you have to set your client fd in readfds(mask in your code) like FDSET(newfd,&mask)
Then you can listen the client(s) always

send() and sendto() blocking in a file transfer program

it seems that when i use send() function (in a TCP file transfer program) like this
while((count = recv(socketConnection, buff, 100000, 0))>0)
myfile.write(buff,count);
the function recv() just waits untill the whole data comes and exits the loop when it is no more receiving any data but in a similar program for a UDP program
while((n = recvfrom(sockfd,mesg,1024,0,(struct sockaddr *)&cliaddr,&len))>0)
myfile.write(mesg,n);
the recvfrom() function just blocks and does not exit the loop for some reason, as far as i know both recv() and recvfrom() are blocking right?? Then why the difference. Does it have something to do with the functions or just the nature of TCP,UDP(which i guess is not a reason)??
P.S. Please help me understand this guys, I'm a newbie to socket programming and networking.
EDIT: full server program for both TCP and UDP
UDP server (with recvfrom() )
int i=0;
int sockfd,n;
struct sockaddr_in servaddr,cliaddr;
socklen_t len;
char mesg[1024];
sockfd=socket(AF_INET,SOCK_DGRAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
servaddr.sin_port=htons(32000);
bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
ofstream myfile;
// fcntl(sockfd,F_SETFL,O_NONBLOCK);
myfile.open("2gb",ios::out);
while((n = recvfrom(sockfd,mesg,1024,0,(struct sockaddr *)&cliaddr,&len))>0)
myfile.write(mesg,n);
TCP (recv() ) server program
struct sockaddr_in socketInfo;
char sysHost[MAXHOSTNAME+1]; // Hostname of this computer we are running on
struct hostent *hPtr;
int socketHandle;
int portNumber = 8070;
//queue<char*> my_queue;
bzero(&socketInfo, sizeof(sockaddr_in)); // Clear structure memory
gethostname(sysHost, MAXHOSTNAME); // Get the name of this computer we are running on
if((hPtr = gethostbyname(sysHost)) == NULL)
{
cerr << "System hostname misconfigured." << endl;
exit(EXIT_FAILURE);
}
if((socketHandle = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
close(socketHandle);
exit(EXIT_FAILURE);
}
// std::cout<<"hi starting server";
socklen_t optlen;
int rcvbuff=262144;
optlen = sizeof(rcvbuff);
socketInfo.sin_family = AF_INET;
socketInfo.sin_addr.s_addr = htonl(INADDR_ANY);
socketInfo.sin_port = htons(portNumber); // Set port number
if( bind(socketHandle, (struct sockaddr *) &socketInfo, sizeof(socketInfo)) < 0)
{
close(socketHandle);
perror("bind");
exit(EXIT_FAILURE);
}
listen(socketHandle, 1);
int socketConnection;
if( (socketConnection = accept(socketHandle, NULL, NULL)) < 0)
{
exit(EXIT_FAILURE);
}
close(socketHandle);
time_start(boost::posix_time::microsec_clock::local_time());
int rc = 0; // Actual number of bytes read
int count=0;
char *buff;
int a=100000;
buff=new char[a];
ofstream myfile;
myfile.open("345kb.doc",ios::out|ios::app);
if(myfile.is_open())
{
long i=0;
while((count = recv(socketConnection, buff, 100000, 0))>0)
{
myfile.write(buff,count);
}}
the function recv() just waits untill the whole data comes and exits the loop when it is no more receiving any data
recv() on a TCP connection returns 0 when the sending side has closed the connection and this is the condition for your loop to terminate.
for a UDP program the recvfrom() function just blocks and does not exit the loop for some reason,
Because UDP is a connection-less protocol hence there is no special return code from recv() for a closed UDP connection. Unless someone sends you a 0-length datagram.
recv() will end the loop because at the other side the socket is closed, so recv() will return 0 (socket gracefully closed) whereas, recvfrom that does not have that signal, it does not know about closing, because it's an unconnected socket. It's stay there until it receives a packet or timeout, with UDP you need a way to tell that the communication is over (finish).

client socket unable to receive data using poll/select

/* SEND FUNC. */
int mysend(unsigned char *buffer, int len) {
int sock,ret;
int status,flags;
struct sockaddr_in6 servaddr;
int opt = 1;
char *addr = "1101::1";
sock = socket(AF_INET6,SOCK_DGRAM,0);
if (sock < 0)
return -1;
if( setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0 )
return -1;
flags = fcntl(sock, F_GETFL, 0);
fcntl(sock, F_SETFL, flags|O_NONBLOCK);
servaddr.sin6_family = AF_INET6;
servaddr.sin6_port = htons(61616);
status = inet_pton(AF_INET6, addr, &servaddr.sin6_addr);
if (status <= 0) {
perror("inet_pton");
return -1;
}
/* send message to server */
status = sendto(sock, buffer, len, 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
if (status < 0) {
perror("sendto");
return -1;
}
close(sock);
printf("MESSAGE SENT SUCCESSFULLY\n");
return 0;
}
/* RECEIVE FUNC. */
int myrcv() {
int sock,ret;
int status,len,rx_bytes;
int timeout,nfds =1;
struct sockaddr_in6 servaddr;
struct timeval wait;
unsigned char rxbuff[1024];
char *rcv;
char *addr = "1101::1";
fd_set rd;
struct pollfd *fds;
sock = socket(AF_INET6,SOCK_DGRAM,0);
if (sock < 0)
return -1;
servaddr.sin6_family = AF_INET6;
servaddr.sin6_port = htons(61616);
status = inet_pton(AF_INET6, addr, &servaddr.sin6_addr);
if (status <= 0)
return -1;
bind(sock,(struct sockaddr *)&servaddr,sizeof(servaddr));
timeout = (1* 1000);
wait.tv_sec = 10;
wait.tv_usec = 0;
len = sizeof(servaddr);
fds->fd = sock;
fds->events = POLLIN;
for(;;) {
//FD_ZERO(&rd);
//FD_SET(sock,&rd);
printf("Waiting for data....\n");
ret = poll(fds,nfds,timeout);
//ret = select(1,&rd,NULL,NULL,&wait);
if(ret < 0)
break;
if(fds->revents == 0)
printf("revents 0 %d\n",ret);
if(ret == 0)
continue;
memset(rxbuff,0,1024);
//if(FD_ISSET(sock,&rd)) {
printf("receiving message\n");
rx_bytes = recvfrom(sock,rxbuff,1024,0,(struct sockaddr *)&servaddr,&len);
memcpy(rcv,rxbuff,rx_bytes);
//}
}
close(sock);
return 0;
}
int main()
{
/* call mysend() periodically using sigaction() */
/* create a thread that continuously monitors(calls myrcv()) for incoming data */
return 0;
}
I'm unable to receive the packets from the server, but I could see the packets in the tcpdump output. Above are the sample client code snippets, which tries to receive and send the data from/to the server. The scenario is: the client needs to send data periodically to server and should also be able to receive any data from the server.
I have tried using both poll and select methods but failed to receive. Please let me know if I'm missing anything. Thanks for your support.
The problem you have with receiving is that you need to bind the receiving socket to the local port.
You also have other things that can be improved, like creating a single socket for both sending and receiving and using SO_REUSEADDR on the sending socket (not needed on a write-only socket).
What you should do is:
Create socket
Set socket options
Bind to local address (Use IN6ADDR_ANY_INIT to bind to all interfaces)
Write to server
Poll for reply
Several things:
Your receive function (myrcv) isn't specifying a listen port via the bind() call. That's the most likely problem. Ditto for your send function, although a port is chosen randomly for you.
In you myrcv() function, I don't see where you have actually initialized fds or nfsd prior to calling poll().
Re-opening and closing the socket on each call to mysend() looks problematic. If you are expecting the server to send back to the same client on the same port it received the message on, chances are high you have already closed the socket. You should just open one socket for both sending and receiving. You can share the same socket between your send thread and your receive thread.

Linux server socket - Bad file descriptor

I have a problem with a server socket under Linux. For some reason unknown to me the server socket vanishes and I get a Bad file descriptor error in the select call that waits for an incomming connection. This problem always occurs when I close an unrelated socket connection in a different thread. This happens on an embedded Linux with 2.6.36 Kernel.
Does anyone know why this would happen? Is it normal that a server socket can simply vanish resulting in Bad file descriptor?
edit:
The other socket code implements a VNC Server and runs in a completely different thread. The only thing special in that other code is the use of setjmp/longjmp but that should not be a problem.
The code that create the server socket is the following:
int server_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
struct sockaddr_in saddr;
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons(1234);
const int optionval = 1;
setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &optionval, sizeof(optionval));
if (bind(server_socket, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) {
perror("bind");
return 0;
}
if (listen(server_socket, 1) < 0) {
perror("listen");
return 0;
}
I wait for an incomming connection using the code below:
static int WaitForConnection(int server_socket, struct timeval *timeout)
{
fd_set read_fds;
FD_ZERO(&read_fds);
int max_sd = server_socket;
FD_SET(server_socket, &read_fds);
// This select will result in 'EBADFD' in the error case.
// Even though the server socket was not closed with 'close'.
int res = select(max_sd + 1, &read_fds, NULL, NULL, timeout);
if (res > 0) {
struct sockaddr_in caddr;
socklen_t clen = sizeof(caddr);
return accept(server_socket, (struct sockaddr *) &caddr, &clen);
}
return -1;
}
edit:
When the problem case happens i currently simply restart the server but I don't understand why the server socket id should suddenly become an invalid file descriptor:
int error = 0;
socklen_t len = sizeof (error);
int retval = getsockopt (server_socket, SOL_SOCKET, SO_ERROR, &error, &len );
if (retval < 0) {
close(server_socket);
goto server_start;
}
Sockets (file descriptors) usually suffer from the same management issues as raw pointers in C. Whenever you close a socket, do not forget to assign -1 to the variable that keeps the descriptor value:
close(socket);
socket = -1;
As you would do to C pointer
free(buffer);
buffer = NULL;
If you forget to do this yo can later close socket twice, as you would free() memory twice if it was a pointer.
The other issue might be related to the fact that people usually forget: file descriptors in UNIX environment start from 0. If somewhere in the code you have
struct FooData {
int foo;
int socket;
...
}
// Either
FooData my_data_1 = {0};
// Or
FooData my_data_2;
memset(&my_data_2, 0, sizeof(my_data_2));
In both cases my_data_1 and my_data_2 have a valid descriptor (socket) value. And later, some piece of code, responsible for freeing FooData structure may blindly close() this descriptor, that happens to be you server's listening socket (0).
1- close your socket:
close(sockfd);
2- clear your socket file descriptor from select set:
FD_CLR(sockfd,&master); //opposite of FD_SET
You don't distinguish the two error cases in your code, both can fail select or accept. My guess is that you just have a time out and that select returns 0.
print retval and errno in an else branch
investigate the return value of accept seperately
ensure that errno is reset to 0 before each of the system calls
In Linux once you create a connection and it get closed then you have to wait for some time before making new connection.
As in Linux, socket doesn't release the port no. as soon as you close the socket.
OR
You reuse the socket, then bad file descriptor want come.

Resources