Socket client recv() always returns 0 - c++

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...

Related

Can the NETLINK/SOCK_DIAG interface be used to listen for `listen()` and `close()` events of said socket?

I've been gleaning information about the NETLINK socket which allows us to listen on what is happening in socket land. It seems to very partially work, but I'm wondering whether I missed something.
I'm using C++, although of course all the NETLINK is pure C, so here we have mainly C headers. The following code has three main parts:
Binding
First I create a socket() and bind() it.
The bind() is kind of magical. When using a bound NETLINK socket, you start receiving events without having to have a polling setup (which is why I'm trying to do this, to avoid polling for when a socket starts listening for connections).
I put -1 in the nl_groups so that way all events are sent to my socket. But, at this point, I seem to only receive two of them: TCP_ESTABLISHED and TCP_CLOSE. The one I really would like to receive is the TCP_LISTEN and "not listening" (which apparently is not going to be available...)
Explicit Request
I tried with an explicit request. I have it in the code below so you can see how I've done it. That request works as expected. I get an explicit reply if the socket exists or an error "No such file or directory" when the socket is closed. Great, except that mechanism means I'd be using a poll (i.e. I need my process to try over and over again on a timer until the socket is visible).
Note: the error when no one is listening is happening because the request is explicit, i.e. it includes the expected IP address and port that I'm interested in.
Response
The next part is a loop, which sits until a response is received. The recvmsg() call is blocking in this version, which is why it sits around in this test.
If I sent my explicit request (see point 2. above), then, as I mentioned, I get a reply if another process is listening, otherwise I get an error saying it's not listening. The state is clearly set to 10 (TCP_LISTEN), so everything works as expected.
When listening to all the events (-1 in the bind), the process will go on and receive more data as events happen. However, so far, the only events I've received are 1 and 7 (i.e. TCP_ESTABLISHED and TCP_CLOSE).
I used the following to compile my code:
g++ -Wall -o a test.cpp
Here is my test code with which I can reproduce my current results:
#include <iostream>
#include <linux/netlink.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/sock_diag.h>
#include <linux/inet_diag.h>
int
main(int argc, char ** argv)
{
// socket / bind
int d = socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG);
if(d < 0)
{
std::cerr << "error: could not create RAW socket.\n";
return 1;
}
struct sockaddr_nl addr = {};
addr.nl_family = AF_NETLINK;
addr.nl_pid = getpid();
addr.nl_groups = -1;
// You can find these flags in Linux source:
//
// "/usr/src/linux-headers-4.15.0-147/include/net/tcp_states.h
//
// (1 << 7) // TCPF_CLOSE
// | (1 << 8) // TCPF_CLOSE-WAIT
// | (1 << 10) // TCPF_LISTEN
// | (1 << 11) // TCPF_CLOSING
// ;
if(bind(d, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) != 0)
{
perror("bind failure\n");
return 1;
}
// request
struct sockaddr_nl nladdr = {};
nladdr.nl_family = AF_NETLINK;
struct nl_request
{
struct nlmsghdr f_nlh;
struct inet_diag_req_v2 f_inet;
};
nl_request req = {};
req.f_nlh.nlmsg_len = sizeof(req);
req.f_nlh.nlmsg_type = SOCK_DIAG_BY_FAMILY;
req.f_nlh.nlmsg_flags = NLM_F_REQUEST;
req.f_inet.sdiag_family = AF_INET;
req.f_inet.sdiag_protocol = IPPROTO_TCP;
req.f_inet.idiag_ext = 0;
req.f_inet.pad = 0;
req.f_inet.idiag_states = 0;
req.f_inet.id.idiag_sport = htons(4998);
req.f_inet.id.idiag_dport = 0;
req.f_inet.id.idiag_src[0] = htonl(0x0A00020A);
req.f_inet.id.idiag_dst[0] = 0;
req.f_inet.id.idiag_if = 0;
req.f_inet.id.idiag_cookie[0] = INET_DIAG_NOCOOKIE;
req.f_inet.id.idiag_cookie[1] = INET_DIAG_NOCOOKIE;
struct iovec vector = {};
vector.iov_base = &req;
vector.iov_len = sizeof(req);
struct msghdr msg = {};
msg.msg_name = &nladdr;
msg.msg_namelen = sizeof(nladdr);
msg.msg_iov = &vector;
msg.msg_iovlen = 1;
int const r(sendmsg(d, &msg, 0));
if(r < 0)
{
perror("sendmsg");
return 1;
}
// response
struct sockaddr_nl r_nladdr = {};
r_nladdr.nl_family = AF_NETLINK;
struct iovec r_vector = {};
long buf[8192 / sizeof(long)];
r_vector.iov_base = buf;
r_vector.iov_len = sizeof(buf);
for(int i(1);; ++i)
{
struct msghdr r_msg = {};
r_msg.msg_name = &r_nladdr;
r_msg.msg_namelen = sizeof(r_nladdr);
r_msg.msg_iov = &r_vector;
r_msg.msg_iovlen = 1;
//std::cout << "wait for message...\n";
ssize_t size(recvmsg(d, &r_msg, 0));
if(size < 0)
{
perror("recvmsg");
return 1;
}
if(size == 0)
{
std::cout << "end of message stream received." << std::endl;
break;
}
//std::cout << "got message #" << i << ": size = " << size << std::endl;
struct nlmsghdr const * h(reinterpret_cast<struct nlmsghdr *>(buf));
if(!NLMSG_OK(h, size))
{
std::cerr << "NLMSG_OK() says there is an error." << std::endl;
return 1;
}
do
{
if(h->nlmsg_type == NLMSG_DONE)
{
std::cout << "explicit end of message stream received (NLMSG_DONE)." << std::endl;
break;
}
if(h->nlmsg_type == NLMSG_ERROR)
{
struct nlmsgerr const * err(reinterpret_cast<struct nlmsgerr const *>(NLMSG_DATA(h)));
if(h->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
{
std::cerr << "unknown NLMSG_ERROR received." << std::endl;
}
else
{
// here is the location display an error when trying to get an
// event about the LISTEN and no one is listening on that port.
//
errno = -err->error;
perror("NLMSG_ERROR:");
}
return 1;
}
if(h->nlmsg_type != SOCK_DIAG_BY_FAMILY)
{
std::cerr << "unexpected message type (h->nlmsg_type) "
<< h->nlmsg_type
<< std::endl;
return 1;
}
//std::cout << "------- sock_diag info!\n";
struct inet_diag_msg const * k_msg(reinterpret_cast<struct inet_diag_msg const *>(NLMSG_DATA(h)));
if(h->nlmsg_len < NLMSG_LENGTH(sizeof(*k_msg)))
{
std::cerr << "unexpected message length (h->nlmsg_len) "
<< h->nlmsg_type
<< std::endl;
return 1;
}
switch(k_msg->idiag_state)
{
case 1:
case 7:
break;
default:
{
std::uint32_t const src_ip(ntohl(k_msg->id.idiag_src[0]));
std::uint32_t const dst_ip(ntohl(k_msg->id.idiag_dst[0]));
std::cout << "inet_diag_msg->idiag_family = " << static_cast<int>(k_msg->idiag_family) << "\n"
<< "inet_diag_msg->idiag_state = " << static_cast<int>(k_msg->idiag_state) << "\n"
<< "inet_diag_msg->idiag_timer = " << static_cast<int>(k_msg->idiag_timer) << "\n"
<< "inet_diag_msg->idiag_retrans = " << static_cast<int>(k_msg->idiag_retrans) << "\n"
<< "inet_diag_msg->id.idiag_sport = " << ntohs(k_msg->id.idiag_sport) << "\n"
<< "inet_diag_msg->id.idiag_dport = " << ntohs(k_msg->id.idiag_dport) << "\n"
<< "inet_diag_msg->id.idiag_src[0] = " << ((src_ip >> 24) & 255)
<< "." << ((src_ip >> 16) & 255) << "." << ((src_ip >> 8) & 255) << "." << (src_ip & 255) << "\n"
<< "inet_diag_msg->id.idiag_dst[0] = " << ((dst_ip >> 24) & 255)
<< "." << ((dst_ip >> 16) & 255) << "." << ((dst_ip >> 8) & 255) << "." << (dst_ip & 255) << "\n"
<< "inet_diag_msg->id.idiag_if = " << k_msg->id.idiag_if << "\n"
<< "inet_diag_msg->id.idiag_cookie[0] = " << k_msg->id.idiag_cookie[0] << "\n"
<< "inet_diag_msg->id.idiag_cookie[1] = " << k_msg->id.idiag_cookie[1] << "\n"
<< "inet_diag_msg->idiag_expires = " << k_msg->idiag_expires << "\n"
<< "inet_diag_msg->idiag_rqueue = " << k_msg->idiag_rqueue << "\n"
<< "inet_diag_msg->idiag_wqueue = " << k_msg->idiag_wqueue << "\n"
<< "inet_diag_msg->idiag_uid = " << k_msg->idiag_uid << "\n"
<< "inet_diag_msg->idiag_inode = " << k_msg->idiag_inode << "\n"
<< "\n";
}
break;
}
// next message
//
h = NLMSG_NEXT(h, size);
}
while(NLMSG_OK(h, size));
}
return 0;
}
To test that IP:port combo, I simply used the nc command like so:
nc -l 10.0.2.10 4998
You of course need the 10.0.2.10 IP on one of your interfaces for this to work.
My question is:
Did I do something wrong that I do not receive TCP_LISTEN events on that socket unless explicitly requested?
P.S. Just in case, I tried to run this test app as root. Same results.

How to enable TCP_NODELAY option in both Server and Client?

I am implementing a communication system (tx, rx) using TCP, in windows 10. My problem is when tx sends a message to rx, the message is received with delay. When tx sends several messages, the rx starts receiving only after several messages are sent. My guess is that tx waits until its buffer gets full and then starts sending messages altogether (in my platform buffer length is 512).
As shown in the bellow picture, before receiving is started, this error appears:
ERROR receiving TCP, error #: 10014
I tried to solve this problem by enabling the TCP_NODELAY option so that the messages are being sent immediately (which was not successful). However, my knowledge of TCP is shallow and I am not sure if I am doing it correctly.
Here are my questions:
Does the delayed sending (or receiving) is related to the TCP_NODELAY option of the TCP?
If yes, am I enabling the TCP_NODELAY correctly as follows:
int yes = 1;
int resultt = setsockopt(serverSocket[0], IPPROTO_TCP, TCP_NODELAY, (char*)&yes, sizeof(int)); // 1 - on, 0 - off
if (resultt < 0)
std::cerr << "Error: TCP_NODELAY";
Am I putting the TCP_NODELAY option in the right place in both server and client codes?
Here are my codes for client:
size_t IPTunnel::ipTunnelSend_tcp(size_t process) {
size_t remaining = process;
size_t result{ 0 };
size_t sent{ 0 };
while (remaining > 0) {
result = send(clientSocket[0], &(ip_tunnel_buffer[0]) + sent, (int) remaining, 0);
if (result >= 0) {
remaining -= result;
sent += result;
}
else {
if (getLogValue()) {
if ( result == 0) std::cerr << "No data send through TCP" << std::endl;
else std::cerr << "ERROR sending TCP, error #: " << WSAGetLastError() << std::endl;
}
break;
}
}
return sent;
}
and:
bool IPTunnel::client_tcp() {
// STEP 1 - Initialize Winsock
WSADATA wsData;
WORD ver = MAKEWORD(2, 2);
int wsResult = WSAStartup(ver, &wsData);
if (wsResult != 0)
{
std::cerr << "Can't start Winsock. Error code #" << wsResult << std::endl;
return false;
}
// STEP 2 - Create a socket
SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
int yes = 1;
int resultt = setsockopt(clientSocket[0], IPPROTO_TCP, TCP_NODELAY, (char*)&yes,
sizeof(int));
if (resultt < 0)
std::cerr << "Error: TCP_NODELAY";
clientSocket.push_back(s);
if (clientSocket[0] == INVALID_SOCKET)
{
std::cerr << "Can't create socket, Error Code #" << WSAGetLastError() << std::endl;
WSACleanup();
return false;
}
// STEP 3 - Connect to the server
sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons((u_short)localMachinePort);
inet_pton(AF_INET, remoteMachineIpAddress.c_str(), &hint.sin_addr);
int connResult{ -2 };
while (connResult != 0 || numberOfTrials == 0) {
connResult = connect(clientSocket[0], (sockaddr*)& hint, sizeof(hint));
if (connResult == SOCKET_ERROR)
{
std::cerr << "Can't connect to server, Err #" << WSAGetLastError() << std::endl;
std::cerr << "Waiting " << timeIntervalSeconds << " seconds." << std::endl;
Sleep(timeIntervalSeconds * 1000);
}
if (--numberOfTrials == 0) {
std::cerr << "Reached maximum number of attempts." << std::endl;
}
}
std::cerr << "Connected!\n";
return true;
}
Here are my codes for server:
size_t IPTunnel::ipTunnelRecv_tcp(size_t space) {
char* recv_buffer = &ip_tunnel_buffer[0];
size_t remaining{ 0 };
if (outputSignals[0]->getValueType() == (signal_value_type::t_message))
{
remaining = ip_tunnel_buffer.size();
}
else
{
remaining = space * outputSignals[0]->valueTypeSizeOf();
}
size_t received{ 0 };
while (remaining > 0) {
auto aux{ 0 };
aux = recv(serverSocket[1], recv_buffer + received, (int) remaining, 0);
if (aux > 0) {
received += aux;
remaining -= received;
}
else {
if (getLogValue()) {
if (aux == 0) std::cerr << "No data received through TCP" << std::endl;
else std::cerr << "ERROR receiving TCP, error #: " << WSAGetLastError() << std::endl;
}
break;
}
}
return received;
}
and:
bool IPTunnel::server_tcp() {
// STEP 1 - Initialize Winsock
WSADATA wsData;
WORD ver = MAKEWORD(2, 2);
int wsResult = WSAStartup(ver, &wsData);
if (wsResult != 0)
{
std::cerr << "Can't start Winsock. Error code #" << wsResult << std::endl;
return false;
}
// STEP 2 - Create a socket
SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
serverSocket.push_back(s);
if (serverSocket[0] == INVALID_SOCKET)
{
std::cerr << "Can't create socket, Error Code #" << WSAGetLastError() << std::endl;
WSACleanup();
return false;
}
// STEP 3 - Bind the socket
sockaddr_in hint;
hint.sin_family = AF_INET; // AF_INET=2, IPv4
inet_pton(AF_INET, localMachineIpAddress.data(), &hint.sin_addr.S_un.S_addr);
hint.sin_port = ntohs((u_short)localMachinePort);
char ipAddress[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &hint.sin_addr.S_un.S_addr, ipAddress, INET_ADDRSTRLEN);
std::cerr << "The TCP server is running on IP address: " << ipAddress;
std::cerr << " Port: " << htons(hint.sin_port) << std::endl;
if (bind(serverSocket[0], (sockaddr*)& hint, sizeof(hint)) < 0) {
std::cerr << "Bind failed with error code #" << WSAGetLastError() << std::endl;
return false;
}
// STEP 4 - Listen on the socket for a client
if (listen(serverSocket[0], SOMAXCONN) == -1) {
std::cerr << "Listen error!" << std::endl;
return false;
}
// STEP 5 - Accept a connection from a client
sockaddr_in client;
int clientSize = sizeof(client);
s = accept(serverSocket[0], (sockaddr*) &client, &clientSize);
serverSocket.push_back(s);
// Provides information
char host[NI_MAXHOST];
char service[NI_MAXSERV];
ZeroMemory(host, NI_MAXHOST);
ZeroMemory(service, NI_MAXSERV);
if (getnameinfo((sockaddr*)& client, sizeof(client), host, NI_MAXHOST, service, NI_MAXSERV, 0) == 0)
{
std::cerr << host << " connected on port " << service << std::endl;
}
else
{
inet_ntop(AF_INET, &client.sin_addr, host, NI_MAXHOST);
std::cerr << host << " connected on port " << ntohs(client.sin_port) << std::endl;
}
int yes = 1;
int resultt = setsockopt(serverSocket[0], IPPROTO_TCP, TCP_NODELAY, (char*)&yes, sizeof(int)); // 1 - on, 0 - off
if (resultt < 0)
std::cerr << "Error: TCP_NODELAY_Server";
return true;
}
Here are the codes for sending the message (tx):
bool LoadFromCommandWindow::runBlock(void) {
int space = outputSignals[0]->space();
if (!space) return false;
if (flag && flag1)
{
std::getline(std::cin, plainData);
}
if (plainData.length() == 0)
{
flag = false;
return false;
}
else
{
data = plainData.substr(k, paddedDataLength);
if (data.length() != paddedDataLength) paddedData = padTo(data, paddedDataLength, '\0');
else paddedData = data;
outputSignals[0]->bufferPut((std::byte*) paddedData.c_str(), paddedDataLength); \\ This line puts the message into buffer
k += data.length();
if (k != plainData.length()) flag1 = false;
else
{
flag1 = true;
k = 0;
}
}
return flag;
}
std::string LoadFromCommandWindow::padTo(std::string str, const size_t num, const char paddingChar = '\0')
{
if (num > str.size())
str.insert(str.size(), num - str.size(), paddingChar);
return str;
}
Here are the codes for receiving the message (rx):
bool PrintData::runBlock(void) {
int ready = inputSignals[0]->ready();
int space = outputSignals[0]->space();
int process = std::min(ready, space);
if (process == 0) return false;
inputSignals[0]->bufferGet((std::byte*)decryptedData, decryptedDataLength);
decryptedDataLength = unpad(decryptedDataLength, decryptedData, '\0');
for (size_t i = 0; i < decryptedDataLength; i++)
{
std::cout << decryptedData[i];
}
std::cout << std::endl;
outputSignals[0]->bufferPut((std::byte*)decryptedData, decryptedDataLength);
decryptedDataLength = 496;
return true;
}
Thank you!
socket should be
int data_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
it should be enough , but it depends
bulletproof stuff is to use whole buffer transfers with buffer size on system maximum
https://stackoverflow.com/a/64726689/7172363

How can I send socket id through pthread?

I have a server in a raspberry pi, and want to allow multithreading. The server is working. The client is in windows.
I believe I need to send the socket id through the pthread_create, but haven't found how. Is there anything else I need to send?
What is the best way to do it?
I've searched the internet, stackoverflow included, and tryed some resolutions, but they didn't work.
const int PORT = 12000;
TCPServer tcp;
pthread_t my_thread[MAXCLIENTQUEUE];
int clientID = 0;
int main()
{
tcp.setup(PORT);
int clientQueueSize = 0, threadJoin = 0;
void *res;
do {
socklen_t sosize = sizeof(tcp.clientAddress);
//realizar o accept
tcp.newsockfd[clientQueueSize] = accept(tcp.sockfd, (struct sockaddr*) & tcp.clientAddress, &sosize);
if (tcp.newsockfd[clientQueueSize] == -1)
{
cout << "Error accepting -> " << tcp.newsockfd[clientQueueSize] << endl;
tcp.detach();
}
cout << ">- accept: " << strerror(errno) << " / codigo: " << tcp.newsockfd[clientQueueSize] << " - Endereco: " << inet_ntoa(tcp.clientAddress.sin_addr) << endl;
clientID++;
cout << ">>> client accepted" << " | Client ID: " << clientID << endl;
// criar threads
int ret = pthread_create(&my_thread[clientQueueSize], NULL, messenger, &tcp.newsockfd[clientQueueSize]);
cout << ">- pthread: " << strerror(errno) << " / codigo: " << ret << endl;
if (ret != 0) {
cout << "Error: pthread_create() failed\n" << "thread_n " << my_thread[clientQueueSize] << endl;
exit(EXIT_FAILURE);
}
cout << "thread n " << my_thread[clientQueueSize] << endl;
clientQueueSize++;
}
while (clientQueueSize < MAXCLIENTQUEUE);
pthread_exit(NULL);
return 0;
}
The server accepts multiple connections but only sends messages to the first client, the others connected successfully, but never receive messages.
I want for the server to be able to send messages to all the clients.
You have to create threads for all sockets.
Or, use Windows-depended async select methods.
P.S. Forget pthreads and use the standard std::thread.
map<SOCKET,std::string> clients;
void newclient(SOCKET x)
{
for(;;)
{
int r = recv(x,...);
if (r == 0 || r == -1)
break;
}
// remove it from clients, ensure proper synchronization
}
void server()
{
SOCKET x = socket(...);
bind(x,...);
listen(x,...);
for(;;)
{
auto s = accept(x,...);
if (s == INVALID_SOCKET)
break;
// save to a map, for example, after ensuring sync with a mutex and a lock guard
m[s] = "some_id";
std::thread t(newclient,s);
s.detach();
}
}
int main() //
{
// WSAStartup and other init
std::thread t(server);
t.detach();
// Message Loop or other GUI
}

Why does server output for separate clients go to the same terminal?

I'm working on a client-server application for my Operating Systems class that is supposed to simulate sales of airplane tickets. We are directed to make it so that the main thread on the TCP server is listening for incoming connections and then, as soon as we receive a client connection, creates a new thread to handle that connection. After a lot of initial confusion, I believe I have the program in a mostly-functioning state.
The problem I'm having now is that when I run all the clients from separate terminals (whether it be 2 or 5 or any other number), all the output from the server comes into the most recent terminal that I have launched it on. This isn't a huge deal in and of itself but it also means that when I use Ctrl+C to close the process running on that last terminal, it exits all clients from the server (which is a problem).
So my questions are:
1. Why is all the output from the server being directed to a single terminal rather than sending the responses to the terminal that each client process was launched from?
2. Why do all clients quit as soon as I end the process in terminal 5?
Picture of the terminals for all the clients and the server (may have to open in new tab to see everything).
Server.cpp (Needs my other class Plane.cpp to compile which I can provide if needed, but I don't think any code in there is relevant to the issue I'm facing):
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <string>
#include <sstream>
#include <stdlib.h>
#include <pthread.h>
#include "Plane.h"
using namespace std;
// default plane sizing
const int DEFAULT_ROWS = 26;
const int DEFAULT_COLUMNS = 6;
// Set up global variables for threads to access (yikes)
int rows, cols;
Plane* plane;
pthread_mutex_t mutexL = PTHREAD_MUTEX_INITIALIZER;
static int clientSocket;
int connections = 0;
void *connection_handler(void*);
struct argList {
string arg;
int row, col;
};
bool argParser(string input, argList &argL) {
stringstream ss;
ss << input;
try {
ss >> argL.arg >> argL.row >> argL.col;
} catch (exception e) {
cout << "Invalid arguments\n";
return false;
}
return true;
}
string purchaseTicket(int row, int col) {
string output;
// lock this section before we use shared resource
pthread_mutex_lock(&mutexL);
cout << "Mutex locked\n";
if (plane->isAvailable(row, col)) {
plane->buyTicket(row, col);
output = "Successfully purchased ticket for row: " + to_string(row) + ", column: " + to_string(col) + "\n";
} else {
if (row > plane->getNumRows() || row < 0 || col > plane->getNumCols() || col < 0) {
output = "Invalid seat location!\n";
} else {
output = "Seat unavailable!\n";
}
}
pthread_mutex_unlock(&mutexL);
cout << "Mutex unlocked\n";
// unlock when we're done
return output;
}
string convertMatrix(Plane plane) {
char** tempMatrix = plane.getSeatMatrix();
string seats = "";
for (int i = 0; i < plane.getNumRows(); i++) {
seats += tempMatrix[i];
seats += "\n";
}
return seats;
}
// arguments to run: column row
int main(int argc, char* argv[]) {
// array of threads (thread pool)
pthread_t threads[5];
if (argc < 3) {
rows = DEFAULT_ROWS;
cols = DEFAULT_COLUMNS;
plane = new Plane(rows, cols);
} else if (argc == 3) {
rows = atoi(argv[1]);
cols = atoi(argv[2]);
plane = new Plane(rows, cols);
} else {
cout << "Only 2 arguments allowed. You entered [" << argc << "]\n";
return -1;
}
// Create socket
int listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listen_sock == -1) {
cerr << "Failed to create socket\n";
return -1;
}
// Socket hint stuff
sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(54000);
inet_pton(AF_INET, "0.0.0.0", &hint.sin_addr);
// Bind socket to IP and port
if (bind(listen_sock, (sockaddr*)&hint, sizeof(hint)) < 0) {
cerr << "Binding to IP/Port failed\n";
return -2;
}
// Mark the socket for listening
if (listen(listen_sock, SOMAXCONN) == -1) {
cerr << "Can't listen";
return -3;
}
char host[NI_MAXHOST];
char service[NI_MAXSERV];
int numThread = 0;
while (numThread < 5) {
cout << "Listening for connections...\n";
sockaddr_in client;
socklen_t clientSize = sizeof(client);
// accept connections
clientSocket = accept(listen_sock, (sockaddr*)&client, &clientSize);
// if connection failed
if (clientSocket == -1) {
cerr << "Failed to connect with client";
return -4;
} else {
cout << "Connection successful\n";
connections++;
}
pthread_create(&threads[numThread], NULL, connection_handler, (void*) &clientSocket);
// 0 out used memory
memset(host, 0, NI_MAXHOST);
memset(service, 0, NI_MAXSERV);
int result = getnameinfo((sockaddr*)&client,
sizeof(client),
host,
NI_MAXHOST,
service,
NI_MAXSERV,
0);
if (result) {
cout << host << " connected on " << service << endl;
} else {
inet_ntop(AF_INET, &client.sin_addr, host, NI_MAXHOST);
cout << host << " connected on " << ntohs(client.sin_port) << endl;
}
numThread++;
}
// join threads together
for (int i = 0; i < numThread; i++) {
pthread_join(threads[i], NULL);
}
return 0;
}
void *connection_handler(void* listen_sock) {
cout << "Thread No: " << pthread_self() << "\n-----\n";
const int clientID = connections;
// necessary variables for processing
char buff[4096];
string custMsg;
custMsg += to_string(rows) + " " + to_string(cols) + "\n";
int msgSize = strlen(custMsg.c_str())*sizeof(char);
send(clientSocket, custMsg.c_str(), msgSize+1, 0);
// Determine what we do when we receieve messages
bool firstMsg = true;
while (true) {
memset(buff, 0, 4096);
custMsg = "";
int bytesRecv = recv(clientSocket, buff, 4096, 0);
if (bytesRecv == -1) {
pthread_mutex_lock(&mutexL);
cerr << "There was a connection issue (client " << clientID << ")\n";
pthread_mutex_unlock(&mutexL);
break;
} else if (bytesRecv == 0) {
pthread_mutex_lock(&mutexL);
cout << "Client " << clientID << " disconnected" << endl;
pthread_mutex_unlock(&mutexL);
}
if (bytesRecv > 0)
cout << "Received: " << string(buff, 0, bytesRecv) << " (client " << clientID << ")\n";
// do things based on user input
string inputStr(buff);
argList args;
if (argParser(inputStr, args)) {
if (args.arg == "buy") {
string purchResult = purchaseTicket(args.row, args.col);
custMsg += purchResult;
cout << purchResult << "------\n";
} else {
custMsg = "To buy a ticket, enter: 'buy <row> <col>'\n";
}
} else {
custMsg = "Invalid argument list";
}
//custMsg += convertMatrix(*plane);
int msgSize = strlen(custMsg.c_str())*sizeof(char);
//cout << custMsg << "\n";
cout << "Responding to client: " << clientID << "\n";
send(clientSocket, custMsg.c_str(), msgSize+1, 0);
}
// Close socket
close(clientSocket);
return 0;
}
Client.cpp:
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <fstream>
#include <sstream>
#include <time.h>
using namespace std;
struct serverInfo {
string ipAddr;
int portNum;
int timeout;
};
int getRand(int max) {
return rand() % max;
}
bool getPlaneInfo(string line, int& rows, int& cols) {
stringstream ss;
ss << line;
try {
ss >> rows >> cols;
return true;
} catch (exception e) {
cout << "Critical error\n";
return false;
}
}
void getServerInfo(ifstream &serverCfg, serverInfo &conn_serv) {
// variables that we'll read into
string label, val, eq;
int i = 0;
try { // for conversion errors
while (serverCfg >> label >> eq >> val) {
if (i == 0)
conn_serv.ipAddr = val;
else if (i == 1)
conn_serv.portNum = stoi(val);
else if (i == 2)
conn_serv.timeout = stoi(val);
else
break;
i++;
}
} catch (exception e) {
e.what();
}
}
// arguments being sent in should be 'automatic' or 'manual' for method of purchasing
// followed by the .ini file containing the server connection info.
int main(int argc, char* argv[]) {
srand(time(NULL));
// we get these int variables from the first server response
int rows, cols;
bool AUTOMATIC = false;
// make sure arguments are present and valid
if (argc != 3) {
cout << "Invalid number of arguments. Exiting...\n";
}
if (strncmp(argv[1],"automatic", 9) != 0 && strncmp(argv[1],"manual", 6) != 0) {
cout << "Invlaid arguments! Please use 'manual' or 'automatic'. Exiting...\n";
return -1;
}
// check to see if they want automatic ticket purchasing
if (strncmp(argv[1], "automatic", 9) == 0) {
AUTOMATIC = true;
}
// Handle file processing in getServerInfo function
string fileName = argv[2];
ifstream SERVER_CFG;
SERVER_CFG.open(fileName);
// store values from file in conn_info
serverInfo conn_info;
if(SERVER_CFG) {
getServerInfo(SERVER_CFG, conn_info);
} else {
cout << "Invalid filename. Exiting...\n";
return -2;
}
SERVER_CFG.close();
// create socket
int conn_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (conn_sock < 0) {
cout << "\nFailed to Create Socket. Exiting...\n";
return -3;
}
// get port and ipAddr information that we read from file
int port = conn_info.portNum;
string ipAddr = conn_info.ipAddr;
// make hint
sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(port);
inet_pton(AF_INET, ipAddr.c_str(), &hint.sin_addr);
// try to connect to server socket i times where i is conn_info.timeout
for (int i = 0; i < conn_info.timeout; i++) {
int connectVal = connect(conn_sock, (sockaddr*) &hint, sizeof(sockaddr_in));
if (connectVal < 0 && i >= conn_info.timeout-1) {
cout << "Failed to connect (" << (i+1) << ")\n";
cout << "Failed to connect after " << (i+1) << " attempts. Exiting.\n";
return -4;
} else if (connectVal == 0) {
break;
}
cout << "Failed to connect (" << (i+1) << ")\n";
sleep(1);
}
char buff[4096];
string userInput;
bool firstMsg = true;
bool needGreet = true;
do {
userInput = "";
int sendResult;
// Send a greeting message to the server to get plane info
if (needGreet) {
userInput = "Greeting the server";
send(conn_sock, userInput.c_str(), userInput.size() + 1, 0);
needGreet = false;
continue;
}
if (AUTOMATIC && !firstMsg) {
int row = getRand(20);
int col = getRand(6);
userInput = string("buy ") + to_string(row) + " " + to_string(col);
cout << "Sending request to buy seat " << row << " " << col << "\n";
sleep(1);
} else { // get input if its manual
if (!firstMsg) {
cout << "> ";
getline(cin, userInput);
}
}
// send to server
sendResult = send(conn_sock, userInput.c_str(), userInput.size() + 1, 0);
// check if sent successfully
if (sendResult < 0) { // connection error
cout << "Failed to send to server\r\n";
continue;
}
// wait for response
memset(buff, 0, 4096);
int bytesReceived = recv(conn_sock, buff, 4096, 0);
// print response
cout << "Server> " << string(buff, bytesReceived) << "\r\n";
if (firstMsg) {
string planeInf(string(buff,bytesReceived));
if (getPlaneInfo(planeInf, rows, cols)) {
cout << "Rows: " << rows << ", Columns: " << cols << "\n";
} else {
return -5;
}
firstMsg = false;
}
} while (true);
// closing socket
close(conn_sock);
return 0;
}
Any help is greatly appreciated.
The problem is your use of global variables.
Your connection thread writes a response to clientSocket, which your main changes with every connection. Every thread will write to the same socket.
You need to create a class to hold data specific to each connection, and pass a new one of those to each thread. Do not use shared global data to hold thread-specific values.

Reading ICMP reply with select in Linux using C++

I am sending an ICMP request using C++ and raw sockets to a router and after that I want to read the ICMP reply. My problem is, select() is not receiving the replay and times-out all the time. I am not getting any error (errno is returning success). The router is sending the ICMP reply, because i can see the response using Wireshark.
http://i.imgur.com/0Wra1.png
wireshark screenshot
For testing my program I am using Ubuntu 12.10 running on VirtualBox 4.2.6 and GN3 for a virtual network.
My source code:
char buffer[IP_MAXPACKET]; // for the received ICMP reply
struct iphdr *ipRec; // ICMP header
timeval tv; // timeout
fd_set mySet; // descriptor set
...
tv.tv_sec = 3; // default time-out 3s
tv.tv_usec = 0;
int retval; // select
...
do {
FD_ZERO(&mySet);
FD_SET(mysocket, &mySet);
retval = select(mysocket+1, &mySet, NULL, NULL, &tv);
cout << "Errno after select:" << strerror(errno) << endl;
if(retval == -1) {
cerr << "select error" << endl;
break;
}
else if (retval) {
if((length = recvfrom(mysocket, buffer, MAX, 0, result->ai_addr, &(result->ai_addrlen))) == -1) {
cerr << "Error: while receiving data." << endl;
}
else {
cout << "good" << endl;
ipRec = (struct iphdr*) buffer;
icmpRec = (struct icmphdr*) (buffer + ipRec->ihl * 4);
cout << "the packet." << " PID: " << ntohs(icmpRec->un.echo.id) << " Seq: " << ntohs(icmpRec->un.echo.sequence) << endl;
if ((icmpRec->type == ICMP_ECHOREPLY) && (ntohs(icmpRec->un.echo.id) == pid) && (ntohs(icmpRec->un.echo.sequence) == (seq - 1))) {
minBuff = lengthBuff;
}
}
} else {
// getting here all the time = select times-out and reads no data
cout << "mysocket:" << mysocket << endl;
cout << "retval:" << retval << endl;
maxBuff = lengthBuff;
break;
}
} while (!((icmpRec->type == ICMP_ECHOREPLY) && (ntohs(icmpRec->un.echo.id) == pid) && (ntohs(icmpRec->un.echo.sequence) == (seq - 1))));
if (packet)
delete(packet);
...
Thank you for any help.
Fixed the problem. Found the solution in a different thread: ICMP packets are not being sent C. Changing IPPROTO_RAW to IPPROTO_ICMP fixed it. Now is select reading the ICMP reply packet.