Using select without listen()ing, possible? - c++

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.

Related

Why does read() block indefinitely when reading a buffer

I'm new to socket programming and wanted to try something simple. This program can manipulate settings on my tv. All messages are 24 bytes. There may be one or more messages returned. I cannot figure out a good solution to get all of the messages without read() blocking on me.
What is below would be what I hoped to be a simple solution. It seems to work in a lot of example code I have found. However, what happens is after the first loop it seems to just block on the read() operation infinitely. If I remove the loop and just put multiple reads, the same thing happens. As long as I don't try to read more information that is sent, I'm ok.
I did try a couple of other things like turning off blocking, and adding a timer. neither worked. At this point I can live with a couple seconds of blocking. I just want the program to exit normally after the read.
adding output for a power_on command. It correctly outputs the two lines it should then blocks indefinitely.
Dans-MBP:~ mreff555$ ./tvthing
24: *SAPOWR0000000000000000
24: *SNPOWR0000000000000001
code below:
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <cstring>
#include <sys/time.h>
#define PORT 20060
#define POWER_ON "*SCPOWR0000000000000001\n"
#define POWER_OFF "*SCPOWR0000000000000000\n"
#define POWER_STATUS "*SEPOWR################\n"
#define POWER_TOGGLE "*STPOWR################\n"
int main(int argc, char const * argv[])
{
struct sockaddr_in tvAddress;
struct hostent *host = gethostbyname("192.168.1.128");
memset(&tvAddress, 0, sizeof(tvAddress));
tvAddress.sin_family = AF_INET;
tvAddress.sin_addr.s_addr = htonl(INADDR_ANY);
tvAddress.sin_addr.s_addr = ((struct in_addr*)(host->h_addr))->s_addr;
tvAddress.sin_port = htons(PORT);
char sendBuffer[24] = {0};
char recBuffer[24] = {0};
int socket_fd;
if((socket_fd = socket(AF_INET,SOCK_STREAM, 0)) < 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
else
{
if(connect(socket_fd, (struct sockaddr *)&tvAddress, sizeof(struct sockaddr)))
{
perror("connection failed failed");
exit(EXIT_FAILURE);
}
memcpy(&sendBuffer, &POWER_STATUS, sizeof(sendBuffer));
write(socket_fd, sendBuffer, strlen(sendBuffer));
int ret;
while((ret = read(socket_fd, recBuffer, sizeof(recBuffer)) > 0))
{
printf("%d: %s\n", ret, recBuffer);
}
close(socket_fd);
}
}
You need to read until your buffer is full like this:
unsigned readLen = 0;
unsigned totalLen = sizeof(recBuffer);
while (readLen < totalLen) {
int ret = read(socket_fd, recBuffer + readLen, totalLen - readLen);
if (ret > 0) {
readLen += ret;
} else {
// error handling here
break;
}
}
This is needed because read() returns only the currently available amount of bytes which might be less than you have requested. From the corresponding man-page:
RETURN VALUE
On success, the number of bytes read is returned (zero indicates end of file), and the file position is advanced by this number. It is not an error if this number is smaller than the number of bytes requested; this may happen for example because fewer bytes are actually available right now (maybe because we were close to end-of-file, or because we are reading from a pipe, or from a terminal), or because read() was interrupted by a signal.
If you need to receive several responses you can put the described algorithm into a function and use it repeatedly. In any case you need to know how many responses to expect otherwise your read() will block because it seems that your TV's server is programmed to keep the connection open and it is client's responsibility to choose when to disconnect.
If you decide to make your application more sophisticated you can use one of the IO Multiplexing mechanisms to make your wait for response interruptable by timer or terminal input. For example:
while (true) {
pollfd fds[] = {
{ socket_fd, POLLIN, 0 },
{ STDIN_FILENO, POLLIN, 0 }
};
int ret = poll(fds, sizeof(fds) / sizeof(*fds), -1);
if (ret > 0) {
if (fds[0].revents & POLLIN) {
readResponse(); // read and process response
}
if (fds[1].revents & POLLIN) {
break; // exit on terminal input
}
}
}
As it turns out, select is designed exactly for that purpose. It checks the specified file descriptors for a specified time interval, and if successful repeats the process. Tweaking the time interval minimizes the blocking while allowing enough time for additional messages to come in.
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <cstring>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/select.h>
#define PORT 20060
#define POWER_ON "*SCPOWR0000000000000001\n"
#define POWER_OFF "*SCPOWR0000000000000000\n"
#define POWER_STATUS "*SEPOWR################\n"
#define POWER_TOGGLE "*STPOWR################\n"
int main(int argc, char const * argv[])
{
struct sockaddr_in tvAddress;
struct hostent *host = gethostbyname("192.168.1.128");
memset(&tvAddress, 0, sizeof(tvAddress));
tvAddress.sin_family = AF_INET;
tvAddress.sin_addr.s_addr = htonl(INADDR_ANY);
tvAddress.sin_addr.s_addr = ((struct in_addr*)(host->h_addr))->s_addr;
tvAddress.sin_port = htons(PORT);
char sendBuffer[24] = {0};
char recBuffer[24] = {0};
int socket_fd;
if((socket_fd = socket(AF_INET,SOCK_STREAM, 0)) < 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
else
{
if(connect(socket_fd, (struct sockaddr *)&tvAddress, sizeof(struct sockaddr)))
{
perror("connection failed failed");
exit(EXIT_FAILURE);
}
struct timeval tv;
fd_set sockRead;
int selectStatus;
memcpy(&sendBuffer, &POWER_ON, sizeof(sendBuffer));
write(socket_fd, sendBuffer, strlen(sendBuffer));
do
{
FD_ZERO(&sockRead);
FD_SET(socket_fd, &sockRead);
tv.tv_sec = 2;
tv.tv_usec = 500000;
selectStatus = select(socket_fd + 1, &sockRead, NULL, NULL, &tv);
switch(selectStatus)
{
case -1:
perror("select()");
exit(EXIT_FAILURE);
break;
case 0:
break;
default:
printf("Ready for Reading\n");
read(socket_fd, recBuffer, sizeof(recBuffer));
printf("%s\n", recBuffer);
}
}while (selectStatus > 0);
close(socket_fd);
}
}

Producer/Consumer High performance UDP packet capture/sniffing in 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;
}

socket recv read no message

The following code is an application supposed to be communicating between two applications. In one exe (A) user type a message and the message is printed in the other exe(B).
The flow of the program:
Both exe calls connectTo so they are ready to send and receive messages between.
User type a message to be send in A console window, which calls sendMsg in A exe. When message is received in B, the message is printed in B console window.
The problem is that the message received is sometimes empty. When I enter 1234 ten times in A, ten messages are printed in B with only 5-6 of them are 1234 and the rest are empty. The situation is the same from B to A.
++++++++++++++++++++++++++
whole program
++++++++++++++++++++++++++
Header.h
#pragma once
#include <WinSock2.h>
#include <Windows.h>
#include <mutex>
#include <thread>
class CommuWin
{
private:
std::mutex m_accessMutexSend;
std::mutex m_accessMutexReceive;
std::thread m_sendThread;
std::thread m_receiveThread;
bool m_IsSendReady = false;
bool m_IsRecvReady = false;
SOCKET m_outSocket;
SOCKADDR_IN m_outAddr;
SOCKET m_inSocket;
SOCKADDR_IN m_inAddr;
public:
CommuWin(int InPort, int OutPort);
~CommuWin();
int connectTo();
int sendMsg(const char* message);
int StartReceiveMsg();
bool GetRecvStatus();
bool GetSendStatus();
private:
void SetRecvStatus(bool ready);
void SetSendStatus(bool ready);
int SetupRecvEnd();
int SetupSendEnd();
int sendMsgTo(const char* message);
int ReceiveMsgFrom();
};
Source.cpp
#pragma comment(lib, "Ws2_32.lib")
#include "Header.h"
#define OKAY (1)
#define ERROR (-1)
#define MAX_MSG_SIZE (200)
class NetworkServices
{
public:
static int sendMessage(SOCKET curSocket, const char* message, int messageSize);
static int receiveMessage(SOCKET curSocket, char * buffer, int bufSize);
};
int NetworkServices::sendMessage(SOCKET curSocket, const char* message, int messageSize)
{
return send(curSocket, message, messageSize, 0);
}
int NetworkServices::receiveMessage(SOCKET curSocket, char * buffer, int bufSize)
{
return recv(curSocket, buffer, bufSize, 0);
}
CommuWin::CommuWin(int InPort, int OutPort)
{
WSAData wsaData;
WORD DLLVersion;
DLLVersion = MAKEWORD(2, 1);
int r = WSAStartup(DLLVersion, &wsaData);
///////////////////////////////////////////////////////////////////////
m_outSocket = socket(AF_INET, SOCK_STREAM, NULL);
m_outAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
m_outAddr.sin_family = AF_INET;
m_outAddr.sin_port = htons(OutPort);
m_inSocket = socket(AF_INET, SOCK_STREAM, NULL);
m_inAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
m_inAddr.sin_family = AF_INET;
m_inAddr.sin_port = htons(InPort);
}
CommuWin::~CommuWin()
{
}
int CommuWin::connectTo()
{
printf("connect to");
printf("\n");
m_sendThread = std::thread(
&CommuWin::SetupSendEnd,
this);
m_receiveThread = std::thread(
&CommuWin::SetupRecvEnd,
this);
return OKAY;
}
int CommuWin::SetupSendEnd()
{
SOCKET sListen;
sListen = socket(AF_INET, SOCK_STREAM, NULL);
bind(sListen, (SOCKADDR*)&m_outAddr, sizeof(m_outAddr));
listen(sListen, SOMAXCONN);
m_outSocket = accept(sListen, NULL, NULL);
if (m_outSocket != INVALID_SOCKET)
{
SetSendStatus(true);
printf("accepted\n");
}
return OKAY;
}
int CommuWin::SetupRecvEnd()
{
int connectSucceed = 0;
do
{
Sleep(1000);
connectSucceed = connect(m_inSocket, (SOCKADDR*)&m_inAddr, sizeof(m_inAddr));
} while (connectSucceed == SOCKET_ERROR);
SetRecvStatus(true);
printf("connected\n");
return OKAY;
}
int CommuWin::sendMsg(const char* message)
{
if (GetSendStatus())
{
m_sendThread.detach();
m_sendThread = std::thread(
&CommuWin::sendMsgTo,
this,
message);
}
return OKAY;
}
int CommuWin::sendMsgTo(const char* message)
{
NetworkServices::sendMessage(m_outSocket, message, (int)strlen(message));
return OKAY;
}
int CommuWin::StartReceiveMsg()
{
if (GetRecvStatus())
{
m_receiveThread.detach();
m_receiveThread = std::thread(
&CommuWin::ReceiveMsgFrom,
this);
}
return OKAY;
}
int CommuWin::ReceiveMsgFrom()
{
while (true)
{
char message[MAX_MSG_SIZE];
ZeroMemory(message, MAX_MSG_SIZE);
NetworkServices::receiveMessage(m_inSocket, message, sizeof(message));
printf(message);
printf("\n");
}
return OKAY;
}
void CommuWin::SetRecvStatus(bool ready)
{
std::lock_guard<std::mutex> lock(m_accessMutexReceive);
m_IsRecvReady = ready;
}
void CommuWin::SetSendStatus(bool ready)
{
std::lock_guard<std::mutex> lock(m_accessMutexSend);
m_IsSendReady = ready;
}
bool CommuWin::GetRecvStatus()
{
std::lock_guard<std::mutex> lock(m_accessMutexReceive);
return m_IsRecvReady;
}
bool CommuWin::GetSendStatus()
{
std::lock_guard<std::mutex> lock(m_accessMutexSend);
return m_IsSendReady;
}
main.cpp
#include "stdafx.h"
#include "Header.h"
#include <iostream>
#include <string>
int main(int argc, char *argv[])
{
std::cout << argc <<std::endl;
int Inport = std::stoi(argv[1]);
int Outport = std::stoi(argv[2]);
//std::cout << "inport = " << argv[1] << " outport = " << argv[2] << std::endl;
std::cout << "inport = " << Inport << " outport = " << Outport << std::endl;
CommuWin com(Inport, Outport);
com.connectTo();
while (true)
{
if (com.GetSendStatus() && com.GetRecvStatus())
{
com.StartReceiveMsg();
break;
}
}
while (true)
{
std::cout << "Enter Send Message" << std::endl;
std::string msg;
std::cin >> msg;
com.sendMsg(msg.c_str());
}
return 0;
}
There are multiple issues with your code. First of all, you need to check results of all the functions, including, but not limited to, to sListen, bind, listen, recv.
NetworkServices::receiveMessage(m_inSocket, message, sizeof(message));
// Without checking recv result there is no way to guess how much
// bytes are actually stored in `message`, if any. Also boldly assuming
// that `message` is null terminated and represents a proper format string
// is dangerous.
printf(message);
You also need to carefully initialize all the stuff, especially sockaddr structures, which may get potentially partially initialized in this case. You are using multiple threads but perform insufficient synchronization. Method sendMsgTo(const char* message) executed by (potentially detached) background thread receives a pointer to a string buffer that may get invalidated at any time.
If you want to send and receive messages, you have to write some code to do that. Nowhere is there any code to send or receive messages. If you think there is, point specifically to the code that figures out whether or not the data you received is one or more messages. You cannot do it.
TCP is not a message protocol. If you need a message protocol on top of TCP, you have to implement one. Have a look at protocols that do this such as HTTP, IRC, or FTP so see how it's done.
If you log the number of bytes received, you will see that all the data you sent was received. It's your job to split that data into messages if you need to -- it won't happen by itself.

Possible to retrieve input from user and running another process?

Is it possible to use getline(cin,buffer); at the top of my program, then have a "animated menu" still running below it?
For example (very basic):
#include <iostream>
#include <string>
using namespace std;
int stringLen=0;
string buffer;
getline(cin, buffer);
for (int i = 0; i < kMaxWait;i++)
{
printf("counter waiting for user input %d",i);
if (1 >= buffer.length())
break;
}
Would I have to fork that loop somehow so it would keep counting and display the counter until the user enters something??
One possible answer, given in the comments, is to use threads. But it's not necessary, there's a way to do this without threads.
Make stdin a non-blocking file descriptor.
Wait for stdin to become readable, via poll()/select(), in the meantime do your animation, etc...
Make stdin a blocking file descriptor, again.
Use std::getline().
There are also some ancillary issues to consider, such as the buffering that comes from std::streambuf, so before doing all that, check if there's already something to read from std::cin, first.
This is something I used sometime ago. It's quite rudimentary, but you can get the gist of the process - using poll. It returns true if there is input, and puts it in str, false otherwise. So, you can put this in your loop somewhere, and take action when there is input.
bool polled_input(std::string& str)
{
struct pollfd fd_user_in;
fd_user_in.fd = STDIN_FILENO;
fd_user_in.events = POLLIN;
fd_user_in.revents = 0;
int rv = poll(&fd_user_in, 1, 0);
if (rv == -1) {/* error */}
else if (rv == 0) return false;
else if (fd_user_in.revents & POLLIN)
{
char buffer[MAX_BUFF_SIZE];
int rc = read(STDIN_FILENO, buffer, MAX_BUFF_SIZE-1);
if (rc >= 0)
{
buffer[rc]='\0';
str = std::string(buffer);
return true;
}
else {/* error */}
}
else {/* error */}
}
select is meant for this, multiplexed, blocking I/O. It can be done without a poll I think:
#include <iostream>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char **arg)
{
const int time_in_secs = 10;
const int buffer_size = 1024;
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(STDIN_FILENO, &readfds);
struct timeval tv;
tv.tv_sec = time_in_secs;
tv.tv_usec = 0;
int ret = select(STDIN_FILENO + 1, &readfds, NULL, NULL, &tv);
if (!ret)
{
std::cout << "Timeout\n";
exit(1);
}
char buf[buffer_size];
if (FD_ISSET(STDIN_FILENO, &readfds))
{
int len = read(STDIN_FILENO, buf, buffer_size);
buf[len] = '\0';
}
std::cout << "You typed: " << buf << "\n";
return 0;
}

C++ constructor not being called [duplicate]

This question already has answers here:
Default constructor with empty brackets
(9 answers)
Closed 4 years ago.
I'm new to C++ and this is my first time with its classes and I was wondering, how do I call a constructor? I've read some documentation on classes in C++ and that's how I came up with what I have. The constructor calls private methods to setup the server.
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sstream>
#include <string>
#include "SimpleIni.h"
#include "MySQL.cpp"
#include <thread>
class LoginServer {
int resSocket;
MySQL mysql;
struct sockaddr_in strctAddr;
private:
void log(std::string strText, std::string strType = "INFO"){
time_t rawtime;
struct tm * timeinfo;
char buffer[50];
time(&rawtime);
timeinfo = localtime(&rawtime);
strftime(buffer, 50, "%c",timeinfo);
std::cout << "[" << buffer << "][" << strType << "] > " << strText << std::endl;
}
void error(std::string strError){
log(strError, "ERROR");
exit(1);
}
int setup(int intPort){
std::stringstream objStringStream;
objStringStream << intPort;
log("Initializing socket server");
resSocket = socket(AF_INET, SOCK_STREAM, 0);
if(resSocket < 0) error("Could not create socket.");
bzero((char *) &strctAddr, sizeof(strctAddr));
strctAddr.sin_family = AF_INET;
strctAddr.sin_addr.s_addr = INADDR_ANY;
strctAddr.sin_port = htons(intPort);
setsockopt(resSocket, SOL_SOCKET, SO_REUSEADDR, (struct sockaddr *) &strctAddr, sizeof(strctAddr));
if(bind(resSocket, (struct sockaddr *) &strctAddr, sizeof(strctAddr)) < 0)
error("Could not bind");
listen(resSocket, 5);
log("Listening for clients on " + objStringStream.str(), "FINE");
return 1;
}
int sendPacket(int resSock, std::string strData){
int intWrite;
char chBuffer[8192];
strcpy(chBuffer, strData.c_str());
log("Sending packet: " + strData, "SEND");
intWrite = write(resSock, chBuffer, strlen(chBuffer) + 1);
return intWrite;
}
std::string RandomString(int len){
srand(time(0));
std::string str = "`~!##$%^&*()-=_+[]\{]|;:'\",<.>/?0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
int pos;
int size = str.size();
while(size != len) {
pos = ((rand() % (str.size() - 1)));
str.erase (pos, 1);
}
return str;
}
void handleData(int resSock, char* strData){
char * chData;
chData = strtok(strData, "\0");
while(chData != NULL){
std::string strPacket = chData;
log("Received data: " + std::string(strPacket), "RECV");
if(strPacket.compare("<policy-file-request/>") == 0){
log("Policy request received");
sendPacket(resSock, "<cross-domain-policy><allow-access-from domain='*' to-ports='6112'/></cross-domain-policy>");
} else if(strPacket.compare("<msg t='sys'><body action='verChk' r='0'><ver v='153' /></body></msg>") == 0){
log("Version check received");
sendPacket(resSock, "<msg t='sys'><body action='apiOK' r='0'></body></msg>");
}
chData = strtok(NULL, "\0");
}
}
void handleClient(int resSock){
char chBuffer[6486];
int intRead;
for(;;){
bzero(chBuffer, 6486);
intRead = read(resSock, chBuffer, 6486);
if(chBuffer == NULL) continue;
if(intRead <= 0){
log("Client disconnected");
close(resSock);
break;
} else {
handleData(resSock, chBuffer);
}
}
}
void listenToClients(){
for(;;){
std::stringstream objStringStream;
struct sockaddr_in clntAddr;
socklen_t intClients = sizeof(clntAddr);
int resClient = accept(resSocket, (struct sockaddr *) &clntAddr, &intClients);
if(resClient < 0) log("Failed to accept client", "ERROR");
setsockopt(resClient, SOL_SOCKET, SO_REUSEADDR, (struct sockaddr *) &clntAddr, sizeof(clntAddr));
char floatIP[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &clntAddr.sin_addr, floatIP, sizeof floatIP);
objStringStream << floatIP;
log("New client connected (IP: " + objStringStream.str() + ")");
std::thread objThread(&LoginServer::handleClient, this, resClient);
objThread.detach();
}
}
public:
LoginServer();
};
LoginServer::LoginServer(){
CSimpleIniA objIniParser;
objIniParser.LoadFile("Settings.conf");
#define Host objIniParser.GetValue("Database", "Host", NULL)
#define User objIniParser.GetValue("Database", "User", NULL)
#define Pass objIniParser.GetValue("Database", "Pass", NULL)
#define Name objIniParser.GetValue("Database", "Name", NULL)
if(!mysql.connect(Host, User, Pass, Name)) error("Could not establish database connection.");
setup(6112);
listenToClients();
}
int main(){
LoginServer objLoginServer();
return 0;
}
Due to the rules of parsing in C++:
LoginServer objLoginServer();
doesn't declare an object of type LoginServer. In fact is declares a function that takes no parameters and returns a LoginServer object by value.
Instead you want to say:
LoginServer objLoginServer;
Try removing the parentheses:
LoginServer objLoginServer;
If you are curious of what's going on, search for the "most vexing parse".
The constructor should be called everytime you instantiate an object, such as the line LoginServer objLoginServer; (hint: try w/o the parenthesis) or LoginServer *objLoginServer = new LoginServer();, of course remember to call delete objLoginServer; when done with it.
There are multiple ways of calling the constructor, but I guess your specific problem is that your put brackets when calling default constructor, you need to omit them: LoginServer objLoginServer;
Such problem happens because compiler isn't able to distingush between declaring function prototype and calling default constructor. Look at A B(), out of context it may be creating object with name B of type A using default constructor, or function B returning an instance of type A.
http://www.cplusplus.com/doc/tutorial/classes/
Reading this is good start. Best of luck.
Important: Notice how if we declare a new object and we want to use its default constructor (the one without parameters), we do not include parentheses ():
CRectangle rectb; // right
CRectangle rectb(); // wrong!