I have client-server application. The server is in C.
Server have this structure:
int main (... ) {
FILE * fp;
fp = fopen("serverLog.log","w");
//init variables
//bind server
//listen server on port
while(1) {
//some code
//accept
//some code
int check = pthread_create(&thread, NULL, handle_client,&ctx);
}
fclose(fp);
return EXIT_SUCCSESS;
}
I run the server, and close the server using CTRL+C. What happens with filedescriptor fd? I suppose, that it stays open. If yes, what can I do with that?
Thx
No, it will be closed by the operating system. When your process exits (whether cleanly or forcibly) the kernel will clean up all dangling handles.
Related
I'm currently trying to figure out how to correctly close a file descriptor when it points to a remote file and the connection is lost.
I have a simple example program which opens a file descriptor on a sshfs mount folder and start to write to the file.
I'm not able to find how to handle the case when the connection is lost.
void *write_thread(void* arg);
int main()
{
pthread_t thread;
int fd = -1;
if(-1 == (fd = open("/mnt/testfile.txt", O_CREAT | O_RDWR | O_NONBLOCK, S_IRWXU)))
{
fprintf(stderr, "Error oppening file : %m\n");
return EXIT_FAILURE;
}
else
{
if(0 > pthread_create(&thread, NULL, write_thread, &fd))
{
fprintf(stderr, "Error launching thread : %m\n");
return EXIT_FAILURE;
}
fprintf(stdout, "Waiting 10 seconds before closing\n");
sleep(10);
if(0 > close(fd))
{
fprintf(stderr, "Error closing file descriptor: %m\n");
}
}
}
void *write_thread(void* arg)
{
int fd = *(int*)arg;
int ret;
while(1)
{
fprintf(stdout, "Write to file\n", fd);
if(0 > ( ret = write(fd, "Test\n", 5)))
{
fprintf(stderr, "Error writing to file : %m\n");
if(errno == EBADF)
{
if(-1 == close(fd))
{
fprintf(stderr, "Close failed : %m\n");
}
return NULL;
}
}
else if(0 == ret)
{
fprintf(stderr, "Nothing happened\n");
}
else
{
fprintf(stderr, "%d bytes written\n", ret);
}
sleep(1);
}
}
When the connection is lost (i.e. I unplug the ethernet cable between my boards), The close in the main thread always blocks whether I use the flag O_NONBLOCK or not.
The write call sometimes immediately fails with EBADF error or sometimes continues for a long time before failing.
My problem is that the write call doesn't always fail when the connection is lost so I can't trigger the event into the thread and I also can't trigger it from the main thread because close blocks forever.
So my question is : How to correctly handle this case in C ?
question is: how to correctly handle this case in C?
Simply you can not. File handles are designed to be unified and simple, no matter where they point to. When a device is mounted, and the connection (physical or virtual) to it crashes down, things become tricky even at the command line level.
There is a fundamental problem with remote filesystems, where on the one hand you have to cache things in order for performance remain at a usable level, and on the other hand caching in multiple clients can lead to conflicts that are not seen by the server.
NFS, for example, chooses caching by default and if the cache is dirty, it will simply hang until the connection resumes.
Documentation for sshfs suggests similar behavior.
From grepping sshfs' source code, it seems that it doesn't support O_NONBLOCK at all.
None of that has anything to do with C.
IMO your best option is to switch to nfs, and mount with e.g. -o soft -o timeo=15 -o retrans=1.
This could cause data corruption/loss in certain situations when there is a network disconnect, mainly when there are multiple clients or when the client crashes, but it does support O_NONBLOCK and in any case will return EIO if the connection is lost while a request is in-flight.
After some diggin around I found that the SSH mount could be configured to drop the connection and disconnect from server if nothing happens.
Setting ServerAliveInterval X on client side to disconnect if the server is unresponsive after X sec.
Setting ClientAliveCountMax X on server side to disconnect if the client is unresponsive after X sec.
ServerAliveCountMax Y and ClientAliveCountMax Y can also be used in order to retry Y times before dropping the connection.
With this configuration applied, the sshfs mount is automatically removed by Linux when the connection is unresponsive.
With this configuration, the write call fails with Input/output error first and then with Transport endpoint is not connected.
This is enough to detect that the connection is lost and thus cleaning up the mess before exiting.
I'm trying to recode a FTP server in C.
I open a data socket to my client (PASV), and when it try to do RETR on a valid file, I use sendfile from the file asked to the data socket:
int fd;
struct stat s;
if (cmd->arg && (fd = open(cmd->arg, O_RDWR)) != -1)
{
fstat(fd, &s);
if ((size = sendfile(client->data_soc, fd, NULL, s.st_size))
== -1)
perror("sendfile failed:");
else
printf("datas sended\n");
close(client->data_soc);
}
Client is a structure containing the data socket client->data_soc already open, and cmd is the client's command, containing the name of the file to open cmd->arg, wich is a char *.
The problem is when I do it, the sendfile function stop with SIGPIPE.
I really don't understand why, I think I use it correctly, and I can't find any solution to this issue in particular.
Thanks for your help :)
This happens because:
1) the client closed the connection in the middle of the transfer; and
2) the system is configured to raise a signal instead of returning the EPIPE error.
So you need to fix both the client and the server: the client must not close the connection in the middle and the server must be robust against client abuse.
Use, for example, sigprocmask() to disable SIGPIPE.
I am trying to create a very basic client server communication between two processes using IPC via named pipes.
I have 2 pipes, namely fifo_client and fifo_server
I have the following two classes fifoclient.c and fifoserver.c that has the following lines of code to open the two pipes.
fifoclient.c
int client = open("fifo_client",O_WRONLY);
int server = open("fifo_server",O_RDONLY);
fifoserver.c
int client = open("fifo_client",O_RDONLY);
int server = open("fifo_server",O_WRONLY);
However, on simply changing the order of opening the client and server pipes in fifoserver.c, the program freezes.
This is how the code is written when it freezes:
fifoserver.c
int server = open("fifo_server",O_WRONLY);
int client = open("fifo_client",O_RDONLY);
Notice that the server pipe is opened before the client pipe in this case. This leads to the program not responding (possible race condition?).
Can someone explain what is happening and why?
EDIT:
Here's the entire code for both the classes:
fifoserver.c
#define BUFSIZE 20
#include<stdio.h>
#include<fcntl.h>
int main()
{
char buf[BUFSIZE];
int client = open("fifo_client",O_RDONLY);
int server = open("fifo_server",O_WRONLY);
if( server<0 || client < 0)
{
printf("Couldn't open file\n");
exit(1);
}
read(client,buf,BUFSIZE*sizeof(char));
printf("Client Says: %s\n",buf);
write(server,"Fine, Thank You!",BUFSIZE*sizeof(char));
close(server);
close(client);
return 0;
}
fifoclient.c
#define BUFSIZE 20
#include<stdio.h>
#include<fcntl.h>
int main()
{
char buf[BUFSIZE];
int client = open("fifo_client",O_WRONLY);
int server = open("fifo_server",O_RDONLY);
if(client <0 || server <0)
{
printf("ERROR! Couldn't open file!\n");
exit(1);
}
write(client,"Hello! How are you?",BUFSIZE*sizeof(char));
read(server,buf,BUFSIZE*sizeof(char));
printf("Server Says: %s\n",buf);
close(server);
close(client);
return 0;
}
From man 7 fifo:
The kernel maintains exactly one pipe object for each FIFO special file that is
opened by at least one process. The FIFO must be opened on both ends (reading and
writing) before data can be passed. Normally, opening the FIFO blocks until the
other end is opened also.
In other words, your open() call will block until there is a process on the other end of the pipe. This is not a race condition -- rather, it is a deadlock. If the processes do not open the pipes in the same order, they will wait forever on one another. So, as you noticed, the solution is that they must both open the fifos in the same order.
fifoclient.c
int client = open("fifo_client",O_WRONLY);
This open in the client will block until the FIFO is opened for reading.
fifoserver.c
int client = open("fifo_client",O_RDONLY);
This open, in the server, will unblock the previous open in the client.
Now, when you swap the lines in the server to look like
int server = open("fifo_server",O_WRONLY);
int client = open("fifo_client",O_RDONLY);
the client is blocked opening the client FIFO but the server is trying to open the server FIFO for writing (which will block until somebody opens it for reading). None of them can proceed to the line which will unblock the other.
I try to make a client/server system in C on a Linux platform. I want to listen on four different ports. Therefore I create four file descriptors, one for each port. Additionally the process binds the fd to the port and starts listen to it. This works fine.
Further I use select() to listen for connections and there I get a problem. In the first run the program waits at select until a client is connected. After I send with telnet a string to the server it continues. But in the second run of the loop the program stops again at select and waits as long as a new client connects. Even if I send a new string via telnet it waits and handles this after a new connection has been established. The example I used is similar to this link.
Therefore I do not use a timeout.
Why does it wait for a new connection at select? How can I handle this issue?
My code:
FD_ZERO(&read_sock);
FD_SET(fd[0], &read_sock);
FD_SET(fd[1], &read_sock);
FD_SET(fd[2], &read_sock);
FD_SET(fd[3], &read_sock);
while(TRUE){
fprintf(stderr,"Waiting for incoming connections...\n");
status = select(maxfd+1,&read_sock,NULL,NULL,NULL);
fprintf(stderr,"Number of fd: %d\n",status);
if(status>0){
for(int i=0; i< FD_SET_SIZE; i++){
if(FD_ISSET(fd[i], &read_sock)){
fd_accept=accept(fd[i],(struct sockaddr*)&client_address[i], &len);
if(client_sock[i] < 0) {
client_sock[i] = fd_accept;
}
int lenght = recv(client_sock[i],data,BUFFER-1,0);
if(lenght>0){
data[lenght] = '\0';
fprintf (stderr,"Received: %s\n", data);
}else if(lenght==0){
getpeername(fd[i],(struct sockaddr*)&client_address[i], &len);
close(fd[i]);
client_sock[i] = -1;
}else{
fprintf(stderr,"Error: %d\n",errno);
}
char string[] = "Test"; //sends a char Test to the client
write(client_sock[i],&string,sizeof(string));
}
}
}
}
select is a blocking operations. From man page:
select() and pselect() allow a program to monitor multiple file descriptors,
waiting until one or more of the file descriptors become "ready" for some class
of I/O operation (e.g., input possible). A file descriptor is considered ready
if it is possible to perform the corresponding I/O operation (e.g., read(2))
without blocking.
Since the descriptors you give it are listener sockets, it will block until there is a new socket on either one.
If you want to listen for data on one of the accepted sockets you need to select on the socket descriptor as well, which you store in the client_address vector.
Your code effectively listens for new connections, accepts it, reads data, writes something in return and then throws the socket away without closing it.
I am trying to design a multithreaded web server in C using Pthreads and i am having a problem in accepting more incoming connections without serving them.
I want to put the file descriptor of each recieved connection in a buffer to be pocessed by a thread, Im using default accept(2) for accepting clients connections.
should i be using select ? any suggestion ?
A common way of doing multi-threaded servers is to create a new thread right after you accept a new connection, and pass the new socket to that thread. Something like this:
int main(int argc, char *argv[])
{
/* ... */
int client_socket = accept(server_socket);
pthread_create(&thread, NULL, my_connection_handler, (void *) client_socket);
/* ... */
}
void *my_connection_handler(void *argp)
{
int socket = (int) argp;
write(socket, "Hello!\r\n", 8);
close(socket);
return NULL;
}
I read this article these days (i didn't try, but looks good):
http://www.linuxjournal.com/content/network-programming-enet
or libevent too.
If you want code by your own hands, look at (performance ideas):
http://www.kegel.com/c10k.html