I have added Keep-alive message in server in order to know about client. In my program server is keep on running and sending data to client. My problem is if client disconnect server never come to know that client get disconnected.
Below is the code snippet where i added keepalive
if ((newConnSD = ::accept(sockDesc, NULL, 0)) < 0) {
throw SocketException("Accept failed (accept())", true);
//TIMEOUT SETTING
int buffersize = 4096;
setsockopt(sockDesc, SOL_SOCKET, SO_SNDBUF, (char *)&buffersize, 4);
// KEEP ALIVE IMPLEMENTATION
struct tcp_keepalive alive;
DWORD dwBytesRet = 0;
alive.onoff = TRUE;
alive.keepaliveinterval = 1000;
alive.keepalivetime = 60000;
if (WSAIoctl(sockDesc, SIO_KEEPALIVE_VALS, &alive, sizeof(alive), NULL, 0,
&dwBytesRet, NULL, NULL) == SOCKET_ERROR)
{
printf("WSAIotcl(SIO_KEEPALIVE_VALS) failed; ");
LOG4CXX_INFO(loggerToFile,"WSAIotcl(SIO_KEEPALIVE_VALS) failed ");
cout<<WSAGetLastError()<<endl;
}
else
{
LOG4CXX_INFO(loggerToFile,"WSAIotcl(SIO_KEEPALIVE_VALS) Success ");
cout<<"WSAIotcl(SIO_KEEPALIVE_VALS) Success " <<endl;
}
int optval;
socklen_t optlen = sizeof(optval);
/* Set the option active */
optval = 1;
optlen = sizeof(optval);
if(setsockopt(sockDesc, SOL_SOCKET, SO_KEEPALIVE,(char*)&optval, optlen) < 0) {
cout<<"Error while setting KEEP_ALIVE Message"<<endl;
LOG4CXX_INFO(loggerToFile,"Error while setting KEEP_ALIVE Message");
}
else
{
LOG4CXX_INFO(loggerToFile,"SO_KEEPALIVE set on socket");
cout<<"SO_KEEPALIVE set on socket"<<endl;
}
printf("SIO_KEEPALIVE_VALS set:\n");
Related
I am trying to have a client socket make a connection to a server with a timeout.
In order to achieve the timeout, I am using a select call with the the ts set to 30s:
int flags = 0, error = 0, ret = 0;
fd_set rset, wset;
socklen_t len = sizeof(error);
struct timeval ts;
ts.tv_sec = 0;
ts.tv_usec = mConnectTimeoutMs * 1000; // this is 30s
// clear out descriptor sets for select
// add socket to the descriptor sets
FD_ZERO(&rset);
FD_SET(sock, &rset);
wset = rset;
// set socket nonblocking flag
if ((flags = fcntl(sock, F_GETFL, 0)) < 0) {
return -1;
}
if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
return -1;
}
// initiate non-blocking connect
if ((ret = ::connect(sock, sa, size)) < 0)
if (errno != EINPROGRESS) {
return -1;
}
if (ret == 0) // then connect succeeded right away
{
// put socket back in blocking mode
if (fcntl(sock, F_SETFL, flags) < 0) {
return -1;
}
return 0;
}
// we are waiting for connect to complete now
if ((ret = select(sock + 1, &rset, &wset, NULL, &ts)) < 0) {
return -1;
}
if (ret == 0) { // we had a timeout
errno = ETIMEDOUT;
return -1;
}
// we had a positive return so a descriptor is ready
if (FD_ISSET(sock, &rset) || FD_ISSET(sock, &wset)) {
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
return -1;
}
} else {
return -1;
}
if (error) { // check if we had a socket error
errno = error; // this always returns 111
return -1;
}
The point of the timeout is to allow time for the server to spawn & the server socket to be listening/accepting.
For some reason, without the server running, the select call falls through immediatly, with the rset and wset both returning true from FD_ISSET(sock.
Calling:
getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len)
Always results in the error being populated with error code 111 (connection refused), which is expected, since the server is not running yet. What am i doing wrong here If I want the select to wait for the socket to be ready to actually connect? Or how can I correctly "wait for the server socket to exist to connect" using a timeout?
Per #Barmar's comments, the select falls through as a result of the RST when the server socket is not yet listening, and the resulting socket will have an error (ECONNREFUSED). To achieve the timeout as intended, we can wrap the existing logic in a do/while loop, and then modify the timeout value to by dynamic based on remaining time in the timeout:
#include <chrono>
#include <thread>
...
int timeoutRemaining = mConnectTimeoutMs;
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
do {
// same conn logic as before, except:
...
ts.tv_usec = timeoutRemaining * 1000;
...
if (error) { // check if we had a socket error
errno = error;
if (errno == ECONNREFUSED) {
close(sock); // can't call connect on a socket thats refused connection
sock = create_new_sock();
// artificially throttle connection requests
std::this_thread::sleep_for(std::chrono::seconds(1));
continue; // there is no server available, continue trying until we reach our connection timeout
}
return -1;
}
...
} while ((timeoutRemaining = (mConnectTimeoutMs
- (std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - start)
.count())))
> 0);
I have a tcp server and I want to make it so after it accepts the first client it doesn't wait on the accept function and just goes on if no-one else has connected
while (1)
{
SOCKET ClientSocket = accept(Socket, (sockaddr *)&Client, &ClientSize);
if(ClientSocket == INVALID_SOCKET)
{
Error("accept failed.\n");
}
ClientSockets[ClientSocketsIndex++] = ClientSocket;
Result = connect(ClientSocket, (sockaddr*)&Client, ClientSize);
sockaddr_in From;
int FromSize = sizeof(From);
Result = recv(ClientSocket, Message, sizeof(Message), 0);
if (Result == SOCKET_ERROR)
{
Error("recv failed.\n");
}
Result2 = getpeername(ClientSocket, (sockaddr*)&From, &FromSize);
if (Result2 == SOCKET_ERROR)
{
Error("getpeername failed.\n");
}
Here I take the ClientSocket with accept() and add it to an array where I store all ClientSockets and then receive a buffer. The problem is that after each loop it waits for a new ClientSocket.
The Error() function is just a one I made for printing to the console and quitting if I encounter an Error btw.
First off, calling connect() on the SOCKET returned by accept() is wrong. Get rid of that.
Second, if you want your loop to service multiple clients properly, then use select() (or equivalent). Don't call accept() until select() tells you that a new client is waiting to be accepted. Likewise, don't call recv() until select() tells you that a client in your array is waiting to be read from.
Try something more like this:
const size_t MAX_SOCKETS = ...;
SOCKET ClientSockets[MAX_SOCKETS];
size_t NumClientSockets = 0;
...
while (true)
{
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(Socket, &readfds);
for(int i = 0; i < NumClientSockets; ++i)
{
FD_SET(ClientSockets[i], &readfds);
}
if (select(0, &readfds, NULL, NULL, NULL) > 0)
{
if (FD_ISSET(Socket, &readfds))
{
ClientSize = sizeof(Client);
SOCKET ClientSocket = accept(Socket, (sockaddr *)&Client, &ClientSize);
if (ClientSocket == INVALID_SOCKET)
{
Error("accept failed.\n");
}
else if (NumClientSockets == MAX_SOCKETS)
{
closesocket(ClientSocket);
}
else
{
ClientSockets[NumClientSockets] = ClientSocket;
++NumClientSockets;
}
}
size_t i = 0;
while (i < NumClientSockets)
{
if (!FD_ISSET(ClientSockets[i], &readfds))
{
++i;
continue;
}
Result = recv(ClientSockets[i], Message, sizeof(Message), 0);
if (Result <= 0)
{
if (Result == SOCKET_ERROR)
Error("recv failed.\n");
for(size_t j = i + 1; j < NumClientSockets; ++j)
{
ClientSockets[j - 1] = ClientSockets[j];
}
--NumClientSockets;
continue;
}
// use Message up to Result bytes as needed...
++i;
}
}
}
I have a system where two machines are connected, and send/receive information to each other. One of the servers sends RTP voice packets using RTSP protocol to the other one. I should implement an edge case, and it requires the following;
When the server goes down/crashes, socket connection is lost. I should create and connect a new socket when the server is available. In this regard, I remove the existing socket, create a new one ( with a new file descriptor ). But it does not connect to the server even if it is available..
static int socket_new_try_connect( struct rtsp_session *s )
{
int flags;
int res = -1;
socklen_t slen;
struct hostent *he;
struct ast_hostent h;
while( res < 0 )
{
sleep(2);
if ((he=ast_gethostbyname("10.1.1.6", &h)) == NULL) {
log("socket_new_try_connect::ast_gethostbyname ERROR !\n");
continue;
}
memset(&s->sin, 0, sizeof(s->sin));
s->sin.sin_family = AF_INET;
s->sin.sin_port = htons(8554);
memmove(&s->sin.sin_addr, he->h_addr, sizeof(s->sin.sin_addr));
/* socket */
s->sock = socket(AF_INET, SOCK_STREAM, 0);
if (s->sock < 0) {
log("socket_new_try_connect::Unable to create RTSP session socket\n");
continue;
}
log("socket_new_try_connect::new RTSP socket is created. \n");
struct timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
/*if(setsockopt(s->sock, SOL_SOCKET, SO_REUSEADDR, (char *)&timeout, sizeof(timeout)) < 0){
perror("setsockopt() SO_REUSEADDR");
close(s->sock);
continue;
}*/
if(setsockopt(s->sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) < 0){
log(LOG_ERROR, "setsockopt, EXIT FAILURE!\n");
perror("setsockopt()");
close(s);
continue;
}
if(setsockopt(s->sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)) < 0){
perror("setsockopt()");
close(s);
continue;
}
flags = fcntl(s->sock, F_GETFL);
fcntl(s->sock, F_SETFL, flags | O_NONBLOCK);
while(1)
{
res = connect(s->sock, (struct sockaddr *)&s->sin, sizeof(s->sin));
log(LOG_NOTICE, "socket_new_try_connect::connect RESULT : %d \n",res);
if(res < 0 && errno != EINPROGRESS) {
sleep(1);
continue;
}
else if( res < 0 )
{
log("socket_new_try_connect::Cannot connect to RTSP recording server. Error :%s\n", strerror(errno));
close(s->sock);
break;
}
else
{
log("socket_new:: SOCKET CONNECT IS SUCCESSFULL ! RESULT : %d \n",res);
return 1;
}
}
}
return 1;
}
With the code above, I am getting "Operation in progress" error. I also saved the messages when the server is available again after getting crashed;
tcp messages
Could you please help me to elaborate the problem ?
I have started implementing a server-client model with UNIX domain sockets of SOCK_STREAM type.
Thanks in advance
You need to avoid connecting again if your socket is already connected, you can keep some flag in your client code that indicates your client socket is already connected or not, based on that skip connecting again.
I have edited your server code below to show what I am trying to say in comments(You need to put accepted fd in select read set too, so that you can check any of clients posted any data, also you have multiple clients connected to you so you will have to keep accepted sockets in an array):
int cltfdlen = 0;
int cltfds[FD_SETSIZE];
int maxFD = sockfd;
int sun_path_size = sizeof(client_address.sun_path);
client_address.sun_family = AF_UNIX;
strncpy(client_address.sun_path, SOCKET_NAME, sun_path_size);
client_len = sizeof(client_address);
cli_interval.tv_sec = 60;
cli_interval.tv_usec = 0;
while (1) {
FD_ZERO(&read_fds);
FD_SET(sockfd, &read_fds);
for(i=0;i<cltfdlen;cltfdlen++)
FD_SET(cltfds[i], &read_fds);
int activity = select(maxFD + 1, &read_fds, NULL, NULL, &cli_interval);
if ((activity < 0) && (errno !=EINTR)) {
printf("socket select failed errno %d\n", errno);
return 1;
}
if (FD_ISSET(sockfd, &read_fds)) {
cli_sockfd = accept(sockfd,
(struct sockaddr *)&client_address,
&client_len);
if(cli_sockfd < 0) {
printf("accept from IPC socket failed");
return 1;
}
else
{
cltfds[cltfdlen++] = cli_sockfd;
if(maxFD < cli_sockfd)
maxFD = cli_sockfd
continue;
}
}
msg = (msg_t *) malloc(sizeof(msg_t));
if (msg == NULL) {
printf("Memory allocation failed for msg");
close(ipc_sockfd);
ipc_sockfd = -1;
return 1;
}
memset(msg, 0, sizeof(msg));
for(i=0;i<cltfdlen;i++)
{
if(FD_ISSET(cltfds[i], &read_fds))
{
if (read(cltfds[i], (void *)msg, sizeof(msg_t)) == -1) {
printf("Read from IPC socket failed");
return 1;
}
}
}
close(cli_sockfd);
cli_sockfd = -1;
}
I am using RFComm socket. I have a client loop where it does read and write in a loop. When the server exits I guess , the client should also terminate. But client is not terminating. It is not printing "client loop exited". My code is as follows-
void* clientLoop(void* arg)
{
char* server_address = (char*)arg;
printf("\nserver address in clientLoop = %s\n",server_address);
struct sockaddr_rc addr = { 0 };
int s, status;
char gpsMessage[128];
int flag = true;
struct timeval tv;
// allocate a socket
s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
if(s<0) perror("socket error in client loop");
// set the connection parameters (who to connect to)
addr.rc_family = AF_BLUETOOTH;
addr.rc_channel = (uint8_t) 1;
str2ba( server_address, &addr.rc_bdaddr );
tv.tv_sec = 30; // 30 seconds
tv.tv_usec = 0; // microsecs, set to 0 (or ...)
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv,sizeof(struct timeval));
// connect to server
status = connect(s, (struct sockaddr *)&addr, sizeof(addr));
if(status<0) perror("socket status error in client loop");
// send a message
if( status == 0 ) {
while(flag)
{
sleep(10);
printf("clientLoop did not exited\n");
prepareMessageToSend(gpsMessage);
status = write(s,gpsMessage , strlen(gpsMessage));
if(status == 0) flag=false;
status = read(s,gpsMessage, 128);
if(strcmp(gpsMessage,"Ring"))
{
printf("RING\n");
system("espeak -ven+f3 -k5 -s150 \"I've just picked up a fault in the AE35 unit\"");
}
if(status == 0) flag=false;
}
}
if( status < 0 ) perror("uh oh");
printf("clientLoop exited\n");
close(s);
//return s;
}
Give the socket a timeout
struct timeval tv;
tv.tv_sec = 30; // 30 seconds
tv.tv_usec = 0; // microsecs, set to 0 (or ...)
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv,sizeof(struct timeval));
if the read cannot be performed within that time, the timeout is triggered.
Also
status = read(s,gpsMessage, 128);
// check status first, or if there is no ambiguity, check only the 4 first chars
if(strncmp(gpsMessage, "Ring", 4))
(in case the message could not be set correctly)