I am using sys/socket.h for sending heartbeats to a server repeatedly.
Connection work fine. Problem occurs when server restart.
This is my code.
bool HbClient::start(const char *address, int port)
{
//create socket if it is not already created
if(sock == -1)
{
sock = socket(AF_INET , SOCK_STREAM , 0);
if (sock == -1)
{
printf("Could not create socket object");
return false;
}
printf("Socket object created\n");
}
server.sin_addr.s_addr = inet_addr( address );
server.sin_family = AF_INET;
server.sin_port = htons( port );
return connect_to_server();
}
bool HbClient::connect_to_server()
{
int status = connect(sock , (struct sockaddr *)&server , sizeof(server));
cout << "returned status: " << status << endl << flush;
if (status < 0)
{
cout << "Error. Connection failed." << endl << flush;
return false;
}
cout << "Connected to server" << endl << flush;
return true;
}
bool HbClient::send_data(const char *data)
{
int res = send(sock , data , strlen(data) , MSG_NOSIGNAL);
if( res < 0)
{
cout << "Data sending failed, status: " << res << endl << flush;
start("127.0.0.1", 9090);
return false;
}
cout << "Data send" << endl << flush;
return true;
}
send_data() function is invoked repeatedly. Until server restart this works fine. But when server restart these outputs were printed repeatedly.
Data sending failed, status: -1
returned status: -1
Error. Connection failed.
I am using Ubuntu 16.04 OS and g++ compiler. Can you point out what the issue here?
Close socket and set it to -1 before reconnecting. So modify your send_data function like this:
close(sock);
sock = -1;
start("127.0.0.1", 9090);
Also socket function always return -1 on failure. You should print errno instead of returned code
I was not able to find what I was doing wrong on the internet.
My problem is that, recvfrom() function seems to remember the last value instead of making new calls and getting my buffer updated.
I've created the same code logic with Python and it works just find but I can't find a way to do the same in C++.
So that's my code :
#define _WINSOCKAPI_
#include <windows.h>
#include <winsock2.h>
#include "stdafx.h"
#include <Windows.h>
#include <iostream>
#include <stdio.h>
#include <time.h>
#pragma comment(lib, "Ws2_32.lib")
using namespace std;
int main()
{
cout << "\t\t--------------UDP Server ---------------" << endl;
cout << endl;
WSADATA WinSockData;
int iWsaStartup, iWsaCleanup;
SOCKET UDPSocketServer;
struct sockaddr_in UDPClient;
char Buffer[200];
int iBind, iReceiveFrom;
int iUDPClientLen = sizeof(UDPClient);
int iCloseSocket;
int response, offset;
ULONG cmd = 0;
iWsaStartup = WSAStartup(MAKEWORD(2, 2), &WinSockData);
if (iWsaStartup != 0) {
cout << "WSAStartup Failed" << endl;
}
cout << "WSAStartup Success" << endl;
//Setting Socket connexion information
UDPClient.sin_family = AF_INET;
UDPClient.sin_addr.s_addr = htons(INADDR_ANY);
UDPClient.sin_port = htons(40100);
//Informations to send to this address
const char* msg = "///";
size_t msg_length = sizeof(msg) - 1;
struct sockaddr_in myaddr;
memset(&myaddr, 0, sizeof(myaddr));
myaddr.sin_addr.s_addr = inet_addr("192.128.20.65");
myaddr.sin_family = AF_INET;
myaddr.sin_port = htons(40100);
//Create Socket
UDPSocketServer = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (UDPSocketServer == INVALID_SOCKET) {
cout << "Socket creation failed with error : " << WSAGetLastError() << endl;
WSACleanup();
exit(0);
}
cout << "Socket creation success" << endl;
//Binding Socket to INADDR_ANY 0.0.0.0:40100
iBind = bind(UDPSocketServer, (SOCKADDR*)&UDPClient, sizeof(UDPClient));
if (iBind == SOCKET_ERROR) {
cout << "Binding failed with error : " << WSAGetLastError() << endl;
closesocket(UDPSocketServer);
WSACleanup();
exit(0);
}
cout << "Binding success" << endl;
//Make sure the socket is blocking
if (ioctlsocket(UDPSocketServer, FIONBIO, &cmd) == SOCKET_ERROR) {
cout << "ioctlsocket failed with error : " << WSAGetLastError() << endl;
}
else
cout << "ioctlsocket success : " << cmd << endl; //Return 0 because it is set for Blocking
//Send information to 192.128.20.65:40100
response = sendto(UDPSocketServer, reinterpret_cast<const char*>(msg), msg_length, 0, (sockaddr*)&myaddr, sizeof(myaddr));
if (response == SOCKET_ERROR) {
cout << "Send failed with error : " << WSAGetLastError() << endl;
}
cout << "Send success : " << response << endl;
//Wait 4 seconds
Sleep(4000);
//Send information to 192.128.20.65:40100
response = sendto(UDPSocketServer, reinterpret_cast<const char*>(msg), msg_length, 0, (sockaddr*)&myaddr, sizeof(myaddr));
if (response == SOCKET_ERROR) {
cout << "Send failed with error : " << WSAGetLastError() << endl;
}
cout << "Send success : " << response << endl;
while (1) {
//Capture receive data from any address on port 40100
//This is supose to be a blocking function but it never block after the first return call
iReceiveFrom = recvfrom(UDPSocketServer, Buffer, sizeof(Buffer) + 1, MSG_PEEK, (SOCKADDR*)&UDPClient, &iUDPClientLen);
if (iReceiveFrom == SOCKET_ERROR) {
cout << "Receive failed with error : " << WSAGetLastError() << endl;
}
else if (iReceiveFrom != 0) {
Buffer[iReceiveFrom] = '\0'; //Add end of line to Buffer
//printf("%.*s\n", iReceiveFrom, Buffer);
cout << "Receive success : " << Buffer << endl;
}
}
iCloseSocket = closesocket(UDPSocketServer);
iWsaCleanup = WSACleanup();
if (iWsaCleanup == SOCKET_ERROR) {
cout << "WSA Cleanup failed with error : " << WSAGetLastError() << endl;
}
cout << "WSA Cleanup success" << endl;
system("PAUSE");
return 0;
}
I've created a UDP C++ socket that binds itself to 0.0.0.0:40100 and listens to that port using the function recvfrom().
The problem is that my recvfrom() function seems to never update or wait for new data. It just sends over the same old data without waiting.
I've tried to add some code that will change all the value of the buffer, but when recvfrom() is called the value recieved is the same as the old one.
From what I've read, recvfrom() function is supposed to be a blocking function, but in my case, it doesn't seems to work.
I've made sure the function was blocking by looking at ioctlsocket() function response; it's set to 0, so it's supposed to block.
I've also tried to create a new socket for my sendTo() function and I got the same result.
Finally, I've also tried to remove the sendTo() functions, but like my code in Python, no data seems to comeback from the socket if I don't send the string in the first place. (Wireshark shows that there's data that is sent to this port at all the time. Without this initialization, I can't get anything to print on my socket).
I find it strange that data is recieved on a certain port on my computer but when bind to that port I can't see this data before sending data to the address that send it in the first place. So I think the problem might happens between the sendTo() call and the recvfrom().
Using MSG_PEEK with recvfrom doesn't remove the data from the incoming data queue, it's still there the next time you call recvfrom.
I'm trying to make a HTTP proxy where, according to the GET/CONNET hostname in the HTTP request, some connections will have higher priorities over others.
The idea is to fulfill requests with higher priority, based on a given list of hostnames, each with a certain priority.
Pending connections will be stored by accepter thread in four different queues (one for each class of priority: maximum, medium, minimum and unclassified); accepter will then fork() a child process, which will dequeue and handle pending connections in order of priority. By doing so, accepter thread will always accept new connections and for every enqueued conne
In short, here's my proxy:
main: opens TCP socket, binds to a given port, listens up to 10 connections, calls thread accepter passing it the socket fd opened with the previous socket() call and joins for this thread;
accepter: this thread gets the socket fd passed from main and loops with accept() returning client socket, recv() from client, parses the request and according to the hostname in the HTTP request a custom struct of mine will be enqueued in the proper queue; it will then fork() so a process will dequeue and deal the connection;
manageConnection: this process, forked by accepter, dequeues from queues, examines the popped struct resolving the hostname field, opens a socket client, connets to the server and, GET or CONNECT, will fulfill the request.
New proxy: no more fork(), I made a thread pool of four threads (one "accepter" and three "connecter": since I'm planning to put this proxy on my RPi 2, which has a quadcore processor, I was thinking that at least four threads were good). I now have one mutex and two condition_variables. The code is almost the same, except for threads, mutexes and condition variables. These are new functions called by threads:
enqueue: this thread contains the accept() loop, where it receives from client, parses the HTTP request, finds the hostname and according to its priority, enqueue an info_conn struct (typedefed at the beginning of the code);
dequeue: this thread contains the dequeueing and managing connections loop, where it gets an info_conn struct from a queue, retrieves client socket (which I got from accept() loop), resolves hostname and manage GET or CONNECT request.
The problem: always the same, when it comes to manage CONNECT requests, recv() from client always return 0: I know recv() returns 0 when the other side of connection has disconnected, but this is not what I wanted!
Based on a thread approach, this is a trivial producer/consumer problem (popping from and pushing to queues) so I think the thread alternation on queueing and dequeueing is correct.
My (new) code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <thread>
#include <iostream>
#include <netdb.h>
#include <queue>
#include <list>
#include <vector>
#include <condition_variable>
#include <cstdlib>
using namespace std;
#define GET 0
#define CONNECT 1
#define DEFAULTCOLOR "\033[0m"
#define RED "\033[22;31m"
#define YELLOW "\033[1;33m"
#define GREEN "\033[0;0;32m"
#define MAX_SIZE 1000
#define CONNECT_200_OK "HTTP/1.1 200 Connection established\r\nProxy-agent: myproxy\r\n\r\n"
// my custom struct stored in queues
typedef struct info_connection {
int client_fd;
string host;
string payload;
int request;
} info_conn;
queue<info_conn>q1;
queue<info_conn>q2;
queue<info_conn>q3;
queue<info_conn>q4;
vector<thread> workers;
condition_variable cond_read, cond_write;
mutex mtx;
void enqueue(int sock_client);
void dequeue(void);
int main(int argc, char *argv[]) {
int socket_desc;
struct sockaddr_in server;
socket_desc = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (socket_desc == -1) {
perror("socket()");
exit(-1);
}
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
if (argc == 2)
server.sin_port = htons(atoi(argv[1]));
printf("listening to port %d\n", atoi(argv[1]));
if (bind(socket_desc,(struct sockaddr *)&server, sizeof(server)) < 0) {
perror("bind failed. Error");
exit(-1);
}
printf("binded\n");
listen(socket_desc, 10);
printf("listen\n");
// thread pool, because I suck at forking
workers.push_back(thread(enqueue, socket_desc));
workers.push_back(thread(dequeue));
workers.push_back(thread(dequeue));
workers.push_back(thread(dequeue));
for (thread& t : workers) {
t.join();
}
return 0;
}
void enqueue(int sock_client) {
printf("enqueue()\n");
int client_sock;
struct sockaddr_in *client_struct;
unsigned int clilen;
bzero((char*)&client_struct, sizeof(client_struct));
clilen = sizeof(client_struct);
char host_name[128];
char buff[4096];
int n_recv, n_send;
char *start_row, *end_row, *tmp_ptr, *tmp_start;
int req;
while( (client_sock = accept(sock_client, (struct sockaddr *)&client_struct, &clilen)) ) {
memset(host_name, 0, sizeof(host_name));
n_recv = recv(client_sock, buff, sizeof(buff), 0);
if (n_recv < 0) {
perror("recv()");
break;
}
start_row = end_row = buff;
while ((end_row = strstr(start_row, "\r\n")) != NULL) {
int row_len = end_row - start_row;
if (row_len == 0)
break;
if (strncmp(buff, "GET ", 4) == 0) {
req = GET;
tmp_start = start_row + 4;
tmp_ptr = strstr(tmp_start, "//");
int len = tmp_ptr - tmp_start;
tmp_start = tmp_start + len + 2;
tmp_ptr = strchr(tmp_start, '/');
len = tmp_ptr - tmp_start;
strncpy(host_name, tmp_start, len);
break;
}
else if (strncmp(buff, "CONNECT ", 8) == 0) {
req = CONNECT;
tmp_start = start_row + 8;
tmp_ptr = strchr(tmp_start, ':');
int host_len = tmp_ptr - tmp_start;
strncpy(host_name, tmp_start, host_len);
break;
}
start_row = end_row + 2;
/* if ((start_row - buff) >= strlen(buff))
break;*/
}
unique_lock<mutex> locker(mtx, defer_lock);
locker.lock();
cond_write.wait(locker, [](){
return (q1.size() < MAX_SIZE || q2.size() < MAX_SIZE || q3.size() < MAX_SIZE || q4.size() < MAX_SIZE);
});
cout << "(DEBUG) thread " << this_thread::get_id() << " wants to insert, queues not full " <<
q1.size() << ' ' << q2.size() << ' ' << q3.size() << ' ' << q4.size() << '\n';
int priority = 0;
info_conn info_c;
info_c.client_fd = client_sock;
info_c.host = host_name;
info_c.request = req;
info_c.payload = string(buff);
cout << "(DEBUG) thread " << this_thread::get_id() << " looking for " << host_name <<
" queues" << '\n';
if (strcmp(host_name, "www.netflix.com") == 0) {
priority = 1;
printf("hostname = www.netflix.com, priority %d\n", priority);
q1.push(info_c);
}
else if (strcmp(host_name, "www.youtube.com") == 0) {
priority = 2;
printf("hostname = www.youtube.com, priority %d\n", priority);
q2.push(info_c);
}
else if (strcmp(host_name, "www.facebook.com") == 0) {
priority = 3;
printf("hostname = www.facebook.com, priority %d\n", priority);
q3.push(info_c);
}
else {
priority = 4;
printf("hostname %s not found in queues\n", host_name);
q4.push(info_c);
}
cout << GREEN << "(DEBUG) thread " << this_thread::get_id() << " inserted " <<
q1.size() << ' ' << q2.size() << ' ' << q3.size() << ' ' << q4.size() << DEFAULTCOLOR<< '\n';
locker.unlock();
cond_read.notify_all();
}
if (client_sock < 0) {
perror("accept failed");
exit(-1);
}
}
void dequeue(void) {
int fd_client = -1;
int fd_server = -1;
struct sockaddr_in server;
int what_request;
char host_name[128];
char buffer[1500];
int n_send, n_recv;
size_t length;
info_conn req;
// CONNECT
int r, max;
int send_200_OK;
int read_from_client = 0;
int read_from_server = 0;
int send_to_client = 0;
int send_to_server = 0;
struct timeval timeout;
char buff[8192];
fd_set fdset;
printf("dequeue()\n");
while (true) {
unique_lock<mutex> locker(mtx, defer_lock);
locker.lock();
cond_read.wait(locker, [](){
return (q1.size() > 0 || q2.size() > 0 || q3.size() > 0 || q4.size() > 0);
});
cout << "(DEBUG) thread " << this_thread::get_id() << " wants to remove, queues not empty " <<
q1.size() << ' ' << q2.size() << ' ' << q3.size() << ' ' << q4.size() << '\n';
if (q1.size() > 0) {
req = q1.front();
q1.pop();
}
else if (q2.size() > 0) {
req = q2.front();
q2.pop();
}
else if (q3.size() > 0) {
req = q3.front();
q3.pop();
}
else if (q4.size() > 0) {
req = q4.front();
q4.pop();
}
cout << YELLOW <<"(DEBUG) thread " << this_thread::get_id() << " removed, " <<
q1.size() << ' ' << q2.size() << ' ' << q3.size() << ' ' << q4.size() << DEFAULTCOLOR<<'\n';
locker.unlock();
// notify one, because I have only one "producer" thread
cond_write.notify_one();
fd_client = req.client_fd;
//memcpy(host_name, req.host.c_str(), strlen(req.host));
length = req.host.copy(host_name, req.host.size(), 0);
host_name[length] = '\0';
what_request = req.request;
//memcpy(buffer, req.payload, req.payload.size());
length = req.payload.copy(buffer, req.payload.size(), 0);
buffer[length] = '\0';
what_request = req.request;
//cout << RED <<"(DEBUG) thread " << this_thread::get_id() << " copied packet payload " <<
// buffer << DEFAULTCOLOR<<'\n';
struct addrinfo* result;
struct addrinfo* res;
int error;
struct sockaddr_in *resolve;
fd_server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (fd_server < 0) {
perror("socket()");
exit(-1);
}
cout << "(DEBUG) thread " << this_thread::get_id() << " fd_server " << fd_server << '\n';
error = getaddrinfo(host_name, NULL, NULL, &result);
if (error != 0) {
if (error == EAI_SYSTEM) {
perror("getaddrinfo");
} else {
fprintf(stderr, "error in getaddrinfo for (%s): %s\n", host_name, gai_strerror(error));
}
exit(EXIT_FAILURE);
}
if (what_request == GET) {
server.sin_port = htons(80);
}
else if (what_request == CONNECT) {
server.sin_port = htons(443);
}
server.sin_family = AF_INET;
cout << "(DEBUG) thread " << this_thread::get_id() << " getaddrinfo()" << '\n';
for (res = result; res != NULL; res = res->ai_next) {
if (res->ai_family == AF_INET) {
resolve = (struct sockaddr_in *)res->ai_addr;
//char *ip = inet_ntoa(resolve->sin_addr);
//printf("%s\n", ip);
server.sin_addr.s_addr = resolve->sin_addr.s_addr;
if (connect(fd_server, (struct sockaddr *)&server, sizeof (struct sockaddr_in)) < 0) {
fflush(stdout);
perror("connect()");
}
else {
cout << "(DEBUG) thread " << this_thread::get_id() << " connected to " << inet_ntoa(server.sin_addr) << '\n';
}
break;
}
}
// dealing with GET
if (what_request == GET) {
cout << "thread " << this_thread::get_id() << " dealing GET " << host_name <<
" sending to server " << buffer << '\n';
n_send = send(fd_server, buffer, strlen(buffer)+1, 0);
if (n_send < 0) {
cout << "thread " << this_thread::get_id() << " error sending GET request to server" << '\n';
perror("send()");
break;
}
do {
memset(buffer, 0, sizeof(buffer));
n_recv = recv(fd_server, buffer, sizeof(buffer), 0);
cout << "thread " << this_thread::get_id() << " GET: " << host_name << " read from recv() " << n_recv << " bytes, " <<
fd_client << "<->" << fd_server << '\n';
n_send = send(fd_client, buffer, n_recv, 0);
} while (n_recv > 0);
if (n_recv < 0) {
cout << RED << "thread " << this_thread::get_id() << " error sending GET response from server to client" << DEFAULTCOLOR<<'\n';
perror("send()");
break;
}
close(fd_client);
close(fd_server);
cout << "thread " << this_thread::get_id() <<
" done with GET request, quitting\n";
}
// dealing with CONNECT
else if (what_request == CONNECT) {
cout << "thread " << this_thread::get_id() << " dealing CONNECT " << host_name << '\n';
max = fd_server >= fd_client ? fd_server+1 : fd_client+1;
send_200_OK = send(fd_client, CONNECT_200_OK, sizeof(CONNECT_200_OK), 0);
if (send_200_OK < 0) {
perror("send() 200 OK to client");
break;
}
cout << "thread " << this_thread::get_id() << " SENT 200 OK to client " << '\n';
int tot_recvd;
int tot_sent;
// TCP tunnel
while(true) {
memset(buff, 0, sizeof(buff));
FD_ZERO(&fdset);
FD_SET(fd_client, &fdset);
FD_SET(fd_server, &fdset);
timeout.tv_sec = 15;
timeout.tv_usec = 0;
r = select(max, &fdset, NULL, NULL, &timeout);
if (r < 0) {
perror("select()");
close(fd_client);
close(fd_server);
break;
}
if (r == 0) { // select timed out
printf("tunnel(): select() request timeout 408\n");
close(fd_client);
close(fd_server);
break;
}
if (FD_ISSET(fd_client, &fdset)) {
tot_recvd = 0;
tot_sent = 0;
do {
read_from_client = recv(fd_client, &(buff[tot_recvd]), sizeof(buff), 0);
tot_recvd += read_from_client;
cout << "thread " << this_thread::get_id() <<
" select(), reading from client " << fd_client <<
" " << read_from_client << " bytes, " << fd_client<< " <-> " << fd_server<<'\n';
if (buff[tot_recvd-1] == '\0') {
break;
}
} while (read_from_client > 0);
if (read_from_client < 0) {
perror("recv()");
close(fd_client);
close(fd_server);
break;
}
if (read_from_client == 0) {
// this always happens!!!
}
send_to_server = send(fd_server, buff, read_from_client, 0);
if (send_to_server < 0) {
perror("send() to client");
close(fd_client);
close(fd_server);
break;
}
}
if (FD_ISSET(fd_server, &fdset)) {
tot_recvd = 0;
tot_sent = 0;
do {
read_from_server = recv(fd_server, &(buff[tot_recvd]), sizeof(buff), 0);
tot_recvd += read_from_server;
cout << "thread " << this_thread::get_id() <<
" select(), reading from server " << fd_client <<
" " << read_from_server << " bytes, " << fd_client<< " <-> " << fd_server<<'\n';
if (buff[tot_recvd-1] == '\0') {
break;
}
} while (read_from_server > 0);
if (read_from_server < 0) {
perror("read()");
close(fd_client);
close(fd_server);
break;
}
if (read_from_server == 0) {
cout << "thread " << this_thread::get_id() << " select(), server closed conn" << '\n';
close(fd_client);
close(fd_server);
break;
}
send_to_client = send(fd_client, buff, read_from_server, 0);
if (send_to_client < 0) {
perror("send() to client");
close(fd_client);
close(fd_server);
break;
}
}
}
cout << "thread " << this_thread::get_id() << " done with CONNECT request\n";
}
}
}
Environment: proxy runs on my laptop running Ubuntu 14.04, x86_64; proxy is tested on Chrome with SwitchyOmega plugin, which lets redirect the traffic on a certain port (the same port I will pass to my proxy), compiled with g++ -std=c++11 -pedantic -Wall -o funwithproxyfork funwithproxyfork.cpp -lpthread.
Output (tried for Netflix and YouTube, they both has the same problem, i.e. client closed conn, recv() returns 0):
req: 1, hostname: www.netflix.com, priority: 1
thread 5611 accepting again
(CHILD 5627) is about to handle conn
(CHILD 5627) popped sock_client 4, request 1
req: 1, hostname: www2-ext-s.nflximg.net, priority: 4
thread 5611 accepting again
(CHILD 5628) is about to handle conn
(CHILD 5628) popped sock_client 4, request 1
req: 1, hostname: www2-ext-s.nflximg.net, priority: 4
thread 5611 accepting again
(CHILD 5629) is about to handle conn
(CHILD 5629) popped sock_client 4, request 1
(CHILD 5627) attempting to connect to 54.247.92.196 (www.netflix.com)
(CHILD 5628) attempting to connect to 54.247.125.40 (www.netflix.com)
(CHILD 5629) attempting to connect to 54.247.110.247 (www.netflix.com)
(CHILD 5627) connected to www.netflix.com, dealing CONNECT request
(CHILD 5628) connected to www.netflix.com, dealing CONNECT request
(CHILD 5628) client closed conn
(CHILD 5627) client closed conn
(CHILD 5628) done with CONNECT request
(CHILD 5627) done with CONNECT request
req: 1, hostname: www.netflix.com, priority: 1
thread 5611 accepting again
(CHILD 5630) is about to handle conn
(CHILD 5630) popped sock_client 4, request 1
(CHILD 5630) attempting to connect to 176.34.188.125 (www.netflix.com)
(CHILD 5629) connected to www.netflix.com, dealing CONNECT request
(CHILD 5629) client closed conn
(CHILD 5629) done with CONNECT request
(CHILD 5630) connected to www.netflix.com, dealing CONNECT request
Then it says nothing else.
From examining the code, this appears to be normal, and due to the way HTTP/1.1 works.
You are probably using some clients that support HTTP/1.1 pipelining. When HTTP/1.1 pipelining is in effect, the server will keep the connection open, in case the client wants to send another request. If the client doesn't, the client closes the connection.
It appears that your code expects the server to close the connection, after responding to the HTTP request, and you do not expect the client to close its side of the connection, first. This is not true with HTTP/1.1, where you can have either the client or the server closing the connection first. Either one closing the connection is normal.
So, there is no issue here, except for several issues that I've noted separately, in the comments, unrelated to the recv() issue at hand. Additionally, the code in many places does not sufficiently check the return value from send(), and it assumes that all bytes requested have been sent. This is false. You are not guaranteed that send() will sent the exact number of bytes requested. It can actually send fewer, and this is indicated in the return value, which is the number of bytes actually sent.
This proxy will start failing when under high traffic load, as fewer bytes will be sent, than was requested, but the code fails to detect this situation, and deal with it correctly. It will, for example, read 2000 bytes from the server, attempt to send them to the client send() reports that 1000 bytes were sent, the code continues on its merry way, and the client ends up not receiving the entire response from the server. Hillarity ensues.
Also, there are some other race conditions here that can cause the proxy to get "wedged", or locked up, with HTTP/1.1 clients that fully support pipelining. But if you start having those kinds of problems, this will have to be another question to ask...
I am learning socket programming for use in an upcoming project, and I have researched the issue pretty extensively. Basically, all this program needs to is on a client computer (locally, i.e. my computer) needs to connect to a remote server and send a command (which it has done, I have gotten it to read back Apache server stats to me).
What is happening is this: I believe I have the socket set right, but the server receives random garbage buffers (one of which consisted of " '>Z"). I have tried various socket settings, different bindings, etc.
I have in the process of starting it will initialize winsock, create a socket, bind the network, and then do a listen loop and while(1) recv data.
I have yet to get the server (on a remote computer, hosted at a datacenter) to output the message. This is my only goal for the time being. I appreciate everyone's help in advance, and the code is before (this is the entire code, sorry for the length).
Client Code:
char *host = "127.0.0.1";
SOCKET clientsock;
struct sockaddr_in server_address;
struct hostent *host_info;
WSADATA WSAData;
if(WSAStartup(MAKEWORD(2,2), &WSAData) != -1) {
cout << "WINSOCK2 Initialized" << endl;
if((clientsock = socket(AF_INET, SOCK_STREAM, 0)) != SOCKET_ERROR) {
cout << "Socket Created" << endl;
char opt[2];
opt[0] = 0;
opt[1] = 1;
//setsockopt(clientsock, SOL_SOCKET, SO_BROADCAST, opt, sizeof(opt));
host_info = gethostbyname(host);
server_address.sin_family = AF_INET;
server_address.sin_addr = *((struct in_addr *)host_info->h_addr);
server_address.sin_port = htons(80);
if(connect(clientsock, (struct sockaddr *)&server_address, sizeof(struct sockaddr)) == 0) {
cout << "Connected to host" << endl;
char COMMAND[22] = "SVR --WINSOCK-VERIFY\0";
if(send(clientsock, COMMAND, sizeof(COMMAND), 0)) {
cout << "Command Sent" << endl;
closesocket(clientsock);
}
else {
cout << "ERROR - Could not send command. " << "Error: " << WSAGetLastError() << endl;
closesocket(clientsock);
WSACleanup();
}
}
else {
cout << "ERROR - Could not connect to host. " << "Error: " << WSAGetLastError() << endl;
closesocket(clientsock);
WSACleanup();
}
}
else {
cout << "ERROR - Could not create the socket. " << "Error: " << WSAGetLastError() << endl;
WSACleanup();
}
}
else {
cout << "ERROR - Could not initialize WINSOCK2. " << "Error: " << WSAGetLastError() << endl;
WSACleanup();
}
Server Code:
SOCKET serversock;
char *server = "127.0.0.1";
//char *server = "50.31.1.180";
struct sockaddr_in server_address;
WSADATA WSAData;
if(WSAStartup(MAKEWORD(2,2), &WSAData) != -1) {
cout << "WINSOCK2 Initialized" << endl;
if((serversock = socket(PF_INET, SOCK_DGRAM, PF_UNSPEC)) != SOCKET_ERROR) {
cout << "Socket Created" << endl;
unsigned long NB = 1;
ioctlsocket(serversock, FIONBIO, &NB);
server_address.sin_family = AF_INET;
server_address.sin_addr = *((struct in_addr *)server);
server_address.sin_port = htons(21578);
if(bind(serversock, (struct sockaddr*)&server_address, sizeof(struct sockaddr) == 0)) {
cout << "Network bound" << endl;
cout << "Listening..." << endl;
listen(serversock, 5);
while(1) {
int size = sizeof((struct sockaddr *)server);
SOCKET clientsock = accept(serversock, (struct sockaddr *)server, &size);
char INCOMMAND[20];
if(clientsock >= 0) {
if(recv(clientsock, INCOMMAND, sizeof(INCOMMAND), 0)) {
int i = 0;
if(INCOMMAND == "SVR --WINSOCK-VERIFY\0") {
cout << "SVR receieved" << endl;
}
while(INCOMMAND[i] != '\0') {
cout << INCOMMAND[i];
i++;
}
cout << endl;
}
else {
cout << "ERROR - Could not receive command" << endl;
break;
}
}
}
}
else {
cout << "ERROR - Could not bind network. " << "Error: " << WSAGetLastError() << endl;
closesocket(serversock);
WSACleanup();
}
}
else {
cout << "ERROR - Could not create the socket. " << "Error: " << WSAGetLastError() << endl;
WSACleanup();
}
}
else {
cout << "ERROR - Could not initialize WINSOCK2. " << "Error: " << WSAGetLastError() << endl;
WSACleanup();
}
Calls to send/recv may not send/receive the amount of bytes you indicate in their third argument, in fact, most of the time they will send/receive less bytes than you expect. You usually have to loop until the entire data has been sent/received. Also note that doing this:
char buffer[100];
recv(clientsock, buffer, sizeof(buffer), 0);
cout << buffer;
Will most surelly print garbage, since you don't have a null terminator in your char array(whatch out for buffer overflows when appending it), and you're not checking the return value of recv. It might be reading 1 byte only(or none if an error ocurred). You're printing your buffer the same way in your server app.
In this case, you are actually sending the null-terminator, but since you might read less bytes than you expect, this character might not be received by the other application, thus printing it will print garbage chars.
Edit: You should have a look at the structure of a sockaddr struct. You can have a look at it here. In your code you are using this convertion:
int size = sizeof((struct sockaddr *)"127.0.0.1");
const char *, which is the type of "127.0.0.1", cannot be casted to a sockaddr pointer, they're incompatible. Here you should use getaddrinfo in order to resolve the IP address(note that you could use a domain name, and this function would resolve it). There are lots of tutorials online on how to use this function, just search for "getaddrinfo".
I have a program that needs to constantly ping a device
However this ping function fails exactlly after 1020 tries
int ping(string target)
{
int s, i, cc, packlen, datalen = DEFDATALEN;
struct hostent *hp;
struct sockaddr_in to, from;
//struct protoent *proto;
struct ip *ip;
u_char *packet, outpack[MAXPACKET];
char hnamebuf[MAXHOSTNAMELEN];
string hostname;
struct icmp *icp;
int ret, fromlen, hlen;
fd_set rfds;
struct timeval tv;
int retval;
struct timeval start, end;
int /*start_t, */end_t;
bool cont = true;
to.sin_family = AF_INET;
// try to convert as dotted decimal address, else if that fails assume it's a hostname
to.sin_addr.s_addr = inet_addr(target.c_str());
if (to.sin_addr.s_addr != (u_int)-1)
hostname = target;
else
{
hp = gethostbyname(target.c_str());
if (!hp)
{
cerr << "unknown host "<< target << endl;
return -1;
}
to.sin_family = hp->h_addrtype;
bcopy(hp->h_addr, (caddr_t)&to.sin_addr, hp->h_length);
strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1);
hostname = hnamebuf;
}
packlen = datalen + MAXIPLEN + MAXICMPLEN;
if ( (packet = (u_char *)malloc((u_int)packlen)) == NULL)
{
cerr << "malloc error\n";
return -1;
}
/*
if ( (proto = getprotobyname("icmp")) == NULL)
{
cerr << "unknown protocol icmp" << endl;
return -1;
}
*/
if ( (s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0)
{
perror("socket"); /* probably not running as superuser */
return -1;
}
icp = (struct icmp *)outpack;
icp->icmp_type = ICMP_ECHO;
icp->icmp_code = 0;
icp->icmp_cksum = 0;
icp->icmp_seq = 12345; /* seq and id must be reflected */
icp->icmp_id = getpid();
cc = datalen + ICMP_MINLEN;
icp->icmp_cksum = in_cksum((unsigned short *)icp,cc);
gettimeofday(&start, NULL);
i = sendto(s, (char *)outpack, cc, 0, (struct sockaddr*)&to, (socklen_t)sizeof(struct sockaddr_in));
if (i < 0 || i != cc)
{
if (i < 0)
perror("sendto error");
cout << "wrote " << hostname << " " << cc << " chars, ret= " << i << endl;
}
// Watch stdin (fd 0) to see when it has input.
FD_ZERO(&rfds);
FD_SET(s, &rfds);
// Wait up to one seconds.
tv.tv_sec = 1;
tv.tv_usec = 0;
while(cont)
{
retval = select(s+1, &rfds, NULL, NULL, &tv);
if (retval == -1)
{
perror("select()");
return -1;
}
else if (retval)
{
fromlen = sizeof(sockaddr_in);
if ( (ret = recvfrom(s, (char *)packet, packlen, 0,(struct sockaddr *)&from, (socklen_t*)&fromlen)) < 0)
{
perror("recvfrom error");
return -1;
}
// Check the IP header
ip = (struct ip *)((char*)packet);
hlen = sizeof( struct ip );
if (ret < (hlen + ICMP_MINLEN))
{
cerr << "packet too short (" << ret << " bytes) from " << hostname << endl;;
return -1;
}
// Now the ICMP part
icp = (struct icmp *)(packet + hlen);
if (icp->icmp_type == ICMP_ECHOREPLY)
{
cout << "Recv: echo reply"<< endl;
if (icp->icmp_seq != 12345)
{
cout << "received sequence # " << icp->icmp_seq << endl;
continue;
}
if (icp->icmp_id != getpid())
{
cout << "received id " << icp->icmp_id << endl;
continue;
}
cont = false;
}
else
{
cout << "Recv: not an echo reply" << endl;
continue;
}
gettimeofday(&end, NULL);
end_t = 1000000*(end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec);
if(end_t < 1)
end_t = 1;
cout << "Elapsed time = " << end_t << " usec" << endl;
return end_t;
}
else
{
cout << "No data within one seconds.\n";
return 0;
}
}
return 0;
}
If i run the ping function twice it fails at exactly half 510. And the pattern is exact. Here is the output after the 1020 mark
socket: Too many open files
ping returned -1
Im pretty bad at network programming but i dont even know where to begin for the above code.
Am i missing a close somewhere?
The ping function is taken from: http://www.linuxforums.org/forum/networking/60389-implementing-ping-c.html
Edit: Running Ubuntu 10.10, same issue on Fedora 13
Thanks
Yes, you need to close the socket with close(s); before exiting the function.
When allocating resources you need to make sure to free them.
As unwind pointed out, you have to close sockets.
But you also need to free the allocated memory (malloc, line 40-ish).
You should consider using memcpy instead of bcopy.
Agree with "unwind".
Note that you run a function, not a program. Hence your process is still alive, and all the memory/resource leaks are not automatically freed.
As a general rule, if you see in the code a call to a socket function without the corresponding closesocket - this must always scratch your eyes!