I'm writing a client/server application where the client and server should send data to each other via a TCP socket. The client should connect to the server and if the connection fails, it should wait a few seconds and then try again to connect to it (up to a certain number of tries).
This is the code I currently have:
const int i_TRIES = 5;
time_t t_timeout = 3000;
int i_port = 5678;
int i_socket;
string s_IP = "127.0.0.1";
for(int i = 0; i < i_TRIES; i++)
{
if((i_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
cout << "[Client]: Socket creation failed." << endl;
exit(EXIT_FAILURE);
}
memset(&server_address, '0', sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_port = htons(i_port);
if(inet_pton(AF_INET, s_IP.c_str(), &server_address.sin_addr) <= 0)
{
cout << "[Client]: Invalid IP address." << endl;
exit(EXIT_FAILURE);
}
if(connect(i_socket, (struct sockaddr *)&server_address, sizeof(server_address)) < 0)
{
if(i < i_TRIES - 2)
{
cout << "[Client]: Connection to server failed. Trying again in " << t_timeout << " ms." << endl;
close(i_socket);
sleep(t_timeout);
}
else
{
cout << "[Client]: Could not connect to server, exiting." << endl;
exit(EXIT_FAILURE);
}
}
else
{
cout << "[Client]: Successfully connected to server." << endl;
break;
}
}
// do stuff with socket
The issue I'm having is that the first call to connect() works as expected, it fails if there's no server and then the loop repeats, however, the second time connect() blocks forever (or at least for much longer than I want it to). Initially, my loop was just around the connect() if block (code below), and this also caused the same problem. After that I included the whole socket setup (the code above) in the loop, but that also didn't help. I also tried closing the socket after a failed connection, but this didn't help either.
Initial for loop:
// other stuff from above here
for(int i = 0; i < i_TRIES; i++)
{
if(connect(i_socket, (struct sockaddr *)&server_address, sizeof(server_address)) < 0)
{
if(i < i_TRIES - 2)
{
cout << "[Client]: Connection to server failed. Trying again in " << t_timeout << " ms." << endl;
sleep(t_timeout);
}
else
{
cout << "[Client]: Could not connect to server, exiting." << endl;
exit(EXIT_FAILURE);
}
}
else
{
cout << "[Client]: Successfully connected to server." << endl;
break;
}
}
// do stuff with socket
Can I force connect() to return after a certain amount of time has passed? Or is there a way to get the connect() function to try multiple times on it's own? Or is there something I need to do to the socket to reset everything before I can try again? I hope this isn't a dumb question, I couldn't find any information about how to connect multiple times.
Thanks in advance!
Can I force connect() to return after a certain amount of time has passed?
No. You must put the socket into non-blocking mode and then use select() or (e)poll() to provide timeout logic while you wait for the socket to connect. If the connection fails, or takes too long to connect, close the socket, create a new one, and try again.
Or is there a way to get the connect() function to try multiple times on it's own?
No. It can perform only 1 connection attempt per call.
Or is there something I need to do to the socket to reset everything before I can try again?
There is no guarantee that you can even call connect() multiple times on the same socket. On some platforms, you must destroy the socket and create a new socket before you call connect() again. You should get in the habit of doing that for all platforms.
Put the socket into non-blocking mode and use select() to implement the timeout. Select for writeability on the socket. Note that you can decrease the platform connect timeout by this means, but not increase it.
The sleep() is pointless, just literally a waste of time.
Related
I am writing a C++ server program and a Qt client program. The problem is, recv() keeps waiting forever when tcpdump clearly shows a tcp packet from client is delivered to my network interface.
Server program is ran on Centos 7.7 and compiled with g++ 4.8.5
while (true) {
sockaddr_in client_addr{0};
socklen_t client_len = sizeof(sockaddr_in);
newsockfd = accept(sockfd, (sockaddr*)&client_addr, &client_len);
cout << "New connection from " << inet_ntoa(client_addr.sin_addr) << endl;
long bytes;
char buf[1000];
while (true) {
bytes = recv(newsockfd, buf, 1000, 0);
if (bytes < 1) {
if (bytes < 0) { cout << "Error: " << errno << ", " << strerror(errno) << endl; }
break;
}
ProcessRequest(buf, bytes);
}
close(newsockfd);
cout << "Host " << inet_ntoa(client_addr.sin_addr) << " disconnected..." << endl;
}
Client program is ran on Windows and compiled with msvc 2017 and Qt:
QTcpSocket* loginClient = new QTcpSocket(this);
loginClient->connectToHost(ipAddr, Port);
if (!loginClient->waitForConnected(30000)) { return; }
char data[100] = "12345678";
int sendLen = loginClient->write(data, 9);
if (sendLen == -1) { return; }
The server is able to detect connection from client, so ip and port issues are out of the way. Furthermore, the server is not able to detect disconnection from this Qt client properly. Disconnection detection is usually postponed by numerous minutes or indefinitely.
An interesting thing is, I wrote another client program ran on another Centos, with which the server worked Perfectly. So I assume the server program is fine?
Thanks for any help.
Found the error. Turned out that Client side had made three connections to the server among which only one was connected and two in queue. The packet was coming into the server not from the connected socket. That is why the opened file descriptor doesn't read anything from NIC.
I'm working on a client application that sends sensor data one way to a remote server. After the initial login there is no return data from the server. My problem is when the ethernet is disconnected such as a hard disconnect i.e. wireless link goes down, my application does not get a error return value after attempting a 'send' call. I am using a single non-blocking socket instance. The thread checks for a 'recv' each loop using 'select'. It does eventually get an error on 'recv' but never on 'send'.
When the remote PC has a internet connectivity loss it will cause the program to be disconnected from the server for minutes to hours before it recognises the connection loss happened and switches to re-login the server. What can be done to help detect the hard disconnect?
void checkConnect(NTRIP& server)
{
//1st check for recv or gracefully closed socket
char databuf[SERIAL_BUFFERSIZE];
fd_set Reader, Writer, Err;
TIMEVAL Timeout;
Timeout.tv_sec = 1; // timeout after 1 seconds
Timeout.tv_usec = 0;
FD_ZERO(&Reader);
FD_ZERO(&Err);
FD_SET(server.socket, &Reader);
FD_SET(server.socket, &Err);
int iResult = select(0, &Reader, NULL, &Err, &Timeout);
if(iResult > 0)
{
if(FD_ISSET(server.socket, &Reader) )
{
int recvBytes = recv(server.socket, databuf, sizeof(databuf), 0);
if(recvBytes == SOCKET_ERROR)
{
cout << "socket error on receive call from server " << WSAGetLastError() << endl;
closesocket(server.socket);
server.connected_IP = false;
}
else if(recvBytes == 0)
{
cout << "server closed the connection gracefully" << endl;
closesocket(server.socket);
server.connected_IP = false;
}
else //>0 bytes were received so read data if needed
{
}
}
if(FD_ISSET(server.socket, &Err))
{
cout << "select returned socket in error state" << endl;
closesocket(server.socket);
server.connected_IP = false;
}
}
else if(iResult == SOCKET_ERROR)
{
cout << "ip thread select socket error " << WSAGetLastError() << endl;
closesocket(server.socket);
server.connected_IP = false;
}
//2nd check hard disconnect if no other data has been sent recently
if(server.connected_IP == true && getTimePrecise() - server.lastDataSendTime > 5.0)
{
char buf1[] = "hello";
cout << "checking send for error" << endl;
iResult = send(server_main.socket, buf1, sizeof(buf1), 0);
if(iResult == SOCKET_ERROR)
{
int lasterror = WSAGetLastError();
if(lasterror == WSAEWOULDBLOCK)
{
cout << "server send WSAEWOULDBLOCK" << endl;
}
if(lasterror != WSAEWOULDBLOCK)
{
cout << "server testing connection send function error " << lasterror << endl;
closesocket(server.socket);
server.connected_IP = false;
}
}
else
{
cout << "sent out " << iResult << " bytes" << endl;
}
server.lastDataSendTime = getTimePrecise();
}
}
It is not possible to detect disconnect until you try to send something.
The solution for you is the following:
You detect that you have received no data for a certain period of time and you want to check is the connection is alive.
You send some data to the server using send function. It could be protocol-specific ping packet or either garbage. The send function returns immediately, because it does not wait for actual data send. It only fills internal send buffer.
You begin waiting for socket read.
While you are waiting, OS tries to send the data in the send buffer to the server.
When OS detects that it cannot deliver data to the server, then the connection is marked as erroneous.
Now you will get an error when calling recv and send functions.
The send timeout is system specific and can be configured. Usually, it is about 20 seconds (Linux) - 2 minutes (Windows). It means that you need to wait a lot before you receive an error.
Notes:
You can also turn on TCP keep alive mechanism, but I don't recommend you to do this.
You can also modify TCP timeout intervals. It can be helpful when you want the connection to survive the temporary network disconnect.
That's how TCP works and is intended to work. You will get an error from a subsequent send, but never from the first send after the disconnect. There is buffering, and retry, and retry timeout to overcome before an error is signalled.
I'm kinda new to network programming and I'm trying to make a socket server that could handle multiple clients. The server will be a connection between players and a game engine for a text-based adventure, written in c++.
I got the code working for single clients, and for sending data between client and server. The next step in the implementation is to make it able to handle multiple clients. For what I understand fork is way to do this. I've got this code this far, but I can't for my life get it to work.
while (1) {
cout << "Server waiting." << endl;
n = sizeof(struct sockaddr_in);
if ((clientSocket = accept(servSocket, (struct sockaddr*) (&client), (socklen_t*) (&n))) < 0) {
cerr << "Error: " << errno << ": " << strerror(errno) << endl;
}
if(fork() == 0){
cout << "Child process created. Handling connection with << " << inet_ntop(AF_INET, &client.sin_addr, buff, sizeof(buff)) << endl;
close(servSocket);
}
string sendmsg;
string recvmsg;
int bytesRecieved = 0;
char package[1024];
string playerMessage;
while(1){
bytesRecieved = recv(clientSocket, package, 1024, 0);
for (int offset = 0; offset < bytesRecieved/sizeof(char); ++offset) {
playerMessage += package[offset];
}
cout << playerMessage;
cin >> sendmsg;
sendmsg += "\n";
send(clientSocket, sendmsg.c_str(), sendmsg.size(), 0);
}
}
close(clientSocket);
close(servSocket);
return 0;
I understand that the bind() and everything before that should happend before the main-loop with fork() in it, so I didn't bother to include that.
Thanks on beforehand!
Creating process per connection is a wrong way in most cases. What if you have 20'000 players? Context switching for 20'000 processes makes too much overhead slowing down the server.
Consider using async programming. boost::asio is one of the best choices then.
I am trying to write a threaded function that sends system information via Tcp/ip over the local network to another computer. I have been using sockets to achieve this and this has worked out quite allright thus far. But I am now at a point where this usually works but around 30% of the time I get error messages telling me that the socket can not be opened. I use the activeSocket library for the sockets.
#include "tbb/tick_count.h"
#include "ActiveSocket.h"
using namespace std;
CActiveSocket socket;
extern int hardwareStatus;
int establishTCP() {
char time[11];
int communicationFailed = 0;
memset(&time, 0, 11);
socket.Initialize();
socket.SetConnectTimeout(0, 20);
socket.SetSendTimeout(0, 20);
return communicationFailed;
}
int monitor() {
cout << "Monitor: init continious monitoring" << endl;
int communicationFailed;
tbb::tick_count monitorCounter = tbb::tick_count::now();
while (!closeProgram) {
tbb::tick_count currentTick = tbb::tick_count::now();
tbb::tick_count::interval_t interval;
interval = currentTick - monitorCounter;
if (interval.seconds() > 2) {
monitorCounter = tbb::tick_count::now();
communicationFailed = 1;
char buffer[256];
sprintf(buffer, "%d;", hardwareStatus);
establishTCP();
char *charip = new char[monitoringIP.size() + 1];
charip[monitoringIP.size()] = 0;
memcpy(charip, monitoringIP.c_str(), monitoringIP.size());
const uint8* realip = (const uint8 *) charip;
int monitorCount = 0;
cout << "Monitor: " << buffer << endl;
while (communicationFailed == 1 && monitorCount < 2) {
monitorCount++;
if (socket.Open(realip, 2417)) {
if (socket.Send((const uint8 *) buffer, strlen(buffer))) {
cout << "Monitor: Succeeded sending data" << endl;
communicationFailed = 0;
socket.Close();
} else {
socket.Close();
communicationFailed = 1;
cout << "Monitor: FAILED TO SEND DATA" << endl;
}
} else {
socket.Close();
communicationFailed = 1;
cout << "Monitor: FAILED TO OPEN SOCKET FOR DATA" << endl;
}
}
if (monitorCount == 2) cout << "Monitor: UNABLE TO SEND DATA" << endl;
}
}
return communicationFailed;
}
I think I am doing something wrong with these functions and that the problem is not on the other side of the line where this data is received. Can anyone see any obvious mistakes in this code that could cause the failure? I keep getting my own cout message "Monitor: FAILED TO OPEN SOCKET FOR DATA"
EDIT: With telnet everything works fine, 100% of the time
You can use netstat to check that the server is listening on the port and connections are being established. Snoop is another good application in your Armour for finding out what is going wrong. Another possibility is to use telnet to see if the client can connect to that IP address and port. As to the code I will take a look at it later to see if something has gone awry.
socket is a global variable. It might be re-used concurrently between two threads or sequentially inside one thread. In fact, the while(~closeProgram) loop indicates that you intend to use it sequentially.
Some documentation for CActiveSocket::Open reads: "Connection-based protocol sockets (CSocket::SocketTypeTcp) may successfully call Open() only once..."
Perhaps your program fails when you call .Open() twice on the same object.
I eventually found out the problem with my code. As the connection was unstable and working for 70% of the time it seemed to be a timeout issue. I removed the two timeout settings
socket.SetConnectTimeout(0, 20);
socket.SetSendTimeout(0, 20);
Now it works perfectly fine, thanks for the troubleshooting tips though!
I am having trouble using the connect() function. My code was completely working before, but now I have moved to a different physical network and my blocking call to connect() no longer works, and just seems to hang indefinitely. Receiving broadcasts over UDP still works fine. Going back to the old network it works fine again. I cant for the life of me figure out why it works on one network and not the other. I have checked firewall settings and they are correct. What could be going on?
I have a pre-defined port being used and I am getting the address from the broadcast. I use recievefrom to receive the broadcast and set the outgoing ip address from it
ret = recvfrom (bcast, bcast_read,sizeof(j4cDAC_broadcast),0,(sockaddr*)&from,&size);
to.sin_addr = from.sin_addr;
local.sin_addr.s_addr = inet_addr("0.0.0.0");
Then for the TCP connection I have
dac = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// cout << "SOCKET\n";
if (dac == INVALID_SOCKET)
{
SetConsoleTextAttribute(console,(WORD)12);
cout << "TCP socket failed: " << WSAGetLastError();
connected_ = false;
return(1);
}
//set SO_REUSEADDR on a socket to true (1):
bool optval = true;
setsockopt(dac, SOL_SOCKET,SO_DONTLINGER,(const char*)&optval, sizeof(optval));
int pies = setsockopt(dac, SOL_SOCKET,SO_REUSEADDR,(const char*)&optval, sizeof(optval) );
if (pies == SOCKET_ERROR )
{
SetConsoleTextAttribute(console,(WORD)12);
cout << "SETSOCKOPT ERROR: " << WSAGetLastError() << endl;
} // */
local_T = local;
local_T.sin_port = htons ((short)TCPport);
//bind the tcp socket
bndt = bind(dac,(SOCKADDR*) &local_T,sizeof(local_T) );
if (bndt == SOCKET_ERROR )
{
SetConsoleTextAttribute(console,(WORD)12);
cout << "BIND TCP FAILED: " << WSAGetLastError();
if (WSAGetLastError() == WSAEACCES)
cout << "ACCESS DENIED";
cout << endl;
SetConsoleTextAttribute(console,(WORD)7);
shutdown(dac,2);
closesocket(dac);
connected_ = false;
return 1;
}
c = connect(dac, (sockaddr*) &to, size); // <------- This hangs
if (c == SOCKET_ERROR)
{
cout << "connection problem: " << WSAGetLastError() <<endl;
}
connected_ = true;`
I found this to be an issue with VMWare virtual networking devices. Even though I had no virtual machines running, after much testing of various things, I found the device broadcast was being received on one of the virtual networking interfaces from VMWare somehow. Disabling these two devices has fixed this issue.