Producer/Consumer High performance UDP packet capture/sniffing in C++ - c++

I am struggling a UDP packet sniffing program which shall capture packets as efficient as wireshark. What I simply do is opening a UDP socket with highest thread priority to capture packets from 192.168.2.20 over port 5001.
After quite a few of trials (sending a couple of seconds of UDP transmission from another computer which has 192.168.2.20 interface on port 5001 using iperf), I come up with the solution of producer/consumer multithreaded program under c++. My objection is to printout the size and the identification number of received packet until the transmission ends (the program will run for weeks).
So, I use the producer buffer as a queue to capture received UDP packets until reaching a queue limit (i.e. 40000). After the limit is achieved, the producer copies its content into another queue buffer and clears its content to continue receiving UDP packets which consumer thread will utilizes so that no thread synchronization will be needed. However, my program does not work perfectly. Below is my code. How can I achieve my goal (printing out total number of received UDP packets and its identification numbers more efficiently)
#include <iostream>
#include <thread>
#include <array>
#include <vector>
#include <mutex>
#include <string>
#include <unistd.h>
#include <condition_variable>
#include <queue>
#include <algorithm>
#include <string.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <chrono>
#include <sys/time.h>
#include <ctime>
#include <numeric>
using namespace std;
const int BUFFER_SIZE = 2000;
#define ETH_DATA_LEN 1512
#define UDP 0x11
#define SRC_ADDR "192.168.2.20"
mutex m;
mutex m_print;
bool is_qq_empty = true;
bool is_transmission_continue = true;
bool is_producer_started = false;
struct ReceiveBufferArray {
uint8_t buf[ETH_DATA_LEN];
int id;
time_t time;
int index;
};
vector<int> packetSize;
vector<int> consume_buffer;
vector<int> loss_buffer;
vector<std::time_t> time_buffer;
std::queue<ReceiveBufferArray> qq;
std::queue<ReceiveBufferArray> qq_copy;
int gmSocket;
struct sockaddr_in gmClientAddr;
struct sockaddr_in gmServerAddr;
socklen_t gmClientLen = sizeof(gmServerAddr);
int openSocket(const std::string &IpAddress, int Port)
{
int ret;
struct timeval timeout;
int optval = 1;
gmSocket = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
if (gmSocket < 0)
{
std::cout << "cannot Open datagram socket!! Ip: " << IpAddress << " - Port " << std::to_string(Port) << std::endl;
return -1;
}
/* Bind our local address so that the client can send to us */
gmServerAddr.sin_family = AF_INET;
gmServerAddr.sin_addr.s_addr =INADDR_ANY;
gmServerAddr.sin_port = htons(Port);
timeout.tv_sec = 10;// timeout for 10seconds
timeout.tv_usec = 0;
setsockopt(gmSocket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
setsockopt(gmSocket, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
setsockopt(gmSocket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
std::cout << "Socket has been opened. Ip: " << IpAddress << " - Port " << std::to_string(Port) << std::endl;
return 0;
}
void clear(std::queue<ReceiveBufferArray> &q)
{
std::queue<ReceiveBufferArray> empty;
std::swap(q,empty);
}
std::queue<ReceiveBufferArray> copy_queue(std::queue<ReceiveBufferArray> &q)
{
std::queue<ReceiveBufferArray>Q2 =q;
return Q2;
}
void consumer_thread()
{
struct sockaddr_in source_socket_address, dest_socket_address;
memset(&source_socket_address, 0, sizeof(source_socket_address));
memset(&dest_socket_address, 0, sizeof(dest_socket_address));
uint8_t ethernet_data[ETH_DATA_LEN];
int old_val = 99999;
bool start_copying_flag = false;
while (is_transmission_continue || qq_copy.empty())
{
if (!qq_copy.empty())
{
start_copying_flag = true;
// Record start time
m.lock();
std::copy(std::begin(qq_copy.front().buf),std::end(qq_copy.front().buf), std::begin(ethernet_data));
qq_copy.pop();
m.unlock();
struct iphdr *ip_packet = (struct iphdr *)ethernet_data;
if((ip_packet->saddr == inet_addr(SRC_ADDR)) && (ip_packet->protocol == UDP))
{
consume_buffer.push_back(ntohs(ip_packet->id));
std::cout << "id: " << std::to_string(ntohs(ip_packet->id))
<< ", Packet Number: " << std::to_string(consume_buffer.size())<<endl;
}
usleep(1);
}else if(qq_copy.empty() && start_copying_flag)
{
if(qq.size()>0)
{
m.lock();//##################################################3
qq_copy = copy_queue(qq);
clear(qq);
m.unlock();//##################################################3
}
}
}
}
void producer_thread()
{
int packet_size;
openSocket(SRC_ADDR,5001);
ReceiveBufferArray _rbuf;
int counter = 0;
while (is_transmission_continue)
{
packet_size = recvfrom(gmSocket , _rbuf.buf , ETH_DATA_LEN , 0 , NULL, NULL);
if (qq.size() < 40000)
{
counter++;
m.lock();
qq.push(_rbuf);
m.unlock();
std::cout <<"Packet Size : " << counter << endl;
}else {
std::cout << "PRODUCER EMPTY" << endl;
m.lock();//##################################################3
qq_copy = copy_queue(qq);
m.unlock();//##################################################3
clear(qq);
is_producer_started = true;
}
if((packet_size < 0) && is_producer_started){
is_transmission_continue =false;
}
}
std::cout << "PRODUCER DONE" << endl;
}
int main()
{
setpriority(PRIO_PROCESS, 0, -20);
thread cons(consumer_thread);
thread prod(producer_thread);
prod.join();
cons.join();
return 0;
}

Related

Multiple messages in a tcp buffer

I am actually programming a simple server and it's client in c++ using the tcp protocol. As this is to be integrated in a multiplayer game, every client has to send data extremely fast.
Issue: The server's buffer sometimes gets multiple messages in it.
I tried various things like putting off nagle's algorithm but I didn't manage to fix this problem. Here's the server's code :
#ifdef __linux__
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <arpa/inet.h>
#define SOCKET int
#define SOCKADDR_IN struct sockaddr_in
#endif
#ifdef _WIN32
#include <winsock2.h>
#endif
#include <cstdio>
#include <iostream>
#include <thread>
#include <vector>
#include <string>
#include "server.h"
#include "../../Logger/logger.h"
#include "../../AltisCraft.fr/Map/map.h"
#include "../../StringPlus/string_plus.h"
#include "../../AltisCraft.fr/Map/User/User.h"
void connectEvent(), receive(), sendAllUsers(string), closeConnectio(),manageMsg();
vector<SOCKET> clients;
vector<thread> clientsThreads;
vector<string> msg;
SOCKET socketId, newSocketId;
SOCKADDR_IN source;
thread connection;
char buffer[65535] = {0};
int position;
// TODO: crypt every data sendLog/receive
// TODO: whitelist ip serv
// TODO: Auth system
// TODO: timer with packet ? (double receive...)
int sendLog(SOCKET s, const char* c, int i0, int i1)
{
log("Send:");
log(c);
send(s, c, i0, i1);
}
void initializeNetwork()
{
#ifdef _WIN32
WSADATA initWin32;
WSAStartup(MAKEWORD(2, 2),&initWin32);
#endif
socketId = socket(AF_INET, SOCK_STREAM, 0);
source.sin_family = AF_INET;
source.sin_addr.s_addr = INADDR_ANY;
source.sin_port = htons(33333);
bind(socketId, (struct sockaddr*)&source, sizeof(source));
connection = thread(&connectEvent);
connection.join();
closeConnection();
}
void connectEvent()
{
int error;
while(1)
{
error = 99;
while(error != 0)
{
error = listen(socketId, 1);
}
#ifdef _WIN32
int tempo = sizeof(source);
newSocketId = accept(socketId, (struct sockaddr*)&source, &tempo);
clients.push_back(newSocketId);
#endif
#ifdef __linux__
socklen_t tempo;
newSocketId = accept(socketId, (struct sockaddr *)&source, &tempo);
clients.push_back(newSocketId);
#endif
clientsThreads.push_back(thread(&receive));
}
}
void receive()
{
int val = 1;
position = clients.size() - 1;
bool connected = 1;
while(connected)
{
buffer[65535] = {0};
if(recv(clients[position], buffer, 1515, 0) > 0)
{
string msg = buffer;
bool isEmpty = false;
log(string(inet_ntoa(source.sin_addr)) + ": " + msg);
if(startsWith(msg, "Connect "))
addUser(replace(msg, "Connect ", ""));
else if(msg == "MAJ Map")
{
log(elements);
string toSend = "MAJ Map\n" + elements;
sendLog(clients[position], toSend.c_str(), strlen(toSend.c_str()), 0);
}
else if(startsWith(msg, "MAJ User ")) /// optimize: don't sendLog pos to player who sendLog
{
msg = replace(msg, "MAJ User ", "");
if(startsWith(msg, "Pos "))
{
msg = replace(msg, "Pos ", "");
vector<string> elements = split(msg, " ");
User user = *getUserByName(elements[0] + " " + elements[1]);
user.updateView(user.getView().updatePosition(Position(convertStrToDouble(elements[2]), convertStrToDouble(elements[3]), convertStrToDouble(elements[4]))));
}
else if(startsWith(msg, "ViewAngle "))
{
msg = replace(msg, "ViewAngle ", "");
vector<string> elements = split(msg, " ");
User user = *getUserByName(elements[0] + " " + elements[1]);
user.updateView(user.getView().updateViewAngle(ViewAngle(convertStrToDouble(elements[2]), convertStrToDouble(elements[3]))));
}
}
else
sendAllUsers(string(string(inet_ntoa(source.sin_addr)) + ": " + msg).c_str());
}
else
connected = 0;
}
shutdown(clients[position], 2);
for(int i=0;i<msg.size();i++)
cout << msg[i] << endl;
#ifdef _WIN32
closesocket(clients[position]);
#endif
#ifdef __linux__
close(clients[position]);
#endif
clients.erase(clients.begin() + position);
}
void sendAllUsersWithoutOne(string msg, string name)
{
for(int j = 0; j < (int)clients.size(); j++)
{
// only linux here (MSG_DONTWAIT)
#ifdef __linux__
if(recv(clients[j], NULL, 1, MSG_PEEK | MSG_DONTWAIT) == 0)
{
clients.erase(clients.begin() + j);
continue;
}
#endif
sendLog(clients[j], msg.c_str(), strlen(msg.c_str()), 0);
}
}
void sendAllUsers(string msg)
{
for(int j = 0; j < (int)clients.size(); j++)
{
// only linux here (MSG_DONTWAIT)
#ifdef __linux__
if(recv(clients[j], NULL, 1, MSG_PEEK | MSG_DONTWAIT) == 0)
{
clients.erase(clients.begin() + j);
continue;
}
#endif
sendLog(clients[j], msg.c_str(), strlen(msg.c_str()), 0);
}
}
void closeConnection()
{
for(int i = 0; i < (int)clients.size(); i++)
{
shutdown(clients[i], 2);
#ifdef _WIN32
closesocket(clients[i]);
#endif
#ifdef __linux__
close(clients[i]);
#endif
}
#ifdef _WIN32
closesocket(socketId);
WSACleanup();
#endif
#ifdef __linux__
close(socketId);
#endif
}
void freeNetwork()
{
closeConnection();
}`
to expand on Barmar's comment
TCP is a streaming protocol, not a message protocol. THe only guarantee is that you send n bytes, you will receive n bytes in the same order.
You might send 1 chunk of 100 bytes and receive 100 1 byte recvs, or you might receive 20 5 bytes recvs
You could send 100 1 byte chunks and receive 4 25 byte messages
You must deal with message boundaries yourself. Either have a sentinel value to mark start and end or prepend a length that is a fixed size itself (so you know you have read the whole length). Then loop on recv till you have received the whole message

Boost Tcp Client Receive Crash

I have been working recently with the Boost library for windows visual studio 2015. I have been recently working on a server and client that can easily transfer data via tcp connection. But recently I was trying to send a Base64 encoded file string to the client which is roughly 366,660 Bytes of data. To do this I split up the data into packets that are about 1000 in size. (not sure what the max size is) But anyway the server sends the data completely fine but when the client receives more than 160,000 bytes it crashes with no exceptions.
Client:
try
{
for (static int i = 1000; i <= sizeofpackets /*(366,660) (currently 170,000 for testing)*/; i += 1000)
{
char Buffer[1000];
memset(Buffer, 0, 1000);
boost::asio::read(s, boost::asio::buffer(Buffer, 1000));
std::cout << i << std::endl;
}
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
Server:
for (int i = 1000; i <= 170000; i += 1000)
{
std::string NewData = encodedBuffer.substr(i - 1000, i);
NewData.erase(0, i - 1000);
boost::asio::write(*sock, boost::asio::buffer(NewData, 1000));
std::cout << NewData.size() + i - 1000 << std::endl;
NewData.clear();
}
Any comments or suggestion would help greatly!
You haven't posted your complete code, so it is hard to tell, but your problem might be the way you are handling strings, not the way you are handling the socket transfers (the substr() member function takes a start offset and a size, and it looks like you are trying to use an ever-increasing size for NewData).
The following (complete) code, which uses your client and server code as a reference, but with the string handling changed, does transfer 170000 bytes just fine on my machine, so it may help:
#include <boost/asio/io_service.hpp>
#include <boost/asio/write.hpp>
#include <boost/asio/read.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/streambuf.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <array>
#include <string>
#include <iostream>
namespace
{
const size_t BIG_MESSAGE_SIZE = 170000;
const size_t BLOCK_SIZE = 1000;
void doTX(boost::asio::ip::tcp::socket& socket, size_t& TXsize)
{
char rawBuffer[BIG_MESSAGE_SIZE] = { 0 }; // raw data to send
std::string encodedBuffer(rawBuffer, BIG_MESSAGE_SIZE);
for (size_t i = 0; i < BIG_MESSAGE_SIZE; i += BLOCK_SIZE)
{
std::string NewData = encodedBuffer.substr(i, BLOCK_SIZE);
TXsize += boost::asio::write(socket, boost::asio::buffer(NewData, BLOCK_SIZE));
}
}
void doRX(boost::asio::ip::tcp::socket& socket, size_t& RXsize)
{
for (size_t i = 0; i < BIG_MESSAGE_SIZE; i += BLOCK_SIZE)
{
char Buffer[BLOCK_SIZE];
memset(Buffer, 0, BLOCK_SIZE);
RXsize += boost::asio::read(socket, boost::asio::buffer(Buffer, BLOCK_SIZE));
}
}
}
int main(int argc, char * argv[])
{
std::cout << "Running:" << std::endl;
int port = 9876;
boost::asio::io_service ios;
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string("127.0.0.1"), port);
boost::asio::ip::tcp::acceptor acceptor(ios, endpoint);
boost::asio::ip::tcp::socket TXsocket(ios);
boost::asio::ip::tcp::socket RXsocket(ios);
TXsocket.connect(endpoint);
acceptor.accept(RXsocket);
size_t TXsize = 0;
size_t RXsize = 0;
doTX(TXsocket, TXsize);
doRX(RXsocket, RXsize);
std::cout << TXsize << " " << RXsize << std::endl;
return 0;
}

Converting asio read_some to async version

I have the following code that reads from a TCP socket using boost asio read_some function. Currently the code is synchronous and I need to convert it to the async version. The issue is initially that some bytes are read to identify the packettype and to get the length of the packet. Then we have a loop that reads the data. Would I need to use two callbacks to do this asynchronously or can it be done with one ( which would be preferable).
void Transport::OnReadFromTcp()
{
int read = 0;
// read 7 bytes from TCP into mTcpBuffer
m_sslsock->read_some(asio::buffer(mTcpBuffer, 7));
bool tag = true;
for (unsigned char i = 0; i < 5; i++)
{
tag = tag && (mTcpBuffer[i] == g_TcpPacketTag[i]);
}
// get the length from the last two bytes
unsigned short dataLen = (mTcpBuffer[5] ) | (mTcpBuffer[6] << 8);
mBuff = new char[dataLen];
int readTotal = 0;
while (readTotal < dataLen)
{
// read lengths worth of data from tcp pipe into buffer
int readlen = dataLen;
size_t read = m_sslsock->read_some(asio::buffer(&mBuff[readTotal], readlen));
readlen = dataLen - read;
readTotal += read;
}
// Process data .....
}
The first step is to realize that you can remove the read_some loop entireyl using the free function read:
void Transport::OnReadFromTcp() {
int read = 0;
// read 7 bytes from TCP into mTcpBuffer
size_t bytes = asio::read(*m_sslsock, asio::buffer(mTcpBuffer, 7), asio::transfer_all());
assert(bytes == 7);
bool tag = g_TcpPacketTag.end() == std::mismatch(
g_TcpPacketTag.begin(), g_TcpPacketTag.end(),
mTcpBuffer.begin(), mTcpBuffer.end())
.first;
// get the length from the last two bytes
uint16_t const dataLen = mTcpBuffer[5] | (mTcpBuffer[6] << 8);
mBuff.resize(dataLen);
size_t readTotal = asio::read(*m_sslsock, asio::buffer(mBuff), asio::transfer_exactly(dataLen));
assert(mBuff.size() == readTotal);
assert(dataLen == readTotal);
}
That's even regardless of whether execution is asynchronous.
Making it asynchronous is slightly involved, because it requires assumptions about lifetime of the buffers/Transport instance as well as potential multi-threading. I'll provide a sample of that after my morning coffee :)
Demo without threading/lifetime complications:
Live On Coliru
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <array>
#include <cassert>
namespace asio = boost::asio;
namespace ssl = asio::ssl;
namespace {
static std::array<char, 5> g_TcpPacketTag {{'A','B','C','D','E'}};
}
struct Transport {
using tcp = asio::ip::tcp;
using SslSocket = std::shared_ptr<asio::ssl::stream<tcp::socket> >;
Transport(SslSocket s) : m_sslsock(s) { }
void OnReadFromTcp();
void OnHeaderReceived(boost::system::error_code ec, size_t transferred);
void OnContentReceived(boost::system::error_code ec, size_t transferred);
private:
uint16_t datalen() const {
return mTcpBuffer[5] | (mTcpBuffer[6] << 8);
}
SslSocket m_sslsock;
std::array<char, 7> mTcpBuffer;
std::vector<char> mBuff;
};
void Transport::OnReadFromTcp() {
// read 7 bytes from TCP into mTcpBuffer
asio::async_read(*m_sslsock, asio::buffer(mTcpBuffer, 7), asio::transfer_all(),
boost::bind(&Transport::OnHeaderReceived, this, asio::placeholders::error, asio::placeholders::bytes_transferred)
);
}
#include <boost/range/algorithm/mismatch.hpp> // I love sugar
void Transport::OnHeaderReceived(boost::system::error_code ec, size_t bytes) {
if (ec) {
std::cout << "Error: " << ec.message() << "\n";
}
assert(bytes == 7);
bool tag = (g_TcpPacketTag.end() == boost::mismatch(g_TcpPacketTag, mTcpBuffer).first);
if (tag) {
// get the length from the last two bytes
mBuff.resize(datalen());
asio::async_read(*m_sslsock, asio::buffer(mBuff), asio::transfer_exactly(datalen()),
boost::bind(&Transport::OnContentReceived, this, asio::placeholders::error, asio::placeholders::bytes_transferred)
);
} else {
std::cout << "TAG MISMATCH\n"; // TODO handle error
}
}
void Transport::OnContentReceived(boost::system::error_code ec, size_t readTotal) {
assert(mBuff.size() == readTotal);
assert(datalen() == readTotal);
std::cout << "Successfully completed receive of " << datalen() << " bytes\n";
}
int main() {
asio::io_service svc;
using Socket = Transport::SslSocket::element_type;
// connect to localhost:6767 with SSL
ssl::context ctx(ssl::context::sslv23);
auto s = std::make_shared<Socket>(svc, ctx);
s->lowest_layer().connect({ {}, 6767 });
s->handshake(Socket::handshake_type::client);
// do transport
Transport tx(s);
tx.OnReadFromTcp();
svc.run();
// all done
std::cout << "All done\n";
}
When using against a sample server that accepts SSL connections on port 6767:
(printf "ABCDE\x01\x01F"; cat main.cpp) |
openssl s_server -accept 6767 -cert so.crt -pass pass:test
Prints:
Successfully completed receive of 257 bytes
All done

Using select without listen()ing, possible?

I am building a client that:
Should be able to recieve information from both the server and the standart input
Should be able to recieve information from the server without asking, for example when another client sends a message.
To do so I tried using select to monitor both possible inputs.
What happens is that when a keyboard input is monitored I send a message to the client and I expect one back, so there's no problem. But when the server sends an unexpected message nothing happens, and I don't know why. Is using select() the proper way to do so? Is it even possible to use select() without listen()ing?
Here's my code (compileable):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <cstring>
#include <arpa/inet.h>
#include <iostream>
#include <fstream>
#define MAX_CLIENT_NAME 30
#define MAX_TWIT_SIZE 140
#define NUM_OF_ARG 4
#define ERROR -1
#define GREAT_SUCCESS 0
#define OK "OK"
#define EXIT "EXIT"
using std::string;
using std::cerr;
using std::endl;
using std::cout;
string clientName;
int srverfd, numbytes, status, maxSock ;
fd_set inputFdSet; /* Socket file descriptors we want to wake
up for, using select() */
int establishConnection(char * serverAddress,char * port){
if ((srverfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
return ERROR;
}
struct sockaddr_in server;
server.sin_family = AF_INET;
inet_aton(serverAddress, &server.sin_addr);
server.sin_port = htons(atoi(port));
memset(&(server.sin_zero), '\0', 8);
if (connect(srverfd,(const struct sockaddr *)&server,sizeof(struct sockaddr)) == -1) {
perror("connect");
close(srverfd);
return ERROR;
}
maxSock = srverfd;
return GREAT_SUCCESS;
}
const char * getUserTweet(){
string temp;
getline(std::cin,temp);
return temp.c_str();
}
void sendMessage(string message){
if ((numbytes = send(srverfd, message.c_str(), message.length(), 0)) == -1) {
perror("sendMessage");
close(srverfd);
}
cout<<"Message sent: "<< message << endl;
return;
}
const char * getMessage(){
char buf[MAX_TWIT_SIZE];
memset(buf,'\0',MAX_TWIT_SIZE);
if ((numbytes = recv(srverfd, buf, 140, 0)) == -1) {
perror("getMessage");
close(srverfd);
}
string temp = buf;
return temp.c_str();
}
void build_select_list() {
FD_ZERO(&inputFdSet);
FD_SET(srverfd,&inputFdSet);
FD_SET(STDIN_FILENO,&inputFdSet);
if (STDIN_FILENO > maxSock)
maxSock = STDIN_FILENO;
return;
}
void readSocket(fd_set tempfd) {
const char * tweet, * inMessage;
if (FD_ISSET(srverfd,&tempfd)) {
inMessage = getMessage();
cout << inMessage << endl;
}
if (FD_ISSET(STDIN_FILENO,&tempfd)) {
tweet = getUserTweet();
sendMessage(tweet);
inMessage = getMessage();
if (strcmp(inMessage,OK) != 0) {
cout << inMessage << endl;
}
if (strcmp(inMessage,EXIT) == 0) {
return;
}
}
return;
}
int main (int argc, char *argv[] ){
int value;
bool clientON = false;
if(establishConnection(argv[2],argv[3])){
cerr << "usage: failed to make connection" << endl << "exiting..." << endl;
exit(EXIT_FAILURE);
}
cout << "Connected successfully" << endl;
sendMessage("CONNECT "+clientName); //Connect
if(strcmp(getMessage(),OK) == 0){
clientON = true;
}
while(clientON){
build_select_list();
value = select(maxSock, &inputFdSet, NULL, NULL, NULL);
if (value < 0) {
perror("select");
exit(EXIT_FAILURE);
}
if (value == 0) {
continue;
}
else {
readSocket(inputFdSet);
}
}
sendMessage("DISCONNECT");
if(strcmp(getMessage(),OK) == 0){
// do nothing
}
close(srverfd);
return 0;
}
Your select call is invalid. The first parameter must be the highest file descriptor in any of the sets, plus one.
As you have it, an event on srverfd will not "wake up" the select call (unless STDIN_FILENO was somehow less than srverfd, in which case stdin events wouldn't unlock select - but that won't happen in practice).
There are quite a few other problems with your code. (It doesn't really look like C++.)
getUserTweet is unreliable (undefined behavior - temp is destroyed as soon as the function returns, so the char* you return has disappeared by the time its caller will try to use it). Same for getMessage. To remedy that, use std::string everywhere, and only extract the char* when you call into C library functions).
readSocket needlessly copies the FD set (can be expensive).
You should really get rid of all those globals - build one or two classes to encapsulate that state and the networking functions, or something like that.

boost::asio over SocketCAN

I was thinking of making use of Boost Asio to read data from a Socket CAN.
There's nothing fancy going on in linux/can.h , and the device should
behave like the loopback interface, and be used with a raw socket.
Looking at the basic_raw_socket interface it seems that I can make use of
basic_raw_socket::assign to assign the native socket created with
socket( PF_CAN, SOCK_RAW, CAN_RAW );
This is what I have so far
namespace can {
class CanSocket {
public:
typedef boost::asio::ip::basic_endpoint<CanSocket> endpoint;
typedef boost::asio::ip::basic_resolver_query<CanSocket> resolver_query;
typedef boost::asio::ip::basic_resolver_iterator<CanSocket> resolver_iterator;
typedef boost::asio::basic_raw_socket<CanSocket> socket;
typedef boost::asio::ip::basic_resolver<CanSocket> resolver;
CanSocket()
: _protocol( CAN_RAW )
, _family( PF_CAN )
{
}
static CanSocket v4()
{
return CanSocket();
}
static CanSocket v6();
int type() const;
int protocol() const;
int family() const;
friend bool operator==(const CanSocket& p1, const CanSocket& p2)
{
return p1._protocol != p2._protocol || p1._family != p2._family;
}
friend bool operator!=(const CanSocket& p1, const CanSocket& p2)
{
return p1._protocol == p2._protocol || p1._family == p2._family;
}
private:
int _protocol;
int _family;
};
}
And this is how I use it in my application
boost::asio::io_service ioserv;
CanSocket::socket s( ioserv );
int sock = socket( PF_CAN, SOCK_RAW, CAN_RAW );
s.assign(CanSocket::v4(), sock);
struct ifreq ifr;
strcpy(ifr.ifr_name, "vcan0");
ioctl(sock, SIOCGIFINDEX, &ifr); /* ifr.ifr_ifindex gets filled
* with that device's index */
/* Select that CAN interface, and bind the socket to it. */
/* this should be the endpoint */
struct sockaddr_can addr;
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
/* s.bind (....) */
bind( sock, (struct sockaddr*)&addr, sizeof(addr) );
What I don't quite get is how do I bind s to the local endpoint? There are no IPs or ports involved.
Is there anything else that should be implemented besides the endpoint to get it going?
Here is working example, assembled with help of this thread
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <iostream>
#include <net/if.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
void data_send(void) {
std::cout << "omg sent" << std::endl;
}
void data_rec(struct can_frame &rec_frame,
boost::asio::posix::basic_stream_descriptor<> &stream) {
std::cout << std::hex << rec_frame.can_id << " ";
for (int i = 0; i < rec_frame.can_dlc; i++) {
std::cout << std::hex << int(rec_frame.data[i]) << " ";
}
std::cout << std::dec << std::endl;
stream.async_read_some(
boost::asio::buffer(&rec_frame, sizeof(rec_frame)),
boost::bind(data_rec, boost::ref(rec_frame), boost::ref(stream)));
}
int main(void) {
struct sockaddr_can addr;
struct can_frame frame;
struct can_frame rec_frame;
struct ifreq ifr;
int natsock = socket(PF_CAN, SOCK_RAW, CAN_RAW);
strcpy(ifr.ifr_name, "vcan0");
ioctl(natsock, SIOCGIFINDEX, &ifr);
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
if (bind(natsock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("Error in socket bind");
return -2;
}
frame.can_id = 0x123;
frame.can_dlc = 2;
frame.data[0] = 0x11;
frame.data[1] = 0x23;
boost::asio::io_service ios;
boost::asio::posix::basic_stream_descriptor<> stream(ios);
stream.assign(natsock);
stream.async_write_some(boost::asio::buffer(&frame, sizeof(frame)),
boost::bind(data_send));
stream.async_read_some(
boost::asio::buffer(&rec_frame, sizeof(rec_frame)),
boost::bind(data_rec, boost::ref(rec_frame), boost::ref(stream)));
ios.run();
}
The solution is to use posix::stream_descriptor.
Just open the native socket, bind and then use posix::basic_stream_descriptor::assign.