Issues with select () function in Multicast communication - c

I will elaborate as much as possible without being too lengthy about the issue I would like help with, if possible:
I'm writing a program that communicates with two sockets, I listen on the multicast socket, while I delegate to the other socket "Unicast" important information stemming from the communicated data coming from the first socket "Multicast".
There are two issues that I think they are related:
1- I run the program in one comuputer "Linux" communicating to another "Linux", and the program performs as expected. But when i take it to another computer "running both my program and the other programs, all in one host" with similar Multicast configuration, I get the following error:
Select : Interrupted system call
This is a perror message, but i am not sure if it is due to error in select() or my multicast configuration.
2- As a result of the first issue, I am unable to delegate to the "Unicast" client socket, but the Unicast works because there is some periodic checking between "Unicast client" and my program running all the time.
My code is as a follow:
struct ConfigStruct
{
struct sockaddr_in Hinfo1, Hinfo2;
struct sockaddr_in Rinfo;
int sock1, sock2;
};
int main()
{
ConfigStruct StructArg;
int fd1, fd2;
int POS(1);
/****************** Network parameters declaration *************************/
// Declaration for socket addresses
struct sockaddr_in Host_info1, Host_info2;
struct sockaddr_in Remote_info;
struct in_addr localInterface;
struct ip_mreq Group;
memset((char *)&Host_info1,0,sizeof(Host_info1));
memset((char *)&Host_info2,0,sizeof(Host_info2));
memset((char *)&Remote_info,0,sizeof(Remote_info));
memset((char *)&Group,0,sizeof(Group));
//**** Reads configuration file****************
cout<<"Reading configuration file..........."<<endl;
std::string input1 ="192.***.**.**";
std::string input2 = "8888";
std::string input3 ="192.***.**.**";
std::string input4 = "8889";
const char* addr_input = input1.data();
const char* port_input = input2.data();
const char* addr_input2 = input3.data();
const char* port_input2 = input4.data();
Remote_info.sin_addr.s_addr=inet_addr(addr_input);
Remote_info.sin_port = htons((uint16_t)stoi(port_input,nullptr,0));
Remote_info.sin_family=AF_INET;
Host_info1.sin_addr.s_addr=inet_addr(addr_input2);//htonl(INADDR_ANY);
Host_info1.sin_port = htons((uint16_t)stoi(port_input2,nullptr,0));
Host_info1.sin_family=AF_INET;
//***** First socket *******
fd1= socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
if (fd1 == -1)
{
std::cout<<"A problem occured"<<endl;
cease("socket", wd) ;
}
if (setsockopt(fd1,SOL_SOCKET,SO_REUSEADDR, &POS, sizeof(POS)) == -1)
{
perror(" Error in setsockopt");
exit(1);
}
// **** I'M NOT SURE IF THIS NECESSARY **************
int opts;
opts = fcntl(fd1,F_GETFL);
if (opts < 0)
{
perror("fcntl(F_GETFL)");
exit(EXIT_FAILURE);
}
opts = (opts | O_NONBLOCK);
if (fcntl(fd1,F_SETFL,opts) < 0)
{
perror("fcntl(F_SETFL)");
exit(EXIT_FAILURE);
}
//*****************************************************
if (bind(fd1,(struct sockaddr *)&Host_info1,sizeof(Host_info1)) < 0)
{
cease("Bind",wd);
}
else
{
cout<<" Socket ID number "<<fd1<<endl;
cout<<" Bound socket..."<<endl;
}
//********** The multicast network setup ***********************
std::string input5 ="230.*.**.**";
std::string input6 = "192.***.***"; // The same host IP address as above
std::string input7 = "1500" ; // The port number to listen to for Multicast message
const char* Group_Multi_Addr = input5.data();
const char* Group_Interface_Addr = input6.data();
const char* Host_port_input = input7.data();
Group.imr_multiaddr.s_addr = inet_addr(Group_Multi_Addr);
Group.imr_interface.s_addr = inet_addr(Group_Interface_Addr);
Host_info2.sin_family = AF_INET;
Host_info2.sin_addr.s_addr = INADDR_ANY;
Host_info2.sin_port = htons((uint16_t)stoi(Host_port_input,nullptr,0));
//***** The second socket *******
fd2 = socket(AF_INET, SOCK_DGRAM, 0);
if(fd2 < 0)
{
perror("Opening datagram socket error");
exit(1);
}
else
printf("Opening the datagram socket...OK.\n");
int reuse = 1;
if(setsockopt(fd2, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0)
{
close(fd2);
cease("Setting SO_REUSEADDR error", wd);
}
else
printf("Setting SO_REUSEADDR...OK.\n");
if(bind(fd2, (struct sockaddr*)&Host_info2, sizeof(Host_info2)))
{
close(fd2);
cease("Binding datagram socket error",wd);
}
else
printf("Binding datagram socket...OK.\n");
if(setsockopt(fd2, IPPROTO_IP, IP_ADD_MEMBERSHIP,(char *)&Group,sizeof(Group)) < 0)
{
perror("Adding multicast group error");
close(fd2);
exit(1);
}
else
printf("Adding multicast group...OK.\n");
StructArg.Hinfo1= Host_info1;
StructArg.Hinfo2= Host_info2 ;
StructArg.Rinfo= Remote_info ;
StructArg.sock1=fd1;
StructArg.sock2=fd2;
fd_set readfds ,rd_fds;
struct timeval tv;
// clear the set ahead of time
FD_ZERO(&readfds);
// add our descriptors to the set
FD_SET(StructArg.sock1, &readfds);
FD_SET(StructArg.sock2, &readfds);
nls = StructArg.sock2 + 1;
tv.tv_sec = 0;
tv.tv_usec = 50;
char Recv_buffer[125];
char TX_buffer[125];
memset((char *)&Recv_buffer,'0',sizeof(Recv_buffer));
memset((char *)&TX_buffer,'0',sizeof(TX_buffer));
int lenremote(sizeof(StructArg.Rinfo));
ssize_t rs, rs2;
uint8_t MsgSize;
uint8_t MsgID;
do
{
rd_fds=readfds;
if (select(nls, &rd_fds, NULL, NULL, &tv) < 0)
{
perror("select"); // error occurred in select()
}
else
{
// one or both of the descriptors have data
if (FD_ISSET(StructArg.sock1, &rd_fds))
{
rs = recvfrom(StructArg.sock1,....,...,0,...,...) ;
if ( rs > 0 )
{
Do bunch of routines
}
}
if (FD_ISSET(StructArg.sock2, &rd_fds))
{
rs2 = recv(StructArg.sock2,&Recv_buffer,sizeof(Recv_buffer),0);
if ( rs2 > 0 )
{
send some data to StructArg.sock1
}
}
// I do some work here , i send somethings to Sock 1 (Is this appropriate ??)
}
while(1);
return 0;
}
So most importantly, why do I get Select : System interrupt call in one computer but not another?

I am not sure if it is due to error in Select() or my multicast configuration.
Neither. It is due to a signal being caught during the system call. Just loop around this condition, eg:
do
{
// ...
rc = select(nls, &rd_fds, NULL, NULL, &tv);
} while (rc == -1 && errno == EINTR);
if (rc == -1)
{
perror("select");
}
else
{
// ...
}
most importantly, why do i get , Select : System interrupt call in one computer but not another
Because you got a signal on one computer and not the other. And it isn't important.

how about the timeouts, because the man page, says that timeout
becomes undefined when an error arise, should i put the tv.sec and
tv.usec inside the loop.
man 2 select:
On Linux, select() modifies timeout to reflect the amount of time not
slept; most other implementations do not do this. (POSIX.1-2001
permits either behavior.)
On Linux thus, the code shown in the question probably doesn't act as intended, because the timeout is set before the loop only once and zeroed if it expires - from then on, it remains zero. Here, it would be appropriate to set the desired timeout inside the main loop, but outside the proposed loop … while (rc == -1 && errno == EINTR).
On systems that don't update timeout accordingly, there is in the presence of interruptions no such easy way to maintain the correct timeout.

Related

c: accept() for non-blocking server

I am writing a tcp server interface in C. In this interface, I would require to listen for command and send periodic outputs through the same sockets. I would think in this case I would need to program my sockets to be non-blocking. Here is my code
#define ERROR -1
#define MAX_CLIENTS 2
#define MAX_DATA 1024
int setNonblocking(int fd)
{
int flags;
#if defined(O_NONBLOCK)
if (-1 == (flags = fcntl(fd, F_GETFL, 0)))
flags = 0;
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
#else
flags = 1;
return ioctl(fd, FIOBIO, &flags);
#endif
}
int main()
{
int lsock;
struct sockaddr_in saddr;
short int port;
char buffer[MAX_DATA];
int socketOption=1;
int LISTENQ =1;
/// creating sockets
if ( (lsock = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
{
printf(" Error: sockets creation \n");
return -1;
}
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons(2222);
setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, &socketOption, sizeof(socketOption));
setNonblocking(lsock);
if ( bind(lsock, (struct sockaddr *) &saddr, sizeof(struct sockaddr_in)) < 0 )
{
fprintf(stderr, "ECHOSERV: Error calling bind()\n");
return -1;
}
if ( listen(lsock, LISTENQ) < 0 )
{
fprintf(stderr, "ECHOSERV: Error calling listen()\n");
return -1;
}
bool running = true;
bool bConnected = true;
while ( running )
{
int csock;
time_t start = time(NULL);
time_t now;
if ( (csock = accept(lsock, NULL, NULL) ) < 0 )
{
int errno_s = errno;
printf("The error is %d \n", errno_s);
fprintf(stderr, "ECHOSERV: Error calling accept()\n");
return -1;
}
else
{
bConnected =true;
}
while(bConnected)
{
int n;
if((n=recv(lsock, buffer, MAX_DATA, 0)) > 0)
{
printf("Command Handling \n");
}
if(now - start == 100)
{
printf("Data Sending \n");
}
}
return 0;
}
The program always run into error whenever accept() is being called, I wonder why csock always return -1? I then tried to print out the errno. The errno read 11, which seems to be EAGAIN ( telling to try again ). So should I ignore this error?
I did think about other method to achieve what I want eg using select() and a set of file sockets... but somehow i think it might run into problem
Need your opinion and help on this
REgards
See the accept man page:
If no pending connections are present on the queue, and the socket is not marked as nonblocking, accept() blocks the caller until a connection is present. If the socket is marked nonblocking and no pending connections are present on the queue,
accept() fails with the error EAGAIN or EWOULDBLOCK.
You can use select() to test for data on a listening socket, and then call accept() only when it actually has data, ie a pending connection.

Socket UDP client memory leak

I coded a UDP socket client-server in C. The client sent a query to the server each second for a long time (e.g.: 1 week).
My code ran fine, but I can see in the timeline that the ram increased considerably, at around 14 hours the memory increased to 150M approximately.
The increment is in the client side, the server is working fine.
I need to detect what causes this problem because the program will be running for long time.
What's wrong in my code?
This is my code in the client side:
int consultar_servidor(char *t1_str_)
{
struct timeval t_ini, t_fin, tv;
double secs;
char cadena_enviada[67];
char cadena_recibida[67];
char tx_str[51]= "|0000000000000000|0000000000000000|0000000000000000";
int validacion, i;
long long int t4;
char t4_str[20];
char t2_str_rec[20];
char t2_pps_str_rec[20];
char t3_str_rec[20];
int nBytes, numfd;
if (t1_str_ != 0)
{
strcpy(cadena_enviada,t1_str_);
strcat(cadena_enviada,tx_str);
}
else
{
error("Error recepcion t1");
return 1;
}
if (cont_parametros == 0)
{
set_param();
}
if ( connect( clientSocket, ( struct sockaddr * ) &serverAddr, sizeof( serverAddr) ) < 0 )
error( "Error connecting socket" );
if ( sendto(clientSocket,cadena_enviada,sizeof(cadena_enviada),0,(struct sockaddr *)&serverAddr,addr_size) < 0)
{
close(clientSocket);
error( "Error sentto function");
cont_parametros = 0;
return 1;
}
/** Socket nonblock **/
int flags = fcntl(clientSocket, F_GETFL, 0);
fcntl(clientSocket, F_SETFL, flags | O_NONBLOCK);
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(clientSocket, &readfds);
numfd = clientSocket + 1;
/** Set 700us to receive **/
tv.tv_sec=0;
tv.tv_usec=700000;
/** Server send me **/
int recibo = select(numfd, &readfds,NULL,NULL,&tv);
switch (recibo)
{
case -1:
/** Error reception **/
error("Error reception");
FD_CLR(clientSocket, &readfds);
close(clientSocket);
cont_parametros=0;
return 1;
case 0:
/** Timeout and close socket **/
error( "Error timeout" );
FD_CLR(clientSocket, &readfds);
close(clientSocket);
cont_parametros = 0;
return 1;
default:
/** If socket contain data **/
if (FD_ISSET(clientSocket, &readfds))
{
/** catch t4 **/
t4=ts();
sprintf(t4_str, "%lld", t4);
/** Receive server message**/
nBytes = recvfrom(clientSocket,cadena_recibida,sizeof(cadena_recibida),0,NULL, NULL);
/** If si a bad data **/
if (nBytes < 0)
{
error( "Error recept data" );
FD_CLR(clientSocket, &readfds);
close(clientSocket);
cont_parametros = 0;
return 1;
}
/** Clean set **/
FD_CLR(clientSocket, &readfds);
int i;
/** trim t2**/
for(i=17;i<33;i++) t2_str_rec[i-17]=cadena_recibida[i];
t2_str_rec[16]= '\0';
/** trim t3**/
for(i=34;i<51;i++) t3_str_rec[i-34]=cadena_recibida[i];
t3_str_rec[16]= '\0';
printf("%s|%s|%s|%s\n",t1_str_, t2_str_rec, t3_str_rec, t4_str);
return 0;
}
}
}
And the function to set the params socket:
void set_param()
{
/** Set client params **/
memset(&local_addr, 0, sizeof(struct sockaddr_in));
local_addr.sin_family = AF_INET;
local_addr.sin_port = htons(SRC_PORT);
local_addr.sin_addr.s_addr = inet_addr(SRC_IP);
/** Configure settings in address struct **/
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(DST_PORT);
serverAddr.sin_addr.s_addr = inet_addr(DST_IP);
memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);
addr_size = sizeof serverAddr;
clientSocket = socket(AF_INET, SOCK_DGRAM, 0);
if ( clientSocket < 0 )
{
error( "Error socket no create" );
exit(1);
}
if (bind(clientSocket, (struct sockaddr *)&local_addr, sizeof(local_addr))< 0)
{
close(clientSocket);
error( "Error bind in socket" );
exit(1);
}
/** Socket create OK**/
cont_parametros = 1;
}
The main part
int main( int argc, char* argv[] )
{
long long int t1;
char t1_str[20];
while(1)
{
t1=ts();
sprintf(t1_str, "%lld", t1);
consultar_servidor(t1_str);
sleep(1);
}
}
The main problem is that you call
close(clientSocket);
for all branches of the code except when you successfully read the data with recvfrom and return with code 0 from consultar_servidor(). As a result, the socket is never closed and there is a socket descriptor leak.
There may be other bugs in the code, make sure to test it under valgrind.
I suggest to restructure the code to avoid duplication and help to catch bugs such as these. For example, one option is to move the cleanup code to a separate function. Another option is to use the goto cleanup pattern, unless you're paranoid about not having goto in your code.
I don't see any actual memory allocations in the posted code, so if there is a direct memory leak, it must be caused by a problem somewhere else in the program.
As #kfx mentioned, another possibility is a socket leak; since each socket comes with buffers that use up a certain amount of RAM, that could show up as increased memory usage as well.
An easy way to test to see if your program is leaking sockets would be to add something like this to your program:
static int socketCount = 0;
int debug_socket(int domain, int type, int protocol)
{
int ret = socket(domain, type, protocol);
if (ret >= 0)
{
++socketCount;
printf("After socket() call succeeded, there are now %i sockets in use by this program\n", socketCount);
}
else perror("socket() failed!");
return ret;
}
int debug_close(int sock)
{
int ret = close(sock);
if (ret == 0)
{
--socketCount;
printf("After close() call succeeded, there now %i sockets in use by this program\n", socketCount);
}
else perror("close() failed!");
return ret;
}
... then temporarily replace all the calls to socket() in your program with debug_socket(), and all the calls to close() in your program with debug_close().
Then run your program, and watch its stdout output. If the numbers printed in the debug output are constantly increasing, your program is leaking sockets and you'll need to figure out why/how and fix it. If not, then you have some other problem elsewhere.

Multithreaded TCP server with C sockets and pthreads - Why does accept() block on the second request?

I've been writing a small multithreaded TCP server in C using unix sockets and pthreads, but I'm having trouble with accept(). It hangs on the second request that comes through, and only unblocks when the previously thread exits.
Here's how I set up the server socket.
int server_start(server_t *server, int port) {
int fd;
struct sockaddr_in server_addr;
// Socket file descriptor.
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1) {
perror("socket failed");
return 1;
}
// Socket address.
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(port);
// Bind.
if (bind(fd, (struct sockaddr *) &server_addr, sizeof(server_addr)) == -1) {
perror("bind failed");
return 1;
}
server->fd = fd;
listen(server->fd, server->clients_len);
pthread_create(&(server->thread), NULL, thread_entry_server, server);
return 0;
}
Here's my add_client code. It spawns a separate thread for the client.
client_t *server_add_client(server_t *server) {
int iter,
fd,
status;
client_t *client;
printf("before\n");
fd = accept(server->fd, NULL, 0);
printf("after\n");
if (fd == -1) {
perror("accept");
return NULL; // Connection failed.
}
// Find an empty spot.
client = server->get_empty_spot();
client->fd = fd;
// Start the new thread.
status = pthread_create(
&(client->thread),
NULL,
thread_entry_client,
client
);
if (status != 0) {
perror("pthread_create");
close(client->fd);
return NULL;
}
client->active = 1;
return client;
}
And here's my entry function for the client thread:
void *thread_entry_client(void *void_client) {
client_t *client = void_client;
int len;
while (1) {
len = recv(client->fd, client->recv_buffer, RECV_BUFFER_LEN, 0);
if (len < 0) {
perror("recv");
client->active = 0;
close(client->fd);
return NULL;
}
if (len == 0) { // Client disconnected.
client->active = 0;
close(client->fd);
printf("disconnect\n");
return NULL;
}
if (len > 0) {
//printf("%s\n", client->recv_buffer);
printf("msg\n");
}
}
return NULL;
}
So what I'm doing to test this is establishing two connections. The first connection goes through and works fine, but the second connection does not - instead the thread hangs on accept(). I know this from my printfs (that I've left in there), and I know that accept() unblocks AFTER the first client disconnects. I also know that my code is not closing the server socket file descriptor or changing it.
Any advice on debugging this? I can't figure it out.
EDIT: Here is thread_entry_server.
void *thread_entry_server(void *void_server) {
server_t *server = void_server;
client_t *client;
while (1) {
client = server_add_client(server);
if (client == NULL) // Server is full or connection failed.
continue;
}
return NULL;
}
I was testing with a javascript websocket, but I wasn't doing any handshaking so it wasn't completing the connection. Testing with telnet works.
This is because accept() will block (unless configured otherwise) till a client connection is available.
See Documentation which mentions -
If the listen queue is empty of connection requests and O_NONBLOCK is
not set on the file descriptor for the socket, accept() will block
until a connection is present. If the listen() queue is empty of
connection requests and O_NONBLOCK is set on the file descriptor for
the socket, accept() will fail and set errno to [EAGAIN] or
[EWOULDBLOCK].
Also, server->get_empty_spot(); should always return new client instance otherwise it would mean you are passing same client->thread to pthread_create.
I usually prefer to create new thread myself, something like -
**pthread_t newListner;**
ThreadArgs thread_args;
thread_args.client_socket = client_socket;
int rc;
if ((rc = pthread_create(&newListner, NULL, startListener, &thread_args)))
{ .. }
Give it a try.

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.

Block Socket with Unix and C/C++ Help

I'm trying to figure out what is blocking my program. I'm running a server that uses POSIX threads. I have to for my computer programming lab. The main function listens for new connections. Once it accepts a connection, it creates a new thread by passing the FD to the thread. I'm able to successfully connect to the server using multiple telnet/client connections. I can send data to the server successfully once, but if I try sending again the server won't do anything.
Part of the main function
int active_thread = 0;
//The Running loop
while(running)
{
if(active_thread > NUMBTHREADS)
{
printf("Unable to accept client connection! Threads are all used up");
running = false;
}
else
{
if(FD_ISSET(sockfd, &readfds))
{
if((bindfd[active_thread] = accept(sockfd, (struct sockaddr *) &client_addr, &client_sock_size)) == -1)
{
fprintf(stderr, "Unable to accept client \n");
perror("What");
break;
}
activethreads[active_thread] = pthread_create( &threads[active_thread], NULL, server_handler, (void*) &bindfd[active_thread]);
//close(bindfd[active_thread]);
//pthread_join( threads[active_thread], NULL);
active_thread++;
//running = false;
}
}
}
close(sockfd);
return 0;
}
Part of the POSIX THREAD CODE
void *server_handler( void *sockfd)
{
int bindfd = *( (int *) sockfd);
char buffer[MESSAGELENGTH];
bool running = true;
printf("Thread was created successfully\n");
char intro[] = "Successfully Connected to server!\n";
struct pollfd pfd;
pfd.fd = bindfd;
pfd.events = POLLIN;
if ( (send(bindfd, intro, strlen(intro), 0)) < 0)
{
perror("Unable to send");
}
while(running){
char msg[] = "\nYou have the following options!\n1) Insert an integer: insert <integer>\n2) Remove An Integer: remove <integer>\n3) Get number of integers in list: get_count\n4) Get first integer: get_first\n5) Get last integer: get_last\n6) Quit program: quit\n ";
if ( (send(bindfd, msg, strlen(msg), 0)) < 0)
{
perror("Unable to send");
}
memset(&buffer, 0, MESSAGELENGTH);
if (recv(bindfd, buffer, MESSAGELENGTH, 0) > 0)
{
//SOme other code
}
}
I think its blocking at either the accept or recv. I've heard of select() and various other methods, but I'm having difficulty trying to implement them. Thanks!
The root cause of your issue appears to be that you are unconditionally executing close(sockfd); return 0; at the bottom of your while (running) loop, which means that the loop only ever executes once.
Additionally, you should not be using FD_ISSET() unless you are also using select(). Your main loop should look something more like:
int active_thread = 0;
while (active_thread < NUMBTHREADS)
{
if((bindfd[active_thread] = accept(sockfd, (struct sockaddr *) &client_addr, &client_sock_size)) == -1)
{
fprintf(stderr, "Unable to accept client \n");
perror("What");
break;
}
activethreads[active_thread] = pthread_create( &threads[active_thread], NULL, server_handler, (void*) &bindfd[active_thread]);
active_thread++;
}
if (active_thread >= NUMBTHREADS)
{
printf("Unable to accept client connection! Threads are all used up.\n");
}
running = false;
close(sockfd);
return 0;
By default network sockets are blocking. You need to set the O_NONBLOCK flag on the socket.
if(fcntl(fd, F_GETFL, &flags) < 0 ||
fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
perror("Failed to set socket as non-blocking");
Now, instead of blocking when there is no input (or buffer space to store output), the error EAGAIN (or EWOUDLBLOCK) is returned. Lastly, you will need to use select() or poll() when you have nothing else to do but wait on I/O. These calls will only wake the process when either there is input, room for output, or possibly a time-out period passes.
int maxFd;
fdset fds;
FD_ZERO(&fds);
FD_SET(listenFd, &fds);
FD_SET(sockFd1, &fds);
FD_SET(sockFd2, &fds);
maxFd = listenFd+1;
maxFd = sockFd1 > maxFd ? sockFd1+1 : maxFd;
maxFd = sockFd2 > maxFd ? sockFd2+1 : maxFd;
if(select(maxFd, &fds, &fds, &fds, NULL) < 0) {
perror("Failed on select()");
exit(1);
}
if(FD_ISSET(listenFd, &fds))
...
This example is not complete or neccessarily 100% correct, but should be a good start. Also, I tend to reserve using send*() and recv*() when dealing with SOCK_DGRAM sockets and just use read(), write() on SOCK_STREAM sockets.

Resources