TCP failing to reconnect to server from client - c++

I am writing a TCP communication library for my codebase. I have implemented this library with an acceptor class (to be run on server), and connector class (to be run on client) and a stream class (handle sending and receiving data).
For the most part, everything works. The one problem I have is that if I disconnect the ethernet wire between client and server, and attempt to connect, I get the error that the destination was unreachable. Upon further attempts to connect, whether the ethernet cable is connected or not, I get an error that operation is in progress.
Note that if connect is called, even when reconnecting, everything works if a connection is possible.
Also, please note in my code during connect I am setting the socket option to SO_REUSEADDR, I am using SO_LINGER, and that after a failed connection I am closing all sockets.
TCPConnector class:
/** #file TCPConnector.cpp
* #brief cpp file to TCPConnector class, which encapsulates the socket mechanisms to actively
* connect to a server.
* #author Austin Small.
*/
#include "TCPConnector.h"
#include <iostream>
#include <errno.h>
/** #brief This method establishes a connection with the server (robot).
*
* #param server Server IP address.
* #param port Server port number.
* #param timeoutSec Number of seconds before timout of connect method.
*/
TCPStream* TCPConnector::connect(const char* serverIP, int port, int timeoutSec)
{
std::cout << "connect was called" << std::endl;
struct sockaddr_in address;
// Store all zeros for address struct.
memset(&address, 0, sizeof(address));
// Configure address struct.
address.sin_family = AF_INET;
address.sin_port = htons(port); // Convert from host to TCP network byte order.
inet_pton(PF_INET, serverIP, &(address.sin_addr)); // Convert IP address to network byte order.
// Create a socket. The socket signature is as follows: socket(int domain, int type, int protocol)
int sd = socket(AF_INET, SOCK_STREAM, 0);
int optval = 1;
if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval) == -1)
{
std::cout << "failed to set socket option" << std::endl;
}
// Set socket to terminate all communications when close is called.
struct linger so_linger;
so_linger.l_onoff = true;
so_linger.l_linger = 0;
if (setsockopt(sd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof so_linger) == -1)
{
std::cout << "failed to set socket option" << std::endl;
}
// Set socket to be non-blocking.
int arg;
arg = fcntl(sd, F_GETFL, NULL);
arg |= O_NONBLOCK;
fcntl(sd, F_SETFL, arg);
// Connect with time limit.
fd_set set;
FD_ZERO(&set); // Clear the set.
FD_SET(sd, &set); // Add our file descriptor to the set.
struct timeval timeout;
timeout.tv_sec = timeoutSec;
timeout.tv_usec = 0;
// If the connect call returns 0, then the connection was established. Otherwise,
// check if the three-way handshake is underway.
if (::connect(sd, (struct sockaddr *)&address, sizeof(address)) < 0)
{
// If the handshake is underway.
if (errno == EINPROGRESS)
{
std::cout << "handshake in progress" << std::endl;
// Designate timeout period.
int ret = select(sd + 1, NULL, &set, NULL, &timeout);
std::cout << "return value from select : " << ret << std::endl;
// Check if timeout or an error occurred.
if (ret <= 0)
{
std::cout << "return less than 0" << std::endl;
std::cout << "closing socket descriptor" << std::endl;
if (close(sd) < 0)
{
char * newerrorMessage = strerror( errno); // get string message from errn
std::string newmsg (newerrorMessage);
std::cout << newmsg << std::endl;
std::cout << "failed to close socket descriptor" << std::cout;
}
return NULL;
}
else
{
// Check if select returned 1 due to an error.
int valopt;
socklen_t len = sizeof(int);
getsockopt(sd, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &len);
if (valopt)
{
char * errorMessage = strerror( errno); // get string message from errn
std::string msg (errorMessage);
std::cout << msg << std::endl;
std::cout << "closing socket descriptor" << std::endl;
if (close(sd) < 0)
{
char * newerrorMessage = strerror( errno); // get string message from errn
std::string newmsg (newerrorMessage);
std::cout << newmsg << std::endl;
std::cout << "failed to close socket descriptor" << std::cout;
}
return NULL;
}
}
}
else
{
std::cout << "error but not EINPROGRESS" << std::endl;
char * errorMessage = strerror( errno); // get string message from errn
std::string msg (errorMessage);
std::cout << msg << std::endl;
if (close(sd) < 0)
{
char * newerrorMessage = strerror( errno); // get string message from errn
std::string newmsg (newerrorMessage);
std::cout << newmsg << std::endl;
std::cout << "failed to close socket descriptor" << std::cout;
}
return NULL;
}
}
// Return socket to blocking mode.
arg = fcntl(sd, F_GETFL, NULL);
arg &= (~O_NONBLOCK);
fcntl(sd, F_SETFL, arg);
// Create stream object.
return new TCPStream(sd, &address);
}
TCPStream Class:
/** #file TCPStream.cpp
* #brief cpp file to TCPStream class, which provides methods to send and receive
* data over a TCP/IP connection.
* #author Austin Small.
*/
#include "TCPStream.h"
#include <iostream>
/** #brief TCPStream class constructor.
*
* #param argsd Socket descriptor.
* #param address sockaddr_in struct.
*/
TCPStream::TCPStream(int argsd, struct sockaddr_in* address) :
sd(argsd)
{
char ip[50];
// Convert a numeric address into a text string.
// struct sockaddr_in
// {
// short sin_family;
// unsigned short sin_port;
// struct in_addr sin_addr;
// char sin_zero[8];
// };
//
inet_ntop(PF_INET, (struct in_addr*)&(address->sin_addr.s_addr), ip, sizeof(ip));
peerIP = ip;
// Convert from network byte order to host byte order.
peerPort = ntohs(address->sin_port);
}
/** #brief TCPComputerComm class destructor.
*
*/
TCPStream::~TCPStream()
{
std::cout << "closing fd" << std::endl;
if (close(sd) < 0)
{
std::cout << "file descriptor not closed successfully" << std::endl;
}
}
/** #brief Wrapper function to send data.
*
* #param buffer Pointer to first character of string.
* #param len Size of input string.
* #param timeoutSec Timeout period for write command.
*
* #return Number of bytes written, -1 if a non-timeout error occured, and -2 if a timeout occurred.
*/
ssize_t TCPStream::send(const char* buffer, size_t len, int timeoutSec)
{
// Attempt to send data with a timeout on write.
fd_set set;
FD_ZERO(&set); // Clear the set.
FD_SET(sd, &set); // Add our file descriptor to the set.
struct timeval timeout;
timeout.tv_sec = timeoutSec;
timeout.tv_usec = 0;
int ret;
ret = select(sd + 1, NULL, &set, NULL, &timeout);
// First check if an error or timeout occurred. Otherwise, call accept method.
if (ret == -1)
{
return -1;
}
else if (ret == 0)
{
return -2;
}
else
{
return write(sd, buffer, len);
}
}
/** #brief Wrapper function to receive data.
*
* #param buffer Pointer to first character of buffer to store received string.
* #param len Max number of bytes to read from file descriptor.
* #param timeoutSec Timeout period for read command.
*
* #return Number of bytes read or -1 if a non-timeout error occurred and -2 if a timeout occurred.
*/
ssize_t TCPStream::receive(char* buffer, size_t len, int timeoutSec)
{
// Attempt to send data with a timeout on write.
fd_set set;
FD_ZERO(&set); // Clear the set.
FD_SET(sd, &set); // Add our file descriptor to the set.
struct timeval timeout;
timeout.tv_sec = timeoutSec;
timeout.tv_usec = 0;
int ret;
ret = select(sd + 1, &set, NULL, NULL, &timeout);
// First check if an error or timeout occurred. Otherwise, call read method.
if (ret == -1)
{
return -1;
}
else if (ret == 0)
{
return -2;
}
else
{
//std::cout << "attempting to read" << std::endl;
return read(sd, buffer, len);
}
}
/** #brief Get peerIP address.
*
* #return peerIP address.
*/
string TCPStream::getPeerIP(void)
{
return peerIP;
}
/** #brief Get peer port.
*
* #return Peer port.
*/
int TCPStream::getPeerPort(void)
{
return peerPort;
}

Related

How to multicast with ipv6 udp socket in C/C++ on linux?

(English is not my native tongue, don't worry if some sentences are strange ;) ).
I was developing a PONG game and by the way creating some classes to help me managing window, event ... and network because I added a LAN feature to the game but currently you have to enter the address of the one with who you want to play with. And a solution to that was a broadcast (scanning LAN for player). This was easy with ipv4, just use the address 255.255.255.255 but we are in 2017 and provide a feature that works only with ipv4 sucks...
Then I look for a way to broadcast with ipv6 and I learn about multi-cast but this part just get me lost. =(
I use standard libraries on Linux in C++, I found several example of multi-cast that didn't work with me. The best I have done at this time is sending a udp packet from one instance of the program to an other on the same computer.
How can I multi-cast with ipv6 udp socket on Linux in C/C++ ?
The best code found on Internet (I rearranged it) that almost work
(there is client and serv all in one, choice is made by adding 1 or 0 to argv) :
int main(int argc, char const *argv[]) {
struct sockaddr_in6 groupSock;
int sd = -1;
char databuf[10];
int datalen = sizeof databuf;
/* Create a datagram socket on which to send/receive. */
if((sd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
perror("Opening datagram socket error");
return 1;
} else {
cout << "Opening the datagram socket...OK." << endl;;
}
/* Enable SO_REUSEADDR to allow multiple instances of this */
/* application to receive copies of the multicast datagrams. */
int reuse = 1;
if(setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof reuse) < 0) {
perror("Setting SO_REUSEADDR error");
close(sd);
return 1;
} else {
cout << "Setting SO_REUSEADDR...OK." << endl;
}
/* Initialize the group sockaddr structure with a */
memset((char *) &groupSock, 0, sizeof groupSock);
groupSock.sin6_family = AF_INET6;
// address of the group
inet_pton(AF_INET6, "ff0e::/16", &groupSock.sin6_addr);
groupSock.sin6_port = htons(4321);
/* Set local interface for outbound multicast datagrams. */
/* The IP address specified must be associated with a local, */
/* multicast capable interface. */
int ifindex = if_nametoindex ("enp3s0");
cout << "ifindex is " << ifindex << endl;
if(setsockopt(sd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof ifindex)) {
perror("Setting local interface error");
return 1;
} else {
cout << "Setting the local interface...OK" << endl;
}
// choice is 0 for sending and 1 for receiving
int choice;
if (argc < 2) {
cout << "missing argv[1]" << endl;
return 1;
}
sscanf (argv[1], "%d", &choice);
// if sending
if (choice == 0) {
memset(databuf, 'a', datalen);
databuf[sizeof databuf - 1] = '\0';
if (sendto(sd, databuf, datalen, 0, (sockaddr*)&groupSock, sizeof groupSock) < 0) {
cout << "Error in send" << endl;
} else {
cout << "Send okay!" << endl;
}
}
// if receiving
else if (choice == 1) {
groupSock.sin6_addr = in6addr_any;
if(bind(sd, (sockaddr*)&groupSock, sizeof groupSock)) {
perror("Binding datagram socket error");
close(sd);
return 1;
} else {
cout << "Binding datagram socket...OK." << endl;
}
/* Join the multicast group ff0e::/16 on the local */
/* interface. Note that this IP_ADD_MEMBERSHIP option must be */
/* called for each local interface over which the multicast */
/* datagrams are to be received. */
struct ipv6_mreq group;
inet_pton (AF_INET6, "ff0e::", &group.ipv6mr_multiaddr.s6_addr);
group.ipv6mr_interface = ifindex;
if(setsockopt(sd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&group, sizeof group) < 0) {
perror("Adding multicast group error");
close(sd);
return 1;
} else {
cout << "Adding multicast group...OK." << endl;
}
if (read(sd, databuf, datalen) < 0) {
perror("Error in read");
} else {
databuf[sizeof databuf - 1] = '\0';// just for safety
cout << "Read Okay" << endl;
cout << "Message is : " << databuf << endl;
}
}
return 0;
}
Here the address is ff0e:: but I have try with ff01:: and ff02::.
I need help, I have not found any simple documentation about that. Thanks in advance for any answer.
Edit :
Thanks Ron Maupin and Jeremy Friesner for these comments, it helps me.
Edit :
THANKS Jeremy ! Your advice to use ff12::blah:blah(...) instead of ff0e:: works ! Should I write answer to my question to close the thread ?
This code below is right:
The only thing wrong is the address used for the multicast.
Like Jeremy said it, ff0e:: is not correct, I used instead ff12::feed:a:dead:beef and it works.
It is possible to get the name and index of the available interface by using if_nameindex().
Update : I try to remove some code to see if it work without it and I manage to get this :
server :
// OPEN
int fd = socket(AF_INET6, SOCK_DGRAM, 0);
// BIND
struct sockaddr_in6 address = {AF_INET6, htons(4321)};
bind(fd, (struct sockaddr*)&address, sizeof address);
// JOIN MEMBERSHIP
struct ipv6_mreq group;
group.ipv6mr_interface = 0;
inet_pton(AF_INET6, "ff12::1234", &group.ipv6mr_multiaddr);
setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &group, sizeof group);
// READ
char buffer[128];
read(fd, buffer, sizeof buffer);
client :
// OPEN
int fd = socket(AF_INET6, SOCK_DGRAM, 0);
// ADDRESS
struct sockaddr_in6 address = {AF_INET6, htons(4321)};
inet_pton(AF_INET6, "ff12::1234", &address.sin6_addr);
// SEND TO
char buffer[128];
strcpy(buffer, "hello world!");
sendto(fd, buffer, sizeof buffer, 0, (struct sockaddr*)&address, sizeof address);

UDP socket in cpp loops for ever

I had a code that implemented several threads in c++ and it worked fine. One of those threads is a UDP server that receives messages from a UDP client. So for so good.
Now I wanted to implement a TCP server on a different thread so both a UDP client and a TCP client would be able to send messages to its proper server (they are running on different ports). After doing this, the UDP server would go nuts ... (I really do not know how to explain nuts). Please, try to follow me:
Minimal Code:
// How to compile using mysql.h
// g++ -o aserver aserver.cpp $(mysql_config --libs) -lpthread
//
//// to operate with I/O functions
#include <iostream>
#include <fstream>
// to operate with strings
#include <string>
// to operate with string streams
#include <sstream>
// to opereta with time
#include <time.h>
// to operate with directories
#include <dirent.h>
// to operate with sleep function
#include <unistd.h>
// to operate with threads
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
// to operate with sockets
#include <sys/socket.h>
#include <sys/types.h>
// Defines the structure of the socket
#include <netinet/in.h>
// Uses memset to clear the structure
#include <string.h>
#include <cerrno>
using namespace std;
// **************************************************************
// * GLOBAL VARIABLES *
// **************************************************************
int logto_id;
int udp_port;
int tcp_port;
int sock;
const int success = 0;
const int general_error = -1;
const string general_error_str = "Error";
void logto(string text, int debug_id) {
int append_status;
switch (debug_id) {
case 1:
cout << text + "\n";
break;
case 2:
break;
case 3:
break;
default:
cout << "";
}
}
int create_udp_socket() {
// UDP Socket Variables
unsigned int serverlen;
sockaddr_in udpServer;
int bind_status = 0;
string function_name="create_udp_socket: ";
/* Create the UDP socket */
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock < 0) {
cout << function_name + "Could not create UDP socket...\n";
return general_error;
}
/* Construct the server sockaddr_in structure */
memset(&udpServer, 0, sizeof(udpServer)); /* Clear struct */
udpServer.sin_family = AF_INET; /* Internet/IP */
udpServer.sin_addr.s_addr = htonl(INADDR_ANY); /* Any IP address */
udpServer.sin_port = htons(udp_port); /* server port */
/* Bind the socket */
serverlen = sizeof(udpServer);
bind_status= bind(sock, (struct sockaddr *) &udpServer, serverlen);
if (bind_status < 0) {
cout << function_name + "Could not bind UDP socket...\n";
return general_error;
} else {
cout << function_name + "UDP Socket created and binded...\n";
return success;
}
}
int create_tcp_socket() {
// TCP Socket Variables
unsigned int serverlen;
sockaddr_in tcpServer;
int bind_status = 0;
int listen_status = 0;
string function_name="create_tcp_socket: ";
/* Create the TCP socket */
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock < 0) {
cout << function_name + "Could not create TCP socket...\n";
return general_error;
}
/* Construct the server sockaddr_in structure */
memset(&tcpServer, 0, sizeof(tcpServer)); /* Clear struct */
tcpServer.sin_family = AF_INET; /* Internet/IP */
tcpServer.sin_addr.s_addr = htonl(INADDR_ANY); /* Any IP address */
tcpServer.sin_port = htons(tcp_port); /* server port */
/* Bind the socket */
serverlen = sizeof(tcpServer);
bind_status = bind(sock, (struct sockaddr *) &tcpServer, serverlen);
if (bind_status < 0) {
cout << function_name + "Could not bind TCP socket...\n";
return general_error;
} else {
cout << function_name + "TCP Socket created and binded...\n";
/* Listen */
listen_status = listen(sock,10);
if (listen_status < 0) {
cout << function_name + "Could not listen on the TCP socket...\n";
return general_error;
} else {
cout << function_name + "TCP Socket listening...\n";
return success;
}
}
}
void *thread_udp_server(void *arg) {
// **************************************************************
// * LOCAL VARIABLES *
// * we define this internal variables that before were Global *
// **************************************************************
/* here we store the SQL INSERT query */
string node_query;
/* here we find the data to build the query
* this variable is always passed by reference to all the functions
*/
string node_line;
/* UDP Socket related variables */
char udp_buffer[255];
int received = 0;
unsigned int echolen, clientlen;
sockaddr_in udpClient;
// Name of thread
string thread_name = (char*)arg;
// We start the whole thing ...
if (create_udp_socket()==success) {
/* Endless loop */
//for(;;) {
while(1) {
logto(udp_buffer,logto_id);
/* Receive a message from the client */
clientlen = sizeof(udpClient);
received = recvfrom(sock, udp_buffer, 255, 0, (struct sockaddr *) &udpClient, &clientlen);
if (received < 0) {
logto(thread_name + " Failed to receive message",logto_id);
std::cout << "Something went wrong! errno " << errno << ": ";
std::cout << strerror(errno) << std::endl;
} else {
logto("\n---------\n" + thread_name,logto_id);
/* We now copy the content of the buffer into 'node_line' */
node_line=udp_buffer;
logto(thread_name + node_line,logto_id);
}
}
} else {
logto(thread_name + " Could not bring up UDP socket...",logto_id);
std::cout << "Something went wrong! errno " << errno << ": ";
std::cout << strerror(errno) << std::endl;
return NULL;
}
}
void *thread_tcp_server(void *arg) {
// **************************************************************
// * LOCAL VARIABLES *
// * we define this internal variables that before were Global *
// **************************************************************
/* here we store the SQL INSERT query */
string node_query;
/* here we find the data to build the query
* this variable is always passed by reference to all the functions
*/
string node_line;
/* TCP Socket related variables */
char tcp_buffer[255];
int recTcp = 0;
unsigned int echolen, clientlen;
sockaddr_in tcpClient;
// Name of thread
string thread_name = (char*)arg;
// We start the whole thing ...
if (create_tcp_socket()==success) {
/* Endless loop */
for(;;) {
logto(tcp_buffer,logto_id);
/* Receive a message from the client */
clientlen = sizeof(tcpClient);
recTcp = accept(sock, (struct sockaddr *) &tcpClient, &clientlen);
if (recTcp < 0) {
logto(thread_name + " Failed to receive message",logto_id);
std::cout << "Something went wrong! errno " << errno << ": ";
std::cout << strerror(errno) << std::endl;
} else {
logto("\n---------\n" + thread_name,logto_id);
/* We now copy the content of the buffer into 'node_line' */
node_line=tcp_buffer;
logto(thread_name + node_line,logto_id);
}
}
} else {
logto(thread_name + " Could not bring up TCP socket...",logto_id);
std::cout << "Something went wrong! errno " << errno << ": ";
std::cout << strerror(errno) << std::endl;
return NULL;
}
}
// -----------------
// - main function -
// -----------------
int main () {
// **************************************************************
// * VARIABLES *
// **************************************************************
// Labels of the threads
string label_udp = "UDP_thread";
string label_tcp = "TCP_thread";
// We define the threads...
pthread_t udp_server_id=20;
pthread_t tcp_server_id=50;
udp_port = 10101;
tcp_port = 10102;
logto_id = 1;
// **************************************************************
// * START *
// **************************************************************
if ( pthread_create( &udp_server_id, NULL, thread_udp_server, (void*) label_udp.c_str()) ) {
logto("Error creating thread_udp_server...",logto_id);
return general_error;
}
if ( pthread_create( &tcp_server_id, NULL, thread_tcp_server, (void*) label_tcp.c_str()) ) {
logto("Error creating thread_tcp_server...",logto_id);
return general_error;
}
if ( pthread_join ( udp_server_id, NULL ) ) {
logto("UDP_thread couldn't join the main thread...",logto_id);
return general_error;
}
if ( pthread_join ( tcp_server_id, NULL ) ) {
logto("TCP_thread couldn't join the main thread...",logto_id);
return general_error;
}
}
After starting the program, the errno are the following, depending on which socket were brought up:
TCP ok!:
./aserver
create_tcp_socket: TCP Socket created and binded...
create_tcp_socket: TCP Socket listening...
create_udp_socket: Could not bind UDP socket...
UDP_thread Could not bring up UDP socket...
Something went wrong! errno 22: Invalid argument
UDP ok!:
./aserver
create_udp_socket: UDP Socket created and binded...
create_tcp_socket: TCP Socket created and binded...
create_tcp_socket: Could not listen on the TCP socket...
TCP_thread Could not bring up TCP socket...
Something went wrong! errno 95: Operation not supported
There is also a third case, where the UDP is brough up (the TCP socket remains down) and for some reasing, I get these messages scrolling all along the window...
./aserver
create_tcp_socket: Could not bind TCP socket...
TCP_thread Could not bring up TCP socket...
Something went wrong! errno create_udp_socket: UDP Socket created and binded...
22: UDP_thread Failed to receive message
Something went wrong! errno 107: Transport endpoint is not connectedInvalid argument
UDP_thread Failed to receive message
Something went wrong! errno 107: Transport endpoint is not connected
UDP_thread Failed to receive message
Something went wrong! errno 107: Transport endpoint is not connected
UDP_thread Failed to receive message
Something went wrong! errno 107: Transport endpoint is not connected
However, if I comment out either thread (TCP or UDP) the remaining one works ok ...
Bottom line: I cannot get both threads (UDP and TCP) to live together at the same time...
Could anyone give me a hint on this. I'm really lost on why both threads at the same time break my application ... :-(
Thanks in advance,
Lucas
It looks like you are using the same, global socket for the two threads.
int sock;
If the create_udp_socket function runs first, the socket that it creates will get overwritten by create_tcp_socket, and vice versa.
Possible solutions, either use two global sockets:
int tcp_sock;
int udp_sock;
or (better) make the create_xxx_socket functions return the sockets directly to the callers, avoiding the use of global variables.
Here's an example of the latter (error handling omitted for clarity).
int create_tcp_socket()
{
int sock;
/* Create the TCP socket */
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
/* Bind and listen... */
return sock;
}
The TCP thread would call create_tcp_socket like this.
void *thread_tcp_server(void *arg)
{
/* ... */
int sock = create_tcp_socket();
if(sock < 0)
{
logto(thread_name + " Could not bring up TCP socket...", logto_id);
return NULL;
}
/* Socket created, start accept'ing connections */
}
Global variables are bad for a number of reasons.
In multithreaded code particularly, keeping data (sock in this case) private means there is less doubt about ownership.
The code might make assumptions about who owns a global variable, but as programs grow in size this becomes impossible to manage in practice.
Contrast this with returning sock from one of the creation methods; It is easy to see that initially, sock is owned by the creation method. When the creation method returns, ownership of the socket is passed to the caller. There is never more than one function or thread with access to the socket, so concurrent access to the socket is never an issue.
Knowing who owns the data also makes it easier to release or deallocate resources when they are no longer needed. In this case, if a server thread were to exit, it - being the owner of the socket - would be responsible for closing it on its way out. And it can do so safely because no-one else could be using the socket.

How to send and receive an array of integers from client to server with C sockets?

The problem: send and receive an array of integers (later floats) from client to server using TCP and the sockets C API. Must run both in Winsock and UNIX.
In the future different endianess for client/server can be handled, but now the test was made between 2 machines with same endianess (Windows client, Linux server).
I implemented the client and server, all seems to work, but the question is a doubt on how the send() (client) and recv() (server) calls handle the way my implementation is made. Or, in other words, if the approach has a flaw.
The approach was:
On the client generate a vector of uint8_t according to a predefined algorithm (N sequences of values from 0 to 254). This sequence is reproduced in the server to compare
with the incoming data (by comparing 2 vectors).
On the client, send the array size
On the client, send the array using a loop on the array, call send() for each element.
On the server, recv() the array size.
On the server, recv() the array using a loop on the array size, call recv() for each element.
To check my approach,
I save the bytes received on the server to a file inside the previous recv() loop
After the loop , read this file, generate another vector with the same size according to step 1), compare the 2 vectors.
They match, using tests up 255,000,000 array elements sent and received.
Question:
One can then assume that the server recv() loop is guaranteed to match the client send() loop?
Or, in other words, that the array indices arrive in the same order?
I am following the excellent "TCP/IP Sockets in C" (Donahoo, Calvert) and on the example of echo client / server
http://cs.baylor.edu/~donahoo/practical/CSockets/
Quote:
"The bytes sent by a call to send() on one end of a connection may not all be returned by a single call to recv() on the other end."
The recv() part is handled differently in this example, a loop is made until the total number of bytes received matches the (known size) of bytes sent, according to:
while (totalBytesRcvd < echoStringLen)
{
bytesRcvd = recv(sock, echoBuffer, RCVBUFSIZE - 1, 0))
totalBytesRcvd += bytesRcvd; /* Keep tally of total bytes */
}
Complete example:
http://cs.baylor.edu/~donahoo/practical/CSockets/code/TCPEchoClient.c
But this case is for one send() with multiple bytes call that might be not received all at once.
In my case there are N send calls (1 byte each) and N receive calls (1 byte each), that happen to be made in the same order.
Question:
Does the TCP/IP protocol guarantee that the multiple send calls (that have sequential time stamps)
are guaranteed to be received in order? Or time not an issue here?
Some research:
When sending an array of int over TCP, why are only the first amount correct?
Quote:
"There is nothing to guarantee how TCP will packet up the data you send to a stream - it only guarantees that it will end up in the correct order at the application level."
Some more links
How do I send an array of integers over TCP in C?
Thanks
EDIT : code edited with main() functions and usage, and variable names for clarity
Usage example: send N sequences 1 time to server at IP-address
./client -i IP-address -n N -d
Code: Client.cpp
#if defined (_MSC_VER)
#include <winsock.h>
#else
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#include <iostream>
#include <stdint.h>
#include <vector>
const unsigned short server_port = 5000; // server port
void client_echo_text(const char *server_ip);
void client_send_data(const char *server_ip, const uint32_t arr_size_mult);
///////////////////////////////////////////////////////////////////////////////////////
//main
///////////////////////////////////////////////////////////////////////////////////////
// usage:
// send N sequences 1 time to server at <IP adress>
// ./client -i <IP adress> -n N -d
// same with infinite loop
// ./client -i <IP adress> -n N -l
int main(int argc, char *argv[])
{
char server_ip[255]; // server IP address (dotted quad)
strcpy(server_ip, "127.0.0.1");
uint32_t arr_size_mult = 10;
//no arguments
if (argc == 1)
{
client_send_data(server_ip, arr_size_mult);
}
for (int i = 1; i < argc && argv[i][0] == '-'; i++)
{
switch (argv[i][1])
{
case 'i':
strcpy(server_ip, argv[i + 1]);
i++;
break;
case 'e':
client_echo_text(server_ip);
exit(0);
break;
case 'n':
arr_size_mult = atoi(argv[i + 1]);
i++;
break;
case 'd':
client_send_data(server_ip, arr_size_mult);
exit(0);
break;
case 'l':
while (true)
{
client_send_data(server_ip, arr_size_mult);
}
break;
}
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////////////
//client_send_data
///////////////////////////////////////////////////////////////////////////////////////
void client_send_data(const char *server_ip, const uint32_t arr_size_mult)
{
int sock; // socket descriptor
struct sockaddr_in server_addr; // server address
//data
const uint32_t arr_size = arr_size_mult * 255; // array size
//construct array
std::vector<uint8_t> val8(arr_size);
uint8_t v8 = 0;
for (size_t i = 0; i < arr_size; ++i)
{
val8[i] = v8;
v8++;
if (v8 == 255)
{
v8 = 0;
}
}
#if defined (_MSC_VER)
WSADATA ws_data;
if (WSAStartup(MAKEWORD(2, 0), &ws_data) != 0)
{
exit(1);
}
#endif
// create a stream socket using TCP
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
exit(1);
}
// construct the server address structure
memset(&server_addr, 0, sizeof(server_addr)); // zero out structure
server_addr.sin_family = AF_INET; // internet address family
server_addr.sin_addr.s_addr = inet_addr(server_ip); // server IP address
server_addr.sin_port = htons(server_port); // server port
// establish the connection to the server
if (connect(sock, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0)
{
std::cout << "connect error: " << strerror(errno) << std::endl;
exit(1);
}
//send array size
if (send(sock, (char *)&arr_size, sizeof(uint32_t), 0) != sizeof(uint32_t))
{
exit(1);
}
std::cout << "client sent array size: " << (int)arr_size << std::endl;
//send array
for (size_t i = 0; i < arr_size; ++i)
{
v8 = val8[i];
if (send(sock, (char *)&v8, sizeof(uint8_t), 0) != sizeof(uint8_t))
{
exit(1);
}
}
std::cout << "client sent array: " << std::endl;
#if defined (_MSC_VER)
closesocket(sock);
WSACleanup();
#else
close(sock);
#endif
}
Code: Server.cpp
if defined(_MSC_VER)
#include <winsock.h>
#else
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#include <iostream>
#include <stdint.h>
#include <assert.h>
#include <vector>
const unsigned short server_port = 5000; // server port
void server_echo_text();
void server_recv_data(bool verbose);
void check_file(const uint32_t arr_size, bool verbose, const size_t slab_size);
///////////////////////////////////////////////////////////////////////////////////////
//main
///////////////////////////////////////////////////////////////////////////////////////
int main(int argc, char *argv[])
{
bool verbose = false;
//no arguments
if (argc == 1)
{
server_recv_data(verbose);
}
for (int i = 1; i < argc && argv[i][0] == '-'; i++)
{
switch (argv[i][1])
{
case 'v':
std::cout << "verbose mode: " << std::endl;
verbose = true;
break;
case 'e':
std::cout << "running echo server: " << std::endl;
server_echo_text();
exit(0);
break;
case 'd':
std::cout << "running data server: " << std::endl;
server_recv_data(verbose);
exit(0);
break;
}
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////////////
//server_recv_data
///////////////////////////////////////////////////////////////////////////////////////
void server_recv_data(bool verbose)
{
const int MAXPENDING = 5; // maximum outstanding connection requests
int server_socket; // socket descriptor for server
int client_socket; // socket descriptor for client
sockaddr_in server_addr; // local address
sockaddr_in client_addr; // client address
int recv_size; // size in bytes returned by recv()
#if defined (_MSC_VER)
int len_addr; // length of client address data structure
#else
socklen_t len_addr;
#endif
//data
uint32_t arr_size = 0;
size_t slab_size = 1;
FILE *file;
#if defined (_MSC_VER)
WSADATA ws_data;
if (WSAStartup(MAKEWORD(2, 0), &ws_data) != 0)
{
exit(1);
}
#endif
// create socket for incoming connections
if ((server_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
exit(1);
}
// construct local address structure
memset(&server_addr, 0, sizeof(server_addr)); // zero out structure
server_addr.sin_family = AF_INET; // internet address family
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // any incoming interface
server_addr.sin_port = htons(server_port); // local port
// bind to the local address
if (bind(server_socket, (sockaddr*)&server_addr, sizeof(server_addr)) < 0)
{
//bind error: Permission denied
//You're probably trying to bind a port under 1024. These ports usually require root privileges to be bound.
std::cout << "bind error: " << strerror(errno) << std::endl;
exit(1);
}
// mark the socket so it will listen for incoming connections
if (listen(server_socket, MAXPENDING) < 0)
{
exit(1);
}
for (;;) // run forever
{
// set length of client address structure (in-out parameter)
len_addr = sizeof(client_addr);
// wait for a client to connect
if ((client_socket = accept(server_socket, (struct sockaddr *) &client_addr, &len_addr)) < 0)
{
exit(1);
}
// convert IP addresses from a dots-and-number string to a struct in_addr and back
char *str_ip = inet_ntoa(client_addr.sin_addr);
std::cout << "handling client " << str_ip << std::endl;
// receive array size
if ((recv_size = recv(client_socket, (char *)&arr_size, sizeof(uint32_t), 0)) != sizeof(uint32_t))
{
exit(1);
}
std::cout << "server received array size: " << (int)arr_size << std::endl;
//save file
file = fopen("file.bin", "wb");
fwrite(&arr_size, sizeof(uint32_t), 1, file);
//receive array
for (size_t i = 0; i < arr_size; ++i)
{
uint8_t v8;
if ((recv_size = recv(client_socket, (char *)&v8, sizeof(uint8_t), 0)) != sizeof(uint8_t))
{
exit(1);
}
//write 1 element
fwrite(&v8, sizeof(uint8_t), slab_size, file);
}
fclose(file);
std::cout << "server received array: " << std::endl;
check_file(arr_size, verbose, slab_size);
// close client socket
#if defined (_MSC_VER)
closesocket(client_socket);
#else
close(client_socket);
#endif
}
}
///////////////////////////////////////////////////////////////////////////////////////
//check_file
///////////////////////////////////////////////////////////////////////////////////////
void check_file(const uint32_t arr_size, bool verbose, const size_t slab_size)
{
//read file
std::vector<uint8_t> val8(arr_size);
std::vector<uint8_t> val8_c(arr_size);
uint32_t arr_size_r;
uint8_t v8;
FILE *file;
file = fopen("file.bin", "rb");
fread(&arr_size_r, sizeof(uint32_t), 1, file);
assert(arr_size_r == arr_size);
for (size_t i = 0; i < arr_size; ++i)
{
fread(&v8, sizeof(uint8_t), slab_size, file);
val8[i] = v8;
if (verbose) std::cout << (int)val8[i] << " ";
}
if (verbose) std::cout << std::endl;
fclose(file);
//check data, define array the same as in client, compare arrays
v8 = 0;
for (size_t i = 0; i < arr_size; ++i)
{
val8_c[i] = v8;
v8++;
if (v8 == 255)
{
v8 = 0;
}
}
//compare arrays
for (size_t i = 0; i < arr_size; ++i)
{
if (val8_c[i] != val8[i])
{
std::cout << "arrays differ at: " << i << " " << (int)val8_c[i] << " " << (int)val8[i] << std::endl;
assert(0);
}
}
std::cout << "arrays match: " << (int)arr_size << " " << (int)arr_size_r << std::endl;
std::cout << std::endl;
}
TCP is a streaming protocol, it guarantees the exact replication of the sent stream at receiver. So yes, ordering will match, always. The protocol stack will reorder messages if they come out of order. So if you reliably catch the beginning of the stream and the end of the stream then everything in between will come in order and in the good shape.
I am not sure though you'd ever want to send a single number and not pre-marshal them into a large buffer. You will get several orders of magnitude improvement in performance.
As #usr pointed out, the loops are badly constructed. What is needed are "send all" and "receive all" functions.
These ones are based on the book by Stevens "UNIX Network Programming: Sockets Introduction"
http://www.informit.com/articles/article.aspx?p=169505&seqNum=9
Send all function and send function from client:
void send_all(int sock, const void *vbuf, size_t size_buf)
{
const char *buf = (char*)vbuf; // can't do pointer arithmetic on void*
int send_size; // size in bytes sent or -1 on error
size_t size_left; // size left to send
const int flags = 0;
size_left = size_buf;
while (size_left > 0)
{
if ((send_size = send(sock, buf, size_left, flags)) == -1)
{
std::cout << "send error: " << strerror(errno) << std::endl;
exit(1);
}
if (send_size == 0)
{
std::cout << "all bytes sent " << std::endl;
break;
}
size_left -= send_size;
buf += send_size;
}
return;
}
///////////////////////////////////////////////////////////////////////////////////////
//client_send_data
///////////////////////////////////////////////////////////////////////////////////////
void client_send_data(const char *server_ip, const uint32_t arr_size_mult)
{
int sock; // socket descriptor
struct sockaddr_in server_addr; // server address
//data
const uint32_t arr_size = arr_size_mult * 255; // array size
//construct array
std::vector<uint8_t> val8(arr_size);
uint8_t v8 = 0;
for (size_t i = 0; i < arr_size; ++i)
{
val8[i] = v8;
v8++;
if (v8 == 255)
{
v8 = 0;
}
}
#if defined (_MSC_VER)
WSADATA ws_data;
if (WSAStartup(MAKEWORD(2, 0), &ws_data) != 0)
{
exit(1);
}
#endif
// create a stream socket using TCP
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
exit(1);
}
// construct the server address structure
memset(&server_addr, 0, sizeof(server_addr)); // zero out structure
server_addr.sin_family = AF_INET; // internet address family
server_addr.sin_addr.s_addr = inet_addr(server_ip); // server IP address
server_addr.sin_port = htons(server_port); // server port
// establish the connection to the server
if (connect(sock, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0)
{
std::cout << "connect error: " << strerror(errno) << std::endl;
exit(1);
}
//send array size
send_all(sock, (void *)&arr_size, sizeof(uint32_t));
std::cout << "client sent array size: " << (int)arr_size << std::endl;
//send array
//std::vector.data() returns the address of the initial element in the container (C++11)
send_all(sock, (void *)val8.data(), sizeof(uint8_t) * val8.size());
std::cout << "client sent array: " << std::endl;
#if defined (_MSC_VER)
closesocket(sock);
WSACleanup();
#else
close(sock);
#endif
}
Receive all function
void recv_all(int sock, void *vbuf, size_t size_buf, FILE *file)
{
char *buf = (char*)vbuf; // can't do pointer arithmetic on void*
int recv_size; // size in bytes received or -1 on error
size_t size_left; // size left to send
const int flags = 0;
size_left = size_buf;
while (size_left > 0)
{
if ((recv_size = recv(sock, buf, size_left, flags)) == -1)
{
std::cout << "recv error: " << strerror(errno) << std::endl;
exit(1);
}
if (recv_size == 0)
{
std::cout << "all bytes received " << std::endl;
break;
}
//save to local file
fwrite(buf, recv_size, 1, file);
size_left -= recv_size;
buf += recv_size;
}
return;
}
///////////////////////////////////////////////////////////////////////////////////////
//server_recv_data
///////////////////////////////////////////////////////////////////////////////////////
void server_recv_data(bool verbose)
{
const int MAXPENDING = 5; // maximum outstanding connection requests
int server_socket; // socket descriptor for server
int client_socket; // socket descriptor for client
sockaddr_in server_addr; // local address
sockaddr_in client_addr; // client address
#if defined (_MSC_VER)
int len_addr; // length of client address data structure
#else
socklen_t len_addr;
#endif
//data
uint32_t arr_size = 0;
const size_t slab_size = 1;
FILE *file;
#if defined (_MSC_VER)
WSADATA ws_data;
if (WSAStartup(MAKEWORD(2, 0), &ws_data) != 0)
{
exit(1);
}
#endif
// create socket for incoming connections
if ((server_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
exit(1);
}
// construct local address structure
memset(&server_addr, 0, sizeof(server_addr)); // zero out structure
server_addr.sin_family = AF_INET; // internet address family
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // any incoming interface
server_addr.sin_port = htons(server_port); // local port
// bind to the local address
if (bind(server_socket, (sockaddr*)&server_addr, sizeof(server_addr)) < 0)
{
//bind error: Permission denied
//You're probably trying to bind a port under 1024. These ports usually require root privileges to be bound.
std::cout << "bind error: " << strerror(errno) << std::endl;
exit(1);
}
// mark the socket so it will listen for incoming connections
if (listen(server_socket, MAXPENDING) < 0)
{
exit(1);
}
for (;;) // run forever
{
// set length of client address structure (in-out parameter)
len_addr = sizeof(client_addr);
// wait for a client to connect
if ((client_socket = accept(server_socket, (struct sockaddr *) &client_addr, &len_addr)) < 0)
{
exit(1);
}
// convert IP addresses from a dots-and-number string to a struct in_addr and back
char *str_ip = inet_ntoa(client_addr.sin_addr);
std::cout << "handling client " << str_ip << std::endl;
///////////////////////////////////////////////////////////////////////////////////////
//receive data and save to local file as received
///////////////////////////////////////////////////////////////////////////////////////
//save local file
file = fopen(check_file_name.c_str(), "wb");
//receive/save array size
recv_all(client_socket, &arr_size, sizeof(uint32_t), file);
std::cout << "server received array size: " << (int)arr_size << std::endl;
//receive/save array
uint8_t *buf = new uint8_t[arr_size];
recv_all(client_socket, buf, sizeof(uint8_t) * arr_size, file);
delete[] buf;
fclose(file);
std::cout << "server received array: " << std::endl;
//check
check_file(arr_size, verbose, slab_size);
// close client socket
#if defined (_MSC_VER)
closesocket(client_socket);
#else
close(client_socket);
#endif
}
}

does this c++ code have memory leaks?

I'm trying to understand this Libevent c++ code I got from this page.
I'm a bit confused - am I correct to think that this code might have memory leaks?
It seems like ConnectionData pointer is created in on_connect() callback, but delete() is only called on bad read or after write is complete.
What if connection was accept()ed - but there were no reads or writes? so is that pointer just stays in daemon memory?
#include <event.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
// Read/write buffer max length
static const size_t MAX_BUF = 512;
typedef struct {
struct event ev;
char buf[MAX_BUF];
size_t offset;
size_t size;
} ConnectionData;
void on_connect(int fd, short event, void *arg);
void client_read(int fd, short event, void *arg);
void client_write(int fd, short event, void *arg);
int main(int argc, char **argv)
{
// Check arguments
if (argc < 3) {
std::cout << "Run with options: <ip address> <port>" << std::endl;
return 1;
}
// Create server socket
int server_sock = socket(AF_INET, SOCK_STREAM, 0);
if (server_sock == -1) {
std::cerr << "Failed to create socket" << std::endl;
return 1;
}
sockaddr_in sa;
int on = 1;
char * ip_addr = argv[1];
short port = atoi(argv[2]);
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
sa.sin_addr.s_addr = inet_addr(ip_addr);
// Set option SO_REUSEADDR to reuse same host:port in a short time
if (setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
std::cerr << "Failed to set option SO_REUSEADDR" << std::endl;
return 1;
}
// Bind server socket to ip:port
if (bind(server_sock, reinterpret_cast<const sockaddr*>(&sa), sizeof(sa)) == -1) {
std::cerr << "Failed to bind server socket" << std::endl;
return 1;
}
// Make server to listen
if (listen(server_sock, 10) == -1) {
std::cerr << "Failed to make server listen" << std::endl;
return 1;
}
// Init events
struct event evserver_sock;
// Initialize
event_init();
// Set connection callback (on_connect()) to read event on server socket
event_set(&evserver_sock, server_sock, EV_READ, on_connect, &evserver_sock);
// Add server event without timeout
event_add(&evserver_sock, NULL);
// Dispatch events
event_dispatch();
return 0;
}
// Handle new connection {{{
void on_connect(int fd, short event, void *arg)
{
sockaddr_in client_addr;
socklen_t len = 0;
// Accept incoming connection
int sock = accept(fd, reinterpret_cast<sockaddr*>(&client_addr), &len);
if (sock < 1) {
return;
}
// Set read callback to client socket
ConnectionData * data = new ConnectionData;
event_set(&data->ev, sock, EV_READ, client_read, data);
// Reschedule server event
event_add(reinterpret_cast<struct event*>(arg), NULL);
// Schedule client event
event_add(&data->ev, NULL);
}
//}}}
// Handle client request {{{
void client_read(int fd, short event, void *arg)
{
ConnectionData * data = reinterpret_cast<ConnectionData*>(arg);
if (!data) {
close(fd);
return;
}
int len = read(fd, data->buf, MAX_BUF - 1);
if (len < 1) {
close(fd);
delete data;
return;
}
data->buf[len] = 0;
data->size = len;
data->offset = 0;
// Set write callback to client socket
event_set(&data->ev, fd, EV_WRITE, client_write, data);
// Schedule client event
event_add(&data->ev, NULL);
}
//}}}
// Handle client responce {{{
void client_write(int fd, short event, void *arg)
{
ConnectionData * data = reinterpret_cast<ConnectionData*>(arg);
if (!data) {
close(fd);
return;
}
// Send data to client
int len = write(fd, data->buf + data->offset, data->size - data->offset);
if (len < data->size - data->offset) {
// Failed to send rest data, need to reschedule
data->offset += len;
event_set(&data->ev, fd, EV_WRITE, client_write, data);
// Schedule client event
event_add(&data->ev, NULL);
}
close(fd);
delete data;
}
//}}}
The documentation for event_set says that the only valid event types are EV_READ or EV_WRITE, but the callback will be invoked with EV_TIMEOUT, EV_SIGNAL, EV_READ, or EV_WRITE. The documentation is not clear, but I expect the read callback will be invoked when the socket is closed by the client. I expect the delete in the failure branch in client_read will handle this situation.
Note that that is only the case if the client sends a FIN or RST packet. A client could establish a connection and leave it open forever. For this reason, this code should be modified to have a timeout (perhaps via event_once) and require the client send a message within that timeout.

Validate IP Address (host) exists prior to connecting (ping)

I have a problem where I need to determine if the host exists prior to connecting to it. This host does not work with the function gethostbyaddr() because it is not PC-based and does not return host information. It is IP-based only. Whenever I try to call gethostbyaddr() on the IP address, WinSock returns 11004 (WSANODATA).
Is there a similar function (besides ping) to determine if an IP is valid before trying to connect?
If you have some kind of control over the destination host, one way you could periodically check if the host is present without using up ephemeral ports would be to send a UDP datagram, and wait for the ICMP response to tell you that the datagram was refused by the host.
You do this by creating a SOCK_DGRAM socket, binding to a local port, and calling sendto() to send to a known remote port which is not listening. You can then poll and call recvfrom() which should give an error if your host got the ICMP response back. If the host is not up then you will not get the response. You can reuse the same socket with the same port to send as many datagrams as are required periodically.
Sending ICMP echo request requires high privileges on most system, so is hard to do directly from your code.
Here is some sample code which does roughly what I describe:
struct sockaddr_in local_address;
struct sockaddr_in remote_address;
int sfd;
char * remote_host;
int s;
fd_set fds;
struct timeval timeout;
remote_host = argv[1];
sfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sfd < 0) {
perror("socket");
}
memset(&local_address, 0, sizeof(struct sockaddr_in));
local_address.sin_family = AF_INET;
local_address.sin_addr.s_addr = INADDR_ANY;
local_address.sin_port = htons(6799);
s = bind(sfd,
(struct sockaddr*)&local_address,
sizeof(local_address));
if (s != 0) {
perror("bind");
exit(1);
}
memset(&remote_address, 0, sizeof(struct sockaddr_in));
remote_address.sin_family = AF_INET;
remote_address.sin_addr.s_addr = inet_addr(remote_host);
remote_address.sin_port = htons(6799);
s = sendto(sfd,
"MSG",
3,
0,
(struct sockaddr*)&remote_address,
sizeof(remote_address));
if (s != 3) {
perror("sento");
exit(1);
}
FD_ZERO(&fds);
FD_SET(sfd, &fds);
timeout.tv_sec = 5;
timeout.tv_usec = 0;
s = select(sfd + 1, &fds, 0, 0, &timeout);
if (s == 1) {
char buf[512];
printf("Got data, host is up\n");
s = recvfrom(sfd, &buf[0], 512, 0, 0, 0);
perror("recvfrom");
} else {
printf("Timeout, host is down\n");
}
I solved the problem by using the built-in Windows API for PING. I changed the gethostbyname() to inet_addr.
shown here: ICMP.DLL Method
dllping.cpp
// Borland C++ 5.0: bcc32.cpp ping.cpp
// Visual C++ 5.0: cl ping.cpp wsock32.lib
//
// This sample program is hereby placed in the public domain.
#include <iostream.h>
#include <winsock.h>
#include <windowsx.h>
#include "icmpdefs.h"
int doit(int argc, char* argv[])
{
// Check for correct command-line args
if (argc < 2) {
cerr << "usage: ping <host>" << endl;
return 1;
}
// Load the ICMP.DLL
HINSTANCE hIcmp = LoadLibrary("ICMP.DLL");
if (hIcmp == 0) {
cerr << "Unable to locate ICMP.DLL!" << endl;
return 2;
}
// Look up an IP address for the given host name
struct hostent* phe;
if ((phe = gethostbyname(argv[1])) == 0) {
cerr << "Could not find IP address for " << argv[1] << endl;
return 3;
}
// Get handles to the functions inside ICMP.DLL that we'll need
typedef HANDLE (WINAPI* pfnHV)(VOID);
typedef BOOL (WINAPI* pfnBH)(HANDLE);
typedef DWORD (WINAPI* pfnDHDPWPipPDD)(HANDLE, DWORD, LPVOID, WORD,
PIP_OPTION_INFORMATION, LPVOID, DWORD, DWORD); // evil, no?
pfnHV pIcmpCreateFile;
pfnBH pIcmpCloseHandle;
pfnDHDPWPipPDD pIcmpSendEcho;
pIcmpCreateFile = (pfnHV)GetProcAddress(hIcmp,
"IcmpCreateFile");
pIcmpCloseHandle = (pfnBH)GetProcAddress(hIcmp,
"IcmpCloseHandle");
pIcmpSendEcho = (pfnDHDPWPipPDD)GetProcAddress(hIcmp,
"IcmpSendEcho");
if ((pIcmpCreateFile == 0) || (pIcmpCloseHandle == 0) ||
(pIcmpSendEcho == 0)) {
cerr << "Failed to get proc addr for function." << endl;
return 4;
}
// Open the ping service
HANDLE hIP = pIcmpCreateFile();
if (hIP == INVALID_HANDLE_VALUE) {
cerr << "Unable to open ping service." << endl;
return 5;
}
// Build ping packet
char acPingBuffer[64];
memset(acPingBuffer, '\xAA', sizeof(acPingBuffer));
PIP_ECHO_REPLY pIpe = (PIP_ECHO_REPLY)GlobalAlloc(
GMEM_FIXED | GMEM_ZEROINIT,
sizeof(IP_ECHO_REPLY) + sizeof(acPingBuffer));
if (pIpe == 0) {
cerr << "Failed to allocate global ping packet buffer." << endl;
return 6;
}
pIpe->Data = acPingBuffer;
pIpe->DataSize = sizeof(acPingBuffer);
// Send the ping packet
DWORD dwStatus = pIcmpSendEcho(hIP, *((DWORD*)phe->h_addr_list[0]),
acPingBuffer, sizeof(acPingBuffer), NULL, pIpe,
sizeof(IP_ECHO_REPLY) + sizeof(acPingBuffer), 5000);
if (dwStatus != 0) {
cout << "Addr: " <<
int(LOBYTE(LOWORD(pIpe->Address))) << "." <<
int(HIBYTE(LOWORD(pIpe->Address))) << "." <<
int(LOBYTE(HIWORD(pIpe->Address))) << "." <<
int(HIBYTE(HIWORD(pIpe->Address))) << ", " <<
"RTT: " << int(pIpe->RoundTripTime) << "ms, " <<
"TTL: " << int(pIpe->Options.Ttl) << endl;
}
else {
cerr << "Error obtaining info from ping packet." << endl;
}
// Shut down...
GlobalFree(pIpe);
FreeLibrary(hIcmp);
return 0;
}
int main(int argc, char* argv[])
{
WSAData wsaData;
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
return 255;
}
int retval = doit(argc, argv);
WSACleanup();
return retval;
}
icmpdefs.h
// Structures required to use functions in ICMP.DLL
typedef struct {
unsigned char Ttl; // Time To Live
unsigned char Tos; // Type Of Service
unsigned char Flags; // IP header flags
unsigned char OptionsSize; // Size in bytes of options data
unsigned char *OptionsData; // Pointer to options data
} IP_OPTION_INFORMATION, * PIP_OPTION_INFORMATION;
typedef struct {
DWORD Address; // Replying address
unsigned long Status; // Reply status
unsigned long RoundTripTime; // RTT in milliseconds
unsigned short DataSize; // Echo data size
unsigned short Reserved; // Reserved for system use
void *Data; // Pointer to the echo data
IP_OPTION_INFORMATION Options; // Reply options
} IP_ECHO_REPLY, * PIP_ECHO_REPLY;
Here you can find the source of a short DNS resolver in C++.
DNS queries are not going to help you to establish whether the box is up (which is what you seem to be trying to do).
If you can run a process on the target box, you could run a heartbeat service of some sort, which would accept a TCP connection from the monitoring app, and send an "I'm alive" message every 2.5 seconds. The inability to connect or the lack of heartbeats would tell your monitoring app that there's a problem.
Alternatively (and perhaps more straightforwardly), why not use ICMP ping?
If you're only allowed a certain number of ephemeral ports, stop using ephemeral ports. Bind the source socket to a known port number before using it to attempt to connect to the other machine.
Alternatively, you don't say why you want to avoid ping. If it's just about doing it in code, you can generate an ICMP packet yourself and use that.