Solved

problem with socket in server

Posted on 2002-04-30
16
333 Views
Last Modified: 2008-02-01
I have a server which allows a dedicated client to connect
and pass data back and forth. The server which I coded runs on Linux and the client an executable I didn't code, runs on windows. My problem is that when the server is stopped and restarted while the client is connected I sometimes have to wait about a minute before I can get them talking again by stopping both of them. I suspect that the socket is still alive and thinks a connection is still there until it times out or something. I close the server which is running as a thread by exiting a while loop when a ctrl-c key combination is pressed.
while(!quit), where quit is set to true when a ctrl-c
is pressed.
After I break the while loop condition, I've tried the following with no luck.

pthread_exit(0); the while no longer stops with this
or
exit(0); results in a segfault
or
close(socketFileDescriptor); has no effect

If I keep the server running and stop and start the client
it will reconnect but will no longer exchange data.

At this point I'm not sure if it's my code or the software I'm working with.

I have not set any socket options or have any code that removes sockets. I read that exit(0) should close all open file descriptors. The only thing I've used is
fcntl() to set the non_blocking option.

So I guess what my question comes down to is what is the proper way to gracefully shutdown a server running as a thread. Or have I tried the right things and the problem is not with my code?
Here is how I create the socket:

struct sockaddr_in server;
struct sockaddr_in client;
unsigned int serverDataLength, clientDataLength;
int serverSock_fd, client_fd;

serverSock_fd = socket(AF_INET, SOCK_STREAM, 0);
server.sin_family = AF_INET;
server.sin_addr.s_addr = htonl(INADDR_ANY);
server.sin_port = htons(5000);
serverDataLength = sizeof(server);
bind(serverSock_fd,(struct sockaddr * )&server
,serverDataLength);
listen(serverSock_fd,1);
clientDataLength = sizeof(client);
client_fd = accept(serverSock_fd,(struct sockaddr * )&client,&clientDataLength);
int flag = fcntl(client_fd,F_GETFL,0);
fcntl(client_fd,F_SETFL, O_NONBLOCK|flag);



0
Comment
Question by:mitchguy
  • 7
  • 6
  • 2
  • +1
16 Comments
 
LVL 32

Expert Comment

by:jhance
ID: 6982193
Can I assume that you do not have access to the client code?
0
 

Author Comment

by:mitchguy
ID: 6982222
yeah I don't have any code for that. So I'm just trying to
make sure I get everything right on my end.
0
 
LVL 1

Expert Comment

by:seva
ID: 6982697
I think all you have to do on your end to terminate
gracefully is to close the client_fd.
This will initiate normal TCP connection termination sequence.
The client should call close on its end after
its "read" call returns 0.
if the client doesn't do this, then it's not your fault.
0
Announcing the Most Valuable Experts of 2016

MVEs are more concerned with the satisfaction of those they help than with the considerable points they can earn. They are the types of people you feel privileged to call colleagues. Join us in honoring this amazing group of Experts.

 
LVL 22

Expert Comment

by:ambience
ID: 6983066
>> I suspect that the socket is still alive and thinks a connection is still there until it times out or something

This usually happens when you set the SO_LINGER option.

Can you post the code where you are termination connections
0
 

Author Comment

by:mitchguy
ID: 6983163

Here is my termination connections

//server is a class
Server *object;
object = new Server();

I run this thread:

void *thread_func(void *arg){
object->startFileTransfer();
pthread_exit(0);
}

void Server::startFileTransfer(void){
while(!quit){
//processing code
}
close(serverSock_fd);
}

}
0
 
LVL 1

Expert Comment

by:seva
ID: 6984168
Another possibility is that the server cannot bind the socket because the address/port is still in use (because the old connection)
Try insert setsockopt with SO_REUSEADDR before bind()

const int     on = 1;

setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const void*)&on, sizeof(on));
0
 
LVL 22

Expert Comment

by:ambience
ID: 6985078
closesocket(client_fd); ?? anywhere ?
0
 

Author Comment

by:mitchguy
ID: 6986418
using:
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,(const void* &on, sizeof(on));
solved half of my problem.
With the client running continuously trying to connect with the server. I can start and stop the server at will and the client connects and I don't have to wait.

My only problem now is if the Server is continuously running and after the client connects I stop the client, the server crashes with the message "broken pipe"
so I can't stop and start the client with the Server Continuosly.
I do also have
closesocket(client_fd);
 

0
 
LVL 1

Expert Comment

by:seva
ID: 6986643
Can you post your code for the server with more details,
better all the server code?
From what is already there, it seems that you
are not calling accept inside the while loop.
0
 

Author Comment

by:mitchguy
ID: 6987046
I don't have my accept call in the while loop.
That is an obvious mistake on my part. I think I originally
put it in there and it wasn't working properly so I moved it out. After putting it back in the while loop it does
connect and disconnect with out crashing the server, but It
hangs before the accept call the second time through the
loop. So The processing stops.
createSocket(){
struct sockaddr_in server;
struct sockaddr_in client;
unsigned int serverDataLength, clientDataLength;
int serverSock_fd, client_fd;

serverSock_fd = socket(AF_INET, SOCK_STREAM, 0);
server.sin_family = AF_INET;
server.sin_addr.s_addr = htonl(INADDR_ANY);
server.sin_port = htons(5000);
serverDataLength = sizeof(server);
bind(serverSock_fd,(struct sockaddr * )&server
,serverDataLength);
listen(serverSock_fd,1);
clientDataLength = sizeof(client);
}

startServer(){
 createSocket();
 
while(!quit){

client_fd = accept(serverSock_fd,(struct sockaddr * )&client,&clientDataLength);
int flag = fcntl(client_fd,F_GETFL,0);
fcntl(client_fd,F_SETFL, O_NONBLOCK|flag);
read(client_fd,&buf,sizeof(buf));
processMsg(buf);
}
}
0
 
LVL 1

Expert Comment

by:seva
ID: 6987183
Does your client follow request-response pattern,
creating a new connection for every data transfer?

Also, you set the socket to be non-blocking,
then, you need to handle all different cases for the read()
call, such as read() returns because it would block
(no data available), read() returns some data < sizeof(buf)
etc.
You need to somehow know how much data the client sends
and make another "while" loop around read() until the server reads that much data.
After you are done with the request, you need to close
client_fd at the end of the loop, before going back to
accept().

0
 

Author Comment

by:mitchguy
ID: 6987222
The connection is meant to stay active once made
until the session is over so a new connection is not
made for every data transfer.
For my program there is only one client that is
allowed to connect at any given time. Once the connection
is made the client starts sending messages of all types
and sizes each at different time intervals most at 4 per second, until the session is over.
The buffer is big enough to hold all messages that are
sent from the client for each read.

I've specified the socket to have allow one connection
at a time with listen(serverSock_fd,1); since there is only one possible client.

so once the client connects and I get my client
socket file descriptor I try to read that file
descriptor. It gets set from the return value
of accept.

what happens to the accept function call while
the client is connected the second time through the loop. Does it still get called
and reset the file Descriptor so my read call no
longer reads a valid file descriptor?
0
 
LVL 1

Expert Comment

by:seva
ID: 6987314
OK, then you need to move accept() before the "while".
accept() takes the first availble incoming TCP
connection and creates a new socket for it.

However, you still have a problem with not knowing
how much data the client sends. read() may return
-1 with errno==EWOULDBLOCK if there is no data available,
or it may return any number of bytes that is ready,
but not necessary all bytes that client sent.
0
 

Author Comment

by:mitchguy
ID: 6987348
It's true I don't know exactly how much data will
be there for any given read() and sometimes it was returning -1 , but it wasn't blocking.
I was printing out the bytes read and saw the -1
so I added this:
int bytes_read = read(...);
if (bytes_read > 0){
processMsg(buf);
}

I suppose the NON_BLOCKING flag allowed me to continue
reading.

So if I have the accept() outside of the while loop.
and the client disconnects and then trys to reconnect
,since I don't have an accept() call in the while loop
it should fail right?
If closing the file descriptor is what's necessary when the client disconnects, how can the server know when the
connection is closed.
I was thinking if I put the accept() call in the while loop
, but only called it when I know there isn't a connection
active.
something like the following.
bool connected = false;

if(!connected){
accept();
connected = true;
}

If I know when a connection is closed I could set connection = false;

Do you think this would work?
is there a way to know when the connection is closed?

 
0
 
LVL 1

Accepted Solution

by:
seva earned 150 total points
ID: 6987452
A connection is closed when read() returns 0.
Also, you may want to check the errno when read()
returns -1.
If errno==EWOULDBLOCK, then it's fine since
read() returned because no data availble.
If errno != EWOULDBLOCK, then an error occured.

It may work, though in general it is not a good design.
Your server continuosly loops around wasting CPU time.
A better solution might be to use select().
0
 

Author Comment

by:mitchguy
ID: 6987589
I think you've given me enough information to solve all of
my problems tomorrow.
Thanks
0

Featured Post

Announcing the Most Valuable Experts of 2016

MVEs are more concerned with the satisfaction of those they help than with the considerable points they can earn. They are the types of people you feel privileged to call colleagues. Join us in honoring this amazing group of Experts.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

  Included as part of the C++ Standard Template Library (STL) is a collection of generic containers. Each of these containers serves a different purpose and has different pros and cons. It is often difficult to decide which container to use and …
Many modern programming languages support the concept of a property -- a class member that combines characteristics of both a data member and a method.  These are sometimes called "smart fields" because you can add logic that is applied automaticall…
The goal of the video will be to teach the user the concept of local variables and scope. An example of a locally defined variable will be given as well as an explanation of what scope is in C++. The local variable and concept of scope will be relat…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.

829 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question