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.
Related
I have main server code, which uses select and waits for hosts activities.
I have also global structure, which stores all clients and theirs sockets.
//old code
Select in main thread unblocks intself when one of the hosts from clients array disconnects. Then I try to read data, and when it is 0, client is disconnected. Why select in Thread doesn't work in the same way? It works perfect when clients exchange data, but there is no reaction, when one of clients disconnects.
EDIT.
Sorry I have previously posted the code, which could not be compiled. The idea was, just to get the information if my idea of clients handling is correct and if this idea should work. I have created simple example and now it works. So, I have to review my oryginal code.
Working example:
#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string.h>
#include <arpa/inet.h>
#include <fcntl.h> // for open
#include <unistd.h> // for close
#include <pthread.h>
#include <limits.h>
#include <errno.h>
#include <time.h>
#include <sys/wait.h>
#define MAXCLIENTS 1000
#define FRAMESIZE 40
struct Client
{
int socket;
char state;
};
struct Client clients[2];
struct ThreadArgs
{
int srcClientIdx;
int dstClientIdx;
};
int portNumber=8090;
void * SocketThread(void *args)
{
printf("New thread created.\n");
struct ThreadArgs *threadArgs = (struct ThreadArgs*)args;
int sidx = threadArgs->srcClientIdx;
int didx = threadArgs->dstClientIdx;
int srcSocket = clients[sidx].socket;
int dstSocket = clients[didx].socket;
fd_set readfds;
struct timeval timeout;
while(1)
{
FD_ZERO(&readfds);
FD_SET(srcSocket, &readfds);
FD_SET(dstSocket, &readfds);
printf("Waiting for activities in thread.\n");
timeout.tv_sec = 60;
timeout.tv_usec = 0;
int activity = select(MAXCLIENTS+1, &readfds , NULL , NULL , &timeout);
if ((activity < 0) && (errno!=EINTR))
printf("Activity error.\n");
else if(activity == 0)
printf("Activity timeout.\n");
if (FD_ISSET(srcSocket, &readfds))
{
unsigned char buffer[FRAMESIZE];
int bytesCnt = read(srcSocket, buffer, sizeof(buffer));
if(bytesCnt == 0)
{
printf("Client%d disconnected.\n",sidx);
clients[sidx].state = 0;
clients[sidx].socket = -1;
pthread_exit(NULL);
}
else
printf("Client%d activity.\n",sidx);
}
if (FD_ISSET(dstSocket, &readfds))
{
unsigned char buffer[FRAMESIZE];
int bytesCnt = read(dstSocket, buffer, sizeof(buffer));
if(bytesCnt == 0)
{
printf("Client%d disconnected.\n",didx);
clients[didx].state = 0;
close(clients[didx].socket);
clients[didx].socket = -1;
pthread_exit(NULL);
}
else
printf("Client%d activity.\n",didx);
}
}
}
int ConfigureTCPIPconnection(int socket,struct sockaddr_in *serverAddr)
{
// Address family = Internet
serverAddr->sin_family = AF_INET;
//Set port number, using htons function to use proper byte order
serverAddr->sin_port = htons(portNumber);
//Incoming addresses
serverAddr->sin_addr.s_addr = INADDR_ANY;
//Set all bits of the padding field to 0
memset(serverAddr->sin_zero, '\0', sizeof serverAddr->sin_zero);
//Bind the address struct to the socket
if(bind(socket, (struct sockaddr *)serverAddr, sizeof(struct sockaddr)) < 0)
{
printf("Binding failed. %s\n",strerror(errno));
return 1;
}
printf("Bind sucessfull.\n");
return 0;
}
int CheckSocketActivity(fd_set *readfds)
{
int activity = select( MAXCLIENTS + 1 , readfds , NULL , NULL , NULL);
if ((activity < 0) && (errno!=EINTR))
return 1;
return 0;
}
int main(int argc, char *argv[])
{
fd_set readfds;
int serverSocket,newSocket;
pthread_t td; //thread descriptor
struct sockaddr_in serverAddr, newAddr={0};
socklen_t addr_size;
clients[0].state = 0;
clients[0].socket = -1;
clients[1].state = 0;
clients[1].socket = -1;
if((serverSocket = socket(PF_INET, SOCK_STREAM, 0)) == 0)
{
printf("Server socket creation failed.\n");
exit(1);
}
if(ConfigureTCPIPconnection(serverSocket,&serverAddr))
{
printf("Binding failed.\n");
exit(2);
}
if(listen(serverSocket,MAXCLIENTS))
{
printf("Listen error.\n");
exit(3);
}
printf("Listening...\n");
while(1)
{
FD_ZERO(&readfds); //clear fd set
FD_SET(serverSocket, &readfds); //add server socket to fd set
if(clients[0].state == 1)
FD_SET(clients[0].socket, &readfds); //add active clients to fd set
if(clients[1].state == 1)
FD_SET(clients[1].socket, &readfds);
printf("Waiting for activities.\n");
if(!CheckSocketActivity(&readfds))
{
if(FD_ISSET(serverSocket, &readfds))
{
if((newSocket = accept(serverSocket, (struct sockaddr *)&newAddr, (socklen_t*)&addr_size))<0) //create new socket
printf("New socket error. %s\n",strerror(errno));
else
{
printf("New socket connected.\n");
if(clients[0].state == 0)
{
printf("Client0 added.\n");
clients[0].state = 1;
clients[0].socket = newSocket;
}
else if(clients[1].state == 0)
{
printf("Client1 added.\n");
clients[1].state = 1;
clients[1].socket = newSocket;
}
}
}
else
{
for(int i=0;i<2;i++)
{
int srcSock = clients[i].socket;
if (FD_ISSET(srcSock, &readfds))
{
unsigned char buffer[FRAMESIZE];
int bytesCnt = read(srcSock, buffer, sizeof(buffer));
if(bytesCnt == 0)
{
printf("Client%d disconnected.\n",i);
clients[i].state = 0;
close(clients[i].socket);
clients[i].socket = -1;
}
else
{
if(clients[0].state == 1 && clients[1].state == 1)
{
int srcIndex,dstIndex;
//some other stuff
clients[0].state = 2;
clients[1].state = 2;
if(i == 0)
{
srcIndex = 0;
dstIndex = 1;
}
else
{
srcIndex = 1;
dstIndex = 0;
}
printf("Creating new thread.\n");
struct ThreadArgs threadArgs = {srcIndex,dstIndex};
if(pthread_create(&td, NULL, SocketThread, &threadArgs) != 0)
printf("Failed to create thread.\n");
if (pthread_detach(td))
printf("Thread detach error.\n");
}
else
printf("Two clients required for creating thread.\n");
}
}
}
}
}
}
}
I assume you posted the code you are indeed dealing with because the code you posted shouldn't compile.
Correction 1 (May not be the source of the problem)
From the select() man page:
void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);
All FD_*() macros take fd_set* argument. In the main() you have passed fd_set instead of fd_set* at few places.
Correction 2 (May be the source of the problem)
In SocketThread() this piece of code might be the cause of the issue:
if (FD_ISSET(dstSocket, &readfds))
{
unsigned char buffer[FRAMESIZE];
bytesCnt = read(srcSock, buffer, sizeof(buffer));
if(bytesCnt == 0)
**//this is not detected**
else
//any other activities are detected
}
You may probably want read(dstSocket, buffer, sizeof(buffer));.
You say:
Select in main thread unblocks intself when one of the hosts from clients array disconnects. Then I try to read data, and when it is 0, client is disconnected. Why select in Thread doesn't work in the same way? It works perfect when clients exchange data, but there is no reaction, when one of clients disconnects.
Please, you don't say how the client disconnects. Does it just close the socket? does it do a shutdown? I don't see any close(2) on the socket, so why do you know who is closing the connection (or calling shutdown(2) to indicate that no more data is going to appear in the connection)?
normally, a connection blocks until you receive some data... receiving 0 data means no data has arrived or, after a select(2) call has selected a socket descriptor that show no more data, then you have the socket closed. But this has to be closed somewhere, and you show neither close(2) nor shutdown(2). You also don't describe what is what is detected on the socket. If you awake from the select(2) you get to the file descriptor and the result from read is not 0 what value does the read return? have you got acquainted that -1 is one of such possible values, and that it means you have a readin error? Do you check the value returned by select? (on timeout, my memory tells me that select(0) returns 0, so are you checking for timeout?)
Please, next time, provide a complete and verifiable example. The code you post cannot be made to run without a huge amount of contribution, so the problem can only be reproduced on your side. Check this page for some reading about how to post code in StackOverflow.
I have a client which is working fine, but whenever I run a new client, sometimes I don't receive the sent message on the other client already running, while using telnet it works flawlessly, the message "broadcasts" to all connected clients, and I want whenever a message is received to one of the clients to show even if I didn't already send a message.
Should I use select on clients ? and what should be changed ?
client.c:
#include <stdio.h> //printf
#include <string.h> //strlen
#include <sys/socket.h> //socket
#include <arpa/inet.h> //inet_addr
#include <unistd.h>
int main(int argc , char *argv[]){
int sock;
struct sockaddr_in server;
char message[256] , server_reply[256];
//Create socket
sock = socket(AF_INET , SOCK_STREAM , 0);
if (sock == -1)
{
printf("Could not create socket");
}
puts("Socket created");
server.sin_addr.s_addr = inet_addr("127.0.0.1");
server.sin_family = AF_INET;
server.sin_port = htons( 9034 );
//Connect to remote server
if (connect(sock , (struct sockaddr *)&server , sizeof(server)) < 0){
perror("connect failed. Error");
return 1;
}
puts("Connected\n");
//keep communicating with server
for(;;){
printf("Enter message: ");
memset(message, 0, 256);
fgets(message, 256,stdin);
// scanf("%s" , message);
//Send some data
if( send(sock , message , strlen(message) , 0) < 0)
{
puts("Send failed");
return 1;
}
//Receive a reply from the server
if( recv(sock , server_reply , 256 , 0) < 0)
{
puts("recv failed");
break;
}
printf("Server Reply: %s\n", server_reply);
server_reply[0]='\0';
}
close(sock);
return 0;
}
server.c:
/*
** selectserver.c -- a cheezy multiperson chat server
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define PORT "9034" // port we're listening on
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(void){
fd_set master; // master file descriptor list
fd_set read_fds; // temp file descriptor list for select()
int fdmax; // maximum file descriptor number
int listener; // listening socket descriptor
int newfd; // newly accept()ed socket descriptor
struct sockaddr_storage remoteaddr; // client address
socklen_t addrlen;
char buf[256]; // buffer for client data
int nbytes;
char remoteIP[INET6_ADDRSTRLEN];
int yes=1; // for setsockopt() SO_REUSEADDR, below
int i, j, rv;
struct addrinfo hints, *ai, *p;
FD_ZERO(&master); // clear the master and temp sets
FD_ZERO(&read_fds);
// get us a socket and bind it
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
if ((rv = getaddrinfo(NULL, PORT, &hints, &ai)) != 0) {
fprintf(stderr, "selectserver: %s\n", gai_strerror(rv));
exit(1);
}
for(p = ai; p != NULL; p = p->ai_next) {
listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (listener < 0) {
continue;
}
// lose the pesky "address already in use" error message
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
if (bind(listener, p->ai_addr, p->ai_addrlen) < 0) {
close(listener);
continue;
}
break;
}
// if we got here, it means we didn't get bound
if (p == NULL) {
fprintf(stderr, "selectserver: failed to bind\n");
exit(2);
}
freeaddrinfo(ai); // all done with this
// listen
if (listen(listener, 10) == -1) {
perror("listen");
exit(3);
}
// add the listener to the master set
FD_SET(listener, &master);
// keep track of the biggest file descriptor
fdmax = listener; // so far, it's this one
// main loop
for(;;) {
read_fds = master; // copy it
if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
perror("select");
exit(4);
}
// run through the existing connections looking for data to read
for(i = 0; i <= fdmax; i++) {
if (FD_ISSET(i, &read_fds)) { // we got one!!
if (i == listener) {
// handle new connections
addrlen = sizeof remoteaddr;
newfd = accept(listener,
(struct sockaddr *)&remoteaddr,
&addrlen);
if (newfd == -1) {
perror("accept");
} else {
FD_SET(newfd, &master); // add to master set
if (newfd > fdmax) { // keep track of the max
fdmax = newfd;
}
printf("selectserver: new connection from %s on "
"socket %d\n",
inet_ntop(remoteaddr.ss_family,
get_in_addr((struct sockaddr*)&remoteaddr),
remoteIP, INET6_ADDRSTRLEN),
newfd);
}
} else {
// handle data from a client
memset(buf, 0, 256);
if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0) {
// got error or connection closed by client
if (nbytes == 0) {
// connection closed
printf("selectserver: socket %d hung up\n", i);
} else {
perror("recv");
}
close(i); // bye!
FD_CLR(i, &master); // remove from master set
} else {
// we got some data from a client
for(j = 0; j <= fdmax; j++) {
// send to everyone!
if (FD_ISSET(j, &master)) {
// except the listener and ourselves
if (j != listener && j != i) {
if (send(j, buf, nbytes, 0) == -1) {
perror("send");
}
}
}
}
}
} // END handle data from client
} // END got new incoming connection
} // END looping through file descriptors
} // END for(;;)--and you thought it would never end!
return 0;
}
The reason a client can't receive a message until they send one is because.
fgets(message, 256,stdin);
Will keep "reading" (and will therefore block) until an EOF or a newline character has been read from the input stream
Also, note that
if( recv(sock , server_reply , 256 , 0) < 0)
blocks if there is nothing to read, which will prevent that user from sending more messages to the server until there is something new to read from the server. Assuming that you've played online games before, I hope that you can see that such a setup would be rather annoying!
So, we have to find someway of checking to see if we can read from STDIN and the server socket without incurring a block. Using select() will prevent us blocking on the sever socket, but it wouldn't work for STDIN whilst using fgets() to read input from the user. This is because, as mentioned above, fgets() blocks until an EOF or newline is detected.
The main solution I have in mind is to replace fgets with a method buffer_message() that will only read from STDIN when it won't block on read (we'll use select() to implement this). We'll then place what is read into a buffer. If there is a full message, this message will then be written to the server. Otherwise, we'll let the control keep going through the program until there is something to read or write.
This is code from a recent university assignment I did and so a small portion of the code isn't mine
Declarations:
//directives are above (e.g. #include ...)
//message buffer related delcartions/macros
int buffer_message(char * message);
int find_network_newline(char * message, int inbuf);
#define COMPLETE 0
#define BUF_SIZE 256
static int inbuf; // how many bytes are currently in the buffer?
static int room; // how much room left in buffer?
static char *after; // pointer to position after the received characters
//main starts below
Main:
//insert the code below into main, after you've connected to the server
puts("Connected\n");
//set up variables for select()
fd_set all_set, r_set;
int maxfd = sock + 1;
FD_ZERO(&all_set);
FD_SET(STDIN_FILENO, &all_set); FD_SET(sock, &all_set);
r_set = all_set;
struct timeval tv; tv.tv_sec = 2; tv.tv_usec = 0;
//set the initial position of after
after = message;
puts("Enter message: ");
//keep communicating with server
for(;;){
r_set = all_set;
//check to see if we can read from STDIN or sock
select(maxfd, &r_set, NULL, NULL, &tv);
if(FD_ISSET(STDIN_FILENO, &r_set)){
if(buffer_message(message) == COMPLETE){
//Send some data
if(send(sock, message, strlen(message) + 1, 0) < 0)//NOTE: we have to do strlen(message) + 1 because we MUST include '\0'
{
puts("Send failed");
return 1;
}
puts("Enter message:");
}
}
if(FD_ISSET(sock, &r_set)){
//Receive a reply from the server
if( recv(sock , server_reply , 256 , 0) < 0)
{
puts("recv failed");
break;
}
printf("\nServer Reply: %s\n", server_reply);
server_reply[0]='\0';
}
}
close(sock);
return 0;
//end of main
Buffer functions:
int buffer_message(char * message){
int bytes_read = read(STDIN_FILENO, after, 256 - inbuf);
short flag = -1; // indicates if returned_data has been set
inbuf += bytes_read;
int where; // location of network newline
// Step 1: call findeol, store result in where
where = find_network_newline(message, inbuf);
if (where >= 0) { // OK. we have a full line
// Step 2: place a null terminator at the end of the string
char * null_c = {'\0'};
memcpy(message + where, &null_c, 1);
// Step 3: update inbuf and remove the full line from the clients's buffer
memmove(message, message + where + 1, inbuf - (where + 1));
inbuf -= (where+1);
flag = 0;
}
// Step 4: update room and after, in preparation for the next read
room = sizeof(message) - inbuf;
after = message + inbuf;
return flag;
}
int find_network_newline(char * message, int bytes_inbuf){
int i;
for(i = 0; i<inbuf; i++){
if( *(message + i) == '\n')
return i;
}
return -1;
}
P.S.
if( send(sock , message , strlen(message) , 0) < 0)
The above can also block if there's no space to write to the server, but there's no need to worry about that here. Also, I'd like to point out a few things you should implement for your client and your server:
Whenever you send data over a network, the standard newline is \r\n, or carriage return / newline, or simply the network newline. All messages sent between the client and the server should have this appended at the end.
You should be buffering all data sent between the server and the client. Why? Because you're not guaranteed to receive all packets in a message in a single read of a socket. I don't have time to find a source, but when using TCP/IP, packets for a message/file don't have to arrive together, meaning that if you do read, you may not be reading all of the data you intend to read. I'm not well versed in this, so please investigate this more. Open to having this edited / corrected
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.
I have a tcp echo server that creates a pthread for each client that connects to it. For each connection, I have a variable nbOfClients that increments.
When a client closes its connection, I detect it and decrease the number of clients. However the server keeps thinking that the client it alive and keeps on trying to read/write from the socket. I guessed that it was because of the thread that created the client and I tries to kill the thread with pthread_cancel all to non avail.
I want to kill the pthread associated to a certain client that closes its connection.
How can I go about it?
Here's my code :
static int nbOfClients = 0;
static pthread_t tid;
int main (int argc, char *argv[]) {
int bytes_to_read, arg, listen_sd, new_conn, sockfd, client_len, port;
struct sockaddr_in server, client_addr;
char *bp, buf[BUFLEN];
ssize_t n;
sockfd = 0;
switch(argc) {
case 1:
port = SERVER_TCP_PORT; // Use the default port
break;
case 2:
port = atoi(argv[1]); // Get user specified port
break;
default:
fprintf(stderr, "Usage: %s [port]\n", argv[0]);
exit(1);
}
// Create a stream socket
if ((listen_sd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
error("Cannot Create Socket!");
// set SO_REUSEADDR so port can be resused imemediately after exit, i.e., after CTRL-c
arg = 1;
if (setsockopt (listen_sd, SOL_SOCKET, SO_REUSEADDR, &arg, sizeof(arg)) == -1)
error("setsockopt");
// Bind an address to the socket
bzero((char *)&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = htonl(INADDR_ANY); // Accept connections from any client
if (bind(listen_sd, (struct sockaddr *)&server, sizeof(server)) == -1)
error("bind error");
listen(listen_sd, MAX_CONNECTIONS); ///put a define constant indicating the maximum number of clients #define NB_CLIENTS 3
while (TRUE) {
client_len = sizeof(client_addr);
if ((new_conn = accept(listen_sd, (struct sockaddr *) &client_addr, (socklen_t *)&client_len)) == -1)
error("accept error");
if(new_conn > 0) {
if(nbOfClients < MAX_CONNECTIONS) {
printf("just here\n");
printf(">> Initializing remote address: %s\n", inet_ntoa(client_addr.sin_addr));
nbOfClients++;
fclose(fp);
printf("Connections to date: %u \n",nbOfClients);
printf("make thread\n");
pthread_create(&tid,NULL,&echo, (void *)new_conn);
printf("had thread\n");
}
else {
printf("connection limit reached\n");
if(send(new_conn, "Server full!\n", 13, 0) == -1)
perror("send");
close(new_conn);
}
}
}
return(0);
}
void * echo(void *arg) {
char buf[BUFSIZE]; /* message buffer */
int n, i = 0;
bzero(buf, BUFSIZE);
if(send((int)arg, "Welcome!!\n", 20, 0) == -1)
perror("send");
detect_closed_connection(arg);
while(TRUE) {
n = read((int)arg, buf, BUFSIZE);
/**read: read input string from the client*/
if(n < 0) {
perror("error reading from socket");
}
printf("Server received from client, %d bytes: %s\n", n, buf);
/**write: echo the input string in UPPERCASE back to the client*/
int len = strlen(buf);
for(i = 0; buf[i]; i++)
buf[i] = toupper(buf[i]);
n = write((int)arg, buf, len);
if(n < 0) {
error("ERROR writing to socket");
}
}
}
void detect_closed_connection(void * listenSocket) {
struct pollfd pfd;
pfd.fd = (int)listenSocket;
pfd.events = POLLIN | POLLHUP | POLLRDNORM;
pfd.revents = 0;
while(pfd.revents == 0) {
if(poll(&pfd, 1, 100) > 0) {
// if result > 0, this means that there is either data available on the
// socket, or the socket has been closed
char buffer[32];
if (recv((int)listenSocket, buffer, sizeof(buffer), MSG_PEEK | MSG_DONTWAIT) == 0) {
// if recv returns zero, that means the connection has been closed:
nbOfClients--;
pthread_cancel(tid);
}
}
}
}
Thanks.
You should check read() for returning 0 in the thread servering the client, as read() returns 0 in case the peer (client here) closed the connection.
After this line
n = read((int)arg, buf, BUFSIZE);
add
if (0 == n)
{
fprintf(stderr, "The client closed the connection.\n");
break;
}
Just before the thread function leave you could add the statement to decrement the number of running threads.
Also be aware that nbOfClients is accessed concurently by all the "client"-threads as well as by the main thread, so accessing it shall be protected, for example by using a mutex.
There is another issues, as the call to strlen() on the buffer read expects the buffer to be 0-terminate, which does not necessarily needs ot be the case, even if you sent 0-terminated "strings". read() might very well return the "string" the client sent in more then one part. So loop around read() until the 0-terminator had been received.
Do not make the thread end itself by calling pthread_cancel(), use pthread_exit() instead.
I have a program that is supposed to both read a serial port and from tcp/ip socket simultaneously.
However, I only can see the serial information when I receive a message via socket. I believe that it has something to do with blocking/nonblocking of the accept() method. However, I have been unsuccessful in figuring out how to add flags to it.
Anyone have any ideas?
Do I have to rewrite the whole thing or can I just add a flag?
int main(int argc, char *argv[]) {
int list_s; /* listening socket */
int conn_s; /* connection socket */
short int port; /* port number */
struct sockaddr_in servaddr; /* socket address structure */
char buffer[MAX_LINE]; /* character buffer */
char *endptr; /* for strtol() */
/* Get port number from the command line, and
* set to default port if no arguments were supplied */
if ( argc == 2 ) {
port = strtol(argv[1], &endptr, 0);
if ( *endptr ) {
fprintf(stderr, "ECHOSERV: Invalid port number.\n");
exit(EXIT_FAILURE);
}
}
else if ( argc < 2 ) {
port = ECHO_PORT;
}
else {
fprintf(stderr, "ECHOSERV: Invalid arguments.\n");
exit(EXIT_FAILURE);
}
/* Create the listening socket */
if ( (list_s = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) {
fprintf(stderr, "ECHOSERV: Error creating listening socket.\n");
exit(EXIT_FAILURE);
}
/* Set all bytes in socket address structure to
* zero, and fill in the relevant data members */
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(port);
/* Bind our socket addresss to the
* listening socket, and call listen() */
if ( bind(list_s, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 ) {
fprintf(stderr, "ECHOSERV: Error calling bind()\n");
exit(EXIT_FAILURE);
}
if ( listen(list_s, LISTENQ) < 0 ) {
fprintf(stderr, "ECHOSERV: Error calling listen()\n");
exit(EXIT_FAILURE);
}
/* Enter an infinite loop to respond
* to client requests and echo input */
int result = 0;
int portID = -1;
char *device = "/dev/ttyUSB1";
int rate = convertRate("115200");
char parity = convertParity("N");
int databits = convertDatabits("8");
int stopbits = convertStopbits("1");
portID = posixComOpen(device,rate,parity,databits,stopbits);
while ( 1 ) {
char input = 0;
while(posixComDataReady(portID) && posixComRead(portID, &input)) {
printf("%c", input);
} //while
/* Write character to Vex */
if(posixComWrite(portID, 'x') < 0)
printf("POSIX: Error Writing char %c",'x');
/* Wait for a connection, then accept() it */
if ( (conn_s = accept(list_s, NULL, NULL) ) < 0 ) {
fprintf(stderr, "ECHOSERV: Error calling accept()\n");
exit(EXIT_FAILURE);
}
/* Retrieve an input line from the connected socket
* then simply write it back to the same socket. */
Readline(conn_s, buffer, MAX_LINE-1);
printf("Netbook got a TCP/IP message: %s\n", buffer);
char* sendMessage = "Thanks for the message!";
Writeline(conn_s,sendMessage , strlen(sendMessage));
/* Close the connected socket */
if ( close(conn_s) < 0 ) {
fprintf(stderr, "ECHOSERV: Error calling close()\n");
exit(EXIT_FAILURE);
}
}//while
return 0;
}
You're right, the problem is that accept blocks until there is a new connection.
I don't know how serial connections work, but you might want to look at the select function, which allows you to wait on several different file descriptors, and respond to them when there is data on the other end.
Here's an example of how to use select for sockets: Server Example