Related
I have written program to work like a telephone operator for TCP clients.
A TCP client connects to the server, and then gives a channel name to the server.
If another TCP client connect to the server and give the same channel name, the server connects the 2 TCP clients together, allowing them to talk to each other without interference.
When I compile this on Mac, it works fine, but when I put it on a digital ocean droplet, I get this error during compilation. I have generated the makefile using netbeans.
This is StreamSwitch.h
#ifndef STREAMSWITCH_H
#define STREAMSWITCH_H
#include <string>
#include <sstream>
#include <iostream>
#include <list>
using namespace std;
struct Comparator{
int length;
uint8_t *pointer;
};
class DiscriptorNode{
public:
int FileDiscriptor;
uint8_t ChannelName[1000];
bool operator == (const DiscriptorNode& s) const { return (this->FileDiscriptor) == (s.FileDiscriptor); }
bool operator == (const Comparator& s) const { return memcmp(s.pointer,this->ChannelName,s.length)==0; }
bool operator != (const DiscriptorNode& s) const { return !operator==(s); }
};
class StreamConnection{
public:
int FileDiscriptors[2];
bool operator == (const StreamConnection& s) const { return this->FileDiscriptors == s.FileDiscriptors; }
bool operator != (const StreamConnection& s) const { return !operator==(s); }
};
class StreamSwitch{
list <DiscriptorNode> Unconnected;
list <StreamConnection> Connected;
int ServerFileDiscriptor;
int PortNumber;
string ErrorMessage;
public:
StreamSwitch(int portNumber);
void HandleCommunication();
bool Begin();
string GetError();
};
#endif /* STREAMSWITCH_H */
This is StreamSwitch.cpp
#include "StreamSwitch.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <cstdlib>
#include <iostream>
#include <sys/select.h>
#include <unistd.h>
void remove(list<DiscriptorNode> List, list<DiscriptorNode>:: iterator &Iterator){
list<DiscriptorNode> ::iterator temp=Iterator;
Iterator++;
List.erase(temp);
}
void remove(list<StreamConnection> List, list<StreamConnection>:: iterator &Iterator){
list<StreamConnection> ::iterator temp=Iterator;
Iterator++;
List.erase(temp);
}
StreamSwitch::StreamSwitch(int portNumber) {
this->PortNumber=portNumber;
}
bool StreamSwitch::Begin(){
int ReUseSocket=1;
struct sockaddr_in ServerAddress;
this->ServerFileDiscriptor=socket(AF_INET, SOCK_STREAM, 0);
bzero((char *) &ServerAddress, sizeof (ServerAddress));
ServerAddress.sin_family = AF_INET;
ServerAddress.sin_port = htons(PortNumber);
ServerAddress.sin_addr.s_addr = INADDR_ANY;
if (setsockopt(this->ServerFileDiscriptor, SOL_SOCKET, SO_REUSEADDR, &ReUseSocket, sizeof (ReUseSocket)) == 1) {
return 1;
}
if (bind(this->ServerFileDiscriptor, (struct sockaddr *) &ServerAddress, sizeof (ServerAddress)) < 0) {
std::stringstream ss;
ss<<"Could not bind, port "<<this->PortNumber<<" is being used";
this->ErrorMessage=ss.str();
return false;
}
listen(this->ServerFileDiscriptor, 1024);
return true;
}
uint8_t InputDataBuffer[1000];
void StreamSwitch::HandleCommunication(){
int maximumFd=0;
struct timeval Timer_Variable;
list<DiscriptorNode> ::iterator DiscIterator;
list<StreamConnection> ::iterator ConnectionIterator;
fd_set ReadFileDiscriptors, ExceptFileDiscriptors, WriteFileDiscriptors;
Timer_Variable.tv_sec = 0;
Timer_Variable.tv_usec = 100;
FD_ZERO(&ReadFileDiscriptors);
FD_ZERO(&WriteFileDiscriptors);
FD_ZERO(&ExceptFileDiscriptors);
FD_SET(this->ServerFileDiscriptor, &ReadFileDiscriptors);
FD_SET(this->ServerFileDiscriptor, &WriteFileDiscriptors);
FD_SET(this->ServerFileDiscriptor, &ExceptFileDiscriptors);
if (maximumFd<this->ServerFileDiscriptor) {
maximumFd = this->ServerFileDiscriptor;
}
for (DiscIterator = this->Unconnected.begin(); DiscIterator != this->Unconnected.end(); DiscIterator++) {
FD_SET((*DiscIterator).FileDiscriptor, &ReadFileDiscriptors);
FD_SET((*DiscIterator).FileDiscriptor, &WriteFileDiscriptors);
FD_SET((*DiscIterator).FileDiscriptor, &ExceptFileDiscriptors);
if (maximumFd < (*DiscIterator).FileDiscriptor) {
maximumFd = (*DiscIterator).FileDiscriptor;
}
}
for (ConnectionIterator = this->Connected.begin(); ConnectionIterator != this->Connected.end(); ConnectionIterator++) {
for (int i = 0; i < 2; i++) {
FD_SET((*ConnectionIterator).FileDiscriptors[i], &ReadFileDiscriptors);
FD_SET((*ConnectionIterator).FileDiscriptors[i], &WriteFileDiscriptors);
FD_SET((*ConnectionIterator).FileDiscriptors[i], &ExceptFileDiscriptors);
if (maximumFd < (*ConnectionIterator).FileDiscriptors[i]) {
maximumFd = (*ConnectionIterator).FileDiscriptors[i];
}
}
}
maximumFd++;
select(maximumFd,&ReadFileDiscriptors,&WriteFileDiscriptors,&ExceptFileDiscriptors,&Timer_Variable);
if(FD_ISSET(this->ServerFileDiscriptor,&ReadFileDiscriptors)){
struct sockaddr_in ClientAddress;
DiscriptorNode node;
socklen_t clientLength=sizeof(ClientAddress);
node.FileDiscriptor=accept(this->ServerFileDiscriptor, (struct sockaddr *) &ClientAddress, &clientLength);
cout<<"Got a new connection"<<endl;
bzero(node.ChannelName,sizeof(node.ChannelName));
this->Unconnected.push_back(node);
}
for (DiscIterator = this->Unconnected.begin(); DiscIterator != this->Unconnected.end(); DiscIterator++) {
if(FD_ISSET((*DiscIterator).FileDiscriptor,&ExceptFileDiscriptors)){
close((*DiscIterator).FileDiscriptor);
remove(this->Unconnected,DiscIterator);
continue;
}
int errorsoc = 0;
socklen_t len = sizeof (errorsoc);
int retval = getsockopt((*DiscIterator).FileDiscriptor
, SOL_SOCKET
, SO_ERROR
, &errorsoc
, &len);
if ((retval != 0) || (errorsoc != 0)) {
close((*DiscIterator).FileDiscriptor);
remove(this->Unconnected,DiscIterator);
continue;
}
if(FD_ISSET((*DiscIterator).FileDiscriptor,&ReadFileDiscriptors)){
int TransferCount;
bzero(InputDataBuffer,sizeof(InputDataBuffer));
TransferCount=read((*DiscIterator).FileDiscriptor, InputDataBuffer, sizeof(InputDataBuffer));
if (TransferCount < 1) {
close((*DiscIterator).FileDiscriptor);
remove(this->Unconnected,DiscIterator);
continue;
}
Comparator s;
cout<<"Got this data "<<InputDataBuffer<<endl;
s.pointer=InputDataBuffer;
s.length=TransferCount;
list<DiscriptorNode>::iterator matching=std::find(this->Unconnected.begin(),this->Unconnected.end(),s);
if(matching==this->Unconnected.end()){
cout<<"Did not find matching"<<endl;
memcpy((*DiscIterator).ChannelName,InputDataBuffer,TransferCount);
continue;
}
cout<<"Matched "<<InputDataBuffer<<endl;
StreamConnection connect;
connect.FileDiscriptors[0]=(*matching).FileDiscriptor;
connect.FileDiscriptors[1]=(*DiscIterator).FileDiscriptor;
remove(this->Unconnected,matching);
remove(this->Unconnected,DiscIterator);
this->Connected.push_back(connect);
}
}
for (ConnectionIterator = this->Connected.begin(); ConnectionIterator != this->Connected.end(); ConnectionIterator++) {
for (int i = 0; i < 2; i++) {
if (FD_ISSET((*ConnectionIterator).FileDiscriptors[i], &ExceptFileDiscriptors)) {
close((*ConnectionIterator).FileDiscriptors[i]);
close((*ConnectionIterator).FileDiscriptors[1-i]);
remove(this->Connected,ConnectionIterator);
break;
}
int errorsoc = 0;
socklen_t len = sizeof (errorsoc);
int retval = getsockopt((*ConnectionIterator).FileDiscriptors[i]
, SOL_SOCKET
, SO_ERROR
, &errorsoc
, &len);
if ((retval != 0) || (errorsoc != 0)) {
close((*ConnectionIterator).FileDiscriptors[i]);
close((*ConnectionIterator).FileDiscriptors[1-i]);
remove(this->Connected,ConnectionIterator);
break;
}
if (FD_ISSET((*ConnectionIterator).FileDiscriptors[i], &ReadFileDiscriptors)) {
int TransferCount;
bzero(InputDataBuffer, sizeof (InputDataBuffer));
TransferCount = read((*ConnectionIterator).FileDiscriptors[i], InputDataBuffer, sizeof (InputDataBuffer));
if (TransferCount < 1) {
close((*ConnectionIterator).FileDiscriptors[i]);
close((*ConnectionIterator).FileDiscriptors[1 - i]);
remove(this->Connected,ConnectionIterator);
break;
}
// cout<<"This is the data received "<<InputDataBuffer<<endl;
write((*ConnectionIterator).FileDiscriptors[1-i],InputDataBuffer,TransferCount);
}
}
}
}
string StreamSwitch::GetError(){
return this->ErrorMessage;
}
And this is my main
#include <list>
#include <cstdlib>
#include <iostream>
#include <unistd.h>
#include "StreamSwitch.h"
using namespace std;
/*
*
*/
int main(int argc, char** argv) {
StreamSwitch switcher(1234);
bool success=switcher.Begin();
if(!success){
cout<<switcher.GetError();
return 0;
}
while(1){
switcher.HandleCommunication();
usleep(1000);
}
return 0;
}
I have checked my make file and confirmed that it runs the following commands for compilation
g++ -c -g -MMD -MP -MF "build/Debug/GNU-MacOSX/StreamSwitch.o.d" -o build/Debug/GNU-MacOSX/StreamSwitch.o StreamSwitch.cpp
mkdir -p dist/Debug/GNU-MacOSX
g++ -o dist/Debug/GNU-MacOSX/streamswitch build/Debug/GNU-MacOSX/StreamSwitch.o build/Debug/GNU-MacOSX/main.o
This is the error I get on digital ocean droplet
g++ -c -g -MMD -MP -MF "build/Debug/GNU-MacOSX/StreamSwitch.o.d" -o build/Debug/GNU-MacOSX/StreamSwitch.o StreamSwitch.cpp
In file included from StreamSwitch.cpp:7:0:
StreamSwitch.h:23:5: error: ‘uint8_t’ does not name a type
uint8_t *pointer;
^
StreamSwitch.h:28:5: error: ‘uint8_t’ does not name a type
uint8_t ChannelName[1000];
^
StreamSwitch.h: In member function ‘bool DiscriptorNode::operator==(const Comparator&) const’:
StreamSwitch.h:30:68: error: ‘const struct Comparator’ has no member named ‘pointer’
bool operator == (const Comparator& s) const { return memcmp(s.pointer,this->ChannelName,s.length)==0; }
^
StreamSwitch.h:30:82: error: ‘const class DiscriptorNode’ has no member named ‘ChannelName’
bool operator == (const Comparator& s) const { return memcmp(s.pointer,this->ChannelName,s.length)==0; }
^
StreamSwitch.h:30:102: error: ‘memcmp’ was not declared in this scope
bool operator == (const Comparator& s) const { return memcmp(s.pointer,this->ChannelName,s.length)==0; }
^
StreamSwitch.cpp: In member function ‘bool StreamSwitch::Begin()’:
StreamSwitch.cpp:34:58: error: ‘bzero’ was not declared in this scope
bzero((char *) &ServerAddress, sizeof (ServerAddress));
^
StreamSwitch.cpp: In member function ‘void StreamSwitch::HandleCommunication()’:
StreamSwitch.cpp:95:20: error: ‘class DiscriptorNode’ has no member named ‘ChannelName’
bzero(node.ChannelName,sizeof(node.ChannelName));
^
StreamSwitch.cpp:95:44: error: ‘class DiscriptorNode’ has no member named ‘ChannelName’
bzero(node.ChannelName,sizeof(node.ChannelName));
^
StreamSwitch.cpp:95:56: error: ‘bzero’ was not declared in this scope
bzero(node.ChannelName,sizeof(node.ChannelName));
^
StreamSwitch.cpp:119:58: error: ‘bzero’ was not declared in this scope
bzero(InputDataBuffer,sizeof(InputDataBuffer));
^
StreamSwitch.cpp:128:15: error: ‘struct Comparator’ has no member named ‘pointer’
s.pointer=InputDataBuffer;
^
StreamSwitch.cpp:130:114: error: no matching function for call to ‘find(std::list<DiscriptorNode>::iterator, std::list<DiscriptorNode>::iterator, Comparator&)’
list<DiscriptorNode>::iterator matching=std::find(this->Unconnected.begin(),this->Unconnected.end(),s);
^
StreamSwitch.cpp:130:114: note: candidate is:
In file included from /usr/include/c++/4.8/bits/locale_facets.h:48:0,
from /usr/include/c++/4.8/bits/basic_ios.h:37,
from /usr/include/c++/4.8/ios:44,
from /usr/include/c++/4.8/istream:38,
from /usr/include/c++/4.8/sstream:38,
from StreamSwitch.h:17,
from StreamSwitch.cpp:7:
/usr/include/c++/4.8/bits/streambuf_iterator.h:369:5: note: template<class _CharT2> typename __gnu_cxx::__enable_if<std::__is_char<_CharT2>::__value, std::istreambuf_iterator<_CharT> >::__type std::find(std::istreambuf_iterator<_CharT>, std::istreambuf_iterator<_CharT>, const _CharT2&)
find(istreambuf_iterator<_CharT> __first,
^
/usr/include/c++/4.8/bits/streambuf_iterator.h:369:5: note: template argument deduction/substitution failed:
StreamSwitch.cpp:130:114: note: ‘std::_List_iterator<DiscriptorNode>’ is not derived from ‘std::istreambuf_iterator<_CharT>’
list<DiscriptorNode>::iterator matching=std::find(this->Unconnected.begin(),this->Unconnected.end(),s);
^
StreamSwitch.cpp:133:40: error: ‘class DiscriptorNode’ has no member named ‘ChannelName’
memcpy((*DiscIterator).ChannelName,InputDataBuffer,TransferCount);
^
StreamSwitch.cpp:133:81: error: ‘memcpy’ was not declared in this scope
memcpy((*DiscIterator).ChannelName,InputDataBuffer,TransferCount);
^
StreamSwitch.cpp:168:64: error: ‘bzero’ was not declared in this scope
bzero(InputDataBuffer, sizeof (InputDataBuffer));
^
Please help me figure out how to fix this for ubuntu, and I would love it if someone could explain how I can avoid this issue in the future.
Edited:
I fixed the issue by adding the following headers in StreamSwitch.h
#include <string.h>
#include <algorithm>
#include <inttypes.h>
and removing the following from StreamSwitch.h
#include <string>
I still don't know what is going on, or why code compiled on Mac and not on the digital Ocean droplet, or how I can avoid this issue all-together in the future.
The fixed width standard types appeared in C99 and C++11, see
http://en.cppreference.com/w/c/types/integer
http://en.cppreference.com/w/cpp/types/integer
They are declared/defined in "" and "".
So two steps to take to solve you problem platofrm independently is
1) #include <stdint.h> or #include <stdint>
2) make sure to compile by C99 or C++11 standards,
which might need to use special comandline parameters,
to be looked up in the documentation/help of your compilers
Note:
I extended an initially C answer to cover both. The C tag has been removed meanwhile. I think keeping the info on C is worth/harmless.
I am unsure how to solve my error. How do I make is so that my void run() function can see this variable 'intf'? the value of intf itself was declared separately in a .cnf file. Thank you
My errors are as follows
monreqserver.cc: In member function 'void Pds::MyXtcMonitorServer::run()':
monreqserver.cc:57: error: 'intf' was not declared in this scope
My code is as follows:
#include "pdsdata/app/XtcMonitorServer.hh"
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <time.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <stdlib.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#define mult_address "225.0.0.37"
#define mult_port "1100"
namespace Pds {
class MyXtcMonitorServer : public XtcMonitorServer {
public:
MyXtcMonitorServer(const char* tag,
unsigned sizeofBuffers,
unsigned numberofEvBuffers,
unsigned numberofEvQueues, const char * intf) :
XtcMonitorServer(tag,
sizeofBuffers,
numberofEvBuffers,
numberofEvQueues)
{
_init();
}
~MyXtcMonitorServer() {}
public:
void run() {
//////////////
//udp socket//
//////////////
int udp_socket_info;
struct sockaddr_in udp_server;
udp_socket_info = socket(AF_INET, SOCK_DGRAM, 0);
if (udp_socket_info == -1) {
puts("Could not create socket");
}
udp_server.sin_addr.s_addr = inet_addr(mult_address);
udp_server.sin_port = htons(1100);
udp_server.sin_family = AF_INET;
ifreq ifr;
ifr.ifr_addr.sa_family = AF_INET;
strcpy(ifr.ifr_name, intf);
if (ioctl(udp_socket_info, SIOCGIFADDR, &ifr)<0) {
perror("SIOCGIFADDR failed");
}
char* port = "1100";
char* ip = inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr);
char* zero = "/0";
char ip_port[100];
sprintf(ip_port, "%s %s %s", ip, port, zero);
sendto(udp_socket_info , ip_port , strlen(ip_port), 0, (struct sockaddr *)&udp_server, sizeof(udp_server));
puts("Message Sent");
///////////////////////////////
///SETTING UP TCP CONNECTION///
///////////////////////////////
int tcp_socket_info, tcp_new_socket;
struct sockaddr_in tcp_server, tcp_client;
tcp_socket_info = socket(AF_INET, SOCK_STREAM, 0);
if (tcp_socket_info == -1) {
printf("Could not create socket");
}
tcp_server.sin_addr.s_addr = INADDR_ANY;
tcp_server.sin_family = AF_INET;
tcp_server.sin_port = htons(1100);
int y=1;
if(setsockopt(tcp_socket_info, SOL_SOCKET, SO_REUSEADDR, (char*)&y, sizeof(y)) == -1) {
perror("set reuseaddr");
}
//binds socket
if (bind(tcp_socket_info, (struct sockaddr *)&tcp_server, sizeof(tcp_server)) < 0) {
perror("Bind error");
}
//listen
listen(tcp_socket_info , 5);
//waiting for connection
puts("Waiting for incoming connections...");
int c = sizeof(struct sockaddr_in);
//accept connection loop
tcp_new_socket = accept(tcp_socket_info, (struct sockaddr *)&tcp_client, (socklen_t*)&c);
puts("Connection accepted");
while(1)
sleep(1);
}
private:
void _copyDatagram(Dgram* dg, char*) {}
void _deleteDatagram(Dgram* dg) {}
void _requestDatagram() {}
private:
};
};
using namespace Pds;
void usage(char* progname) {
printf("Usage: %s -p <platform> -P <partition> -i <node mask> -n <numb shm buffers> -s <shm buffer size> [-q <# event queues>] [-t <tag name>] [-d] [-c] [-g <max groups>] [-h]\n", progname);
}
int main(int argc, char** argv) {
const unsigned NO_PLATFORM = unsigned(-1UL);
unsigned platform=NO_PLATFORM;
const char* partition = 0;
const char* tag = 0;
const char* intf = 0;
int numberOfBuffers = 0;
unsigned sizeOfBuffers = 0;
unsigned nevqueues = 1;
unsigned node = 0xffff;
unsigned nodes = 6;
bool ldist = false;
int c;
while ((c = getopt(argc, argv, "I:p:i:g:n:P:s:q:t:dch")) != -1) {
errno = 0;
char* endPtr;
switch (c) {
case 'p':
platform = strtoul(optarg, &endPtr, 0);
if (errno != 0 || endPtr == optarg) platform = NO_PLATFORM;
break;
case 'I':
intf = optarg;
break;
case 'i':
node = strtoul(optarg, &endPtr, 0);
break;
case 'g':
nodes = strtoul(optarg, &endPtr, 0);
break;
case 'n':
sscanf(optarg, "%d", &numberOfBuffers);
break;
case 'P':
partition = optarg;
break;
case 't':
tag = optarg;
break;
case 'q':
nevqueues = strtoul(optarg, NULL, 0);
break;
case 's':
sizeOfBuffers = (unsigned) strtoul(optarg, NULL, 0);
break;
case 'd':
ldist = true;
break;
case 'h':
// help
usage(argv[0]);
return 0;
break;
default:
printf("Unrecogized parameter\n");
usage(argv[0]);
break;
}
}
if (!numberOfBuffers || !sizeOfBuffers || platform == NO_PLATFORM || !partition || node == 0xffff) {
fprintf(stderr, "Missing parameters!\n");
usage(argv[0]);
return 1;
}
if (numberOfBuffers<8) numberOfBuffers=8;
if (!tag) tag=partition;
printf("\nPartition Tag:%s\n", tag);
MyXtcMonitorServer* apps = new MyXtcMonitorServer(tag,
sizeOfBuffers,
numberOfBuffers,
nevqueues, intf);
apps->distribute(ldist);
apps->run();
return 0;
}
and the header file include is as follows:
#ifndef Pds_XtcMonitorServer_hh
#define Pds_XtcMonitorServer_hh
#include "pdsdata/app/XtcMonitorMsg.hh"
#include "pdsdata/xtc/TransitionId.hh"
#include <pthread.h>
#include <mqueue.h>
#include <queue>
#include <stack>
#include <vector>
#include <poll.h>
#include <time.h>
namespace Pds {
class Dgram;
class TransitionCache;
class XtcMonitorServer {
public:
XtcMonitorServer(const char* tag,
unsigned sizeofBuffers,
unsigned numberofEvBuffers,
unsigned numberofEvQueues, const char * intf);
virtual ~XtcMonitorServer();
public:
enum Result { Handled, Deferred };
Result events (Dgram* dg);
void discover ();
void routine ();
void unlink ();
public:
void distribute (bool);
protected:
int _init ();
private:
void _initialize_client();
mqd_t _openQueue (const char* name, mq_attr&);
void _flushQueue (mqd_t q);
void _flushQueue (mqd_t q, char* m, unsigned sz);
void _moveQueue (mqd_t iq, mqd_t oq);
bool _send (Dgram*);
void _update (int,TransitionId::Value);
void _clearDest (mqd_t);
private:
virtual void _copyDatagram (Dgram* dg, char*);
virtual void _deleteDatagram(Dgram* dg);
virtual void _requestDatagram();
private:
const char* _tag; // name of the complete shared memory segment
unsigned _sizeOfBuffers; // size of each shared memory datagram buffer
unsigned _numberOfEvBuffers; // number of shared memory buffers for events
unsigned _numberOfEvQueues; // number of message queues for events
char* _myShm; // the pointer to start of shared memory
XtcMonitorMsg _myMsg; // template for messages
mqd_t _discoveryQueue; // message queue for clients to get
// the TCP port for initiating connections
mqd_t _myInputEvQueue; // message queue for returned events
mqd_t* _myOutputEvQueue; // message queues[nclients] for distributing events
std::vector<int> _myTrFd; // TCP sockets to clients for distributing
// transitions and detecting disconnects.
std::vector<int> _msgDest; // last client to which the buffer was sent
TransitionCache* _transitionCache;
int _initFd;
pollfd* _pfd; /* poll descriptors for:
** 0 new client connections
** 1 buffer returned from client
** 2 events to be distributed
** 3+ transition send/receive */
int _nfd;
mqd_t _shuffleQueue; // message queue for pre-distribution event processing
mqd_t _requestQueue; // message queue for buffers awaiting request completion
timespec _tmo;
pthread_t _discThread; // thread for receiving new client connections
pthread_t _taskThread; // thread for datagram distribution
unsigned _ievt; // event vector
};
};
#endif
The variable intf is a local variable of main(). It is therefore unknown outside the scope of main(). This is why you can't access it in the member functions of your class.
Three possible solutions:
you make the variable global (as it seems to be a global parameter that applies to all the classes
you make the variable a public static variable in the class. You then can initialize it from main, by using the prefix of your class.
or you define it as parameter of the run() member function (and invoke run from main accordingly).
The last one is the on which I'd choose, but i don't know enough about the context to give more objective advices for the choice:
// in the class:
void run(const char*intf) { // for convenience you can use the same name
...
}
// in main:
...
apps->run(intf); // pass the local variable as parameter
Name intf is referred in function run. But the compiler does not see any its declaration before its usage in the function
void run() {
//...
strcpy(ifr.ifr_name, intf);
^^^^^
I'm trying to improve my knowledge of OOP and decided to create a simple class to simplify sockets programming.
This is a learning experiment so I do not want to use boost, or other libraries.
I want to implement an event-driven recv(). Meaning, everytime there is new data coming in, it should call my function.
I think I need to create a thread to run a recv() loop and then call my function everytime there is new data. Is there other way around using threads? I want my code to be portable.
Here is my simple Class and example code:
class.h:
#ifndef _SOCKETSCLASS_H
#define _SOCKETSCLASS_H
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
#define W32
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")
#else
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#define SOCKET int
#endif
#include <string>
#include<ctime>
#include <stdio.h>
#include <stdarg.h>
#include <varargs.h>
#include <tchar.h>
using namespace std;
#ifdef _DEBUG
#define DEBUG(msg) XTrace(msg)
#else
#define DEBUG(msg, params)
#endif
struct TCP_Client_opts
{
BOOL UseSCprotocol;
BOOL UseEncryption;
BOOL UseCompression;
int CompressionLevel;
void *Callback;
BOOL async;
};
struct TCP_Stats
{
unsigned long int upload; //bytes
unsigned long int download;//bytes
time_t uptime; //seconds
};
class TCP_Client
{
public:
TCP_Client();
TCP_Client(TCP_Client_opts opts_set);
~TCP_Client();
SOCKET GetSocket();
void SetOptions(TCP_Client_opts opts_set);
TCP_Client_opts GetOptions();
BOOL Connect(string server, int port);
int Send(string data);
int Recv(string *data);
BOOL IsConnected();
int Disconnect();
TCP_Stats GetStats();
private:
SOCKET s = SOCKET_ERROR;
TCP_Client_opts opts;
TCP_Stats stats;
BOOL connected = FALSE;
time_t starttime;
};
#endif
class.cpp:
#include "SocketsClass.h"
void XTrace(LPCTSTR lpszFormat, ...)
{
va_list args;
va_start(args, lpszFormat);
int nBuf;
TCHAR szBuffer[512]; // get rid of this hard-coded buffer
nBuf = _vsnwprintf_s(szBuffer, 511, lpszFormat, args);
::OutputDebugString(szBuffer);
va_end(args);
}
TCP_Client::TCP_Client(TCP_Client_opts opts_set)
{
SetOptions(opts_set);
}
TCP_Client::~TCP_Client()
{
Disconnect();
}
TCP_Client::TCP_Client()
{
}
void TCP_Client::SetOptions(TCP_Client_opts opts_set)
{
opts = opts_set;
}
TCP_Client_opts TCP_Client::GetOptions()
{
return opts;
}
SOCKET TCP_Client::GetSocket()
{
return s;
}
BOOL TCP_Client::IsConnected()
{
return connected;
}
int TCP_Client::Disconnect()
{
connected = FALSE;
stats.uptime = time(0) - starttime;
return shutdown(s, 2);
}
BOOL TCP_Client::Connect(string server, int port)
{
struct sockaddr_in RemoteHost;
#ifdef W32
WSADATA wsd;
if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
{
DEBUG(L"Failed to load Winsock!\n");
return FALSE;
}
#endif
//create socket if it is not already created
if (s == SOCKET_ERROR)
{
//Create socket
s = socket(AF_INET, SOCK_STREAM, 0);
if (s == SOCKET_ERROR)
{
DEBUG(L"Could not create socket");
return FALSE;
}
}
//setup address structure
if (inet_addr(server.c_str()) == INADDR_NONE)
{
struct hostent *he;
//resolve the hostname, its not an ip address
if ((he = gethostbyname(server.c_str())) == NULL)
{
//gethostbyname failed
DEBUG(L"gethostbyname() - Failed to resolve hostname\n");
return FALSE;
}
}
else//plain ip address
{
RemoteHost.sin_addr.s_addr = inet_addr(server.c_str());
}
RemoteHost.sin_family = AF_INET;
RemoteHost.sin_port = htons(port);
//Connect to remote server
if (connect(s, (struct sockaddr *)&RemoteHost, sizeof(RemoteHost)) < 0)
{
DEBUG(L"connect() failed");
return FALSE;
}
connected = TRUE;
starttime = time(0);
stats.download = 0;
stats.upload = 0;
return TRUE;
}
TCP_Stats TCP_Client::GetStats()
{
if (connected==TRUE)
stats.uptime = time(0)-starttime;
return stats;
}
int TCP_Client::Send(string data)
{
stats.upload += data.length();
return send(s, data.c_str(), data.length(), 0);
}
int TCP_Client::Recv(string *data)
{
int ret = 0;
char buffer[512];
ret = recv(s, buffer, sizeof(buffer), 0);
data->assign(buffer);
data->resize(ret);
stats.download += data->length();
return ret;
}
main.cpp:
#include <stdio.h>
#include <string.h>
#include "SocketsClass.h"
using namespace std;
int main(int argc, char *argv)
{
TCP_Client tc;
tc.Connect("127.0.0.1", 9999);
tc.Send("HEllo");
string data;
tc.Recv(&data);
puts(data.c_str());
tc.Disconnect();
printf("\n\nDL: %i\nUP: %i\nUptime: %u\n", tc.GetStats().download, tc.GetStats().upload, tc.GetStats().uptime);
return 0;
}
Some extra questions:
Imagine I'm sending a file. How would my function know that the current data is related to the previous message?
How is my class design and implementation? SHould I change anything?
Thank you
If by "portable" you mean runs on other platforms besides Windows then a recv() loop in a worker thread is your only portable option. On Windows specifically, you have some additional choices:
Allocate a hidden window and then use WSAAsyncSelect() to receive FD_READ notifications. This requires a message loop, which you can put in a worker thread.
Use WSAEventSelect() to register a waitable event for FD_READ notifications and then wait for those events via WSAWaitForMultipleEvents() in a thread.
use WSARecv() with an I/O Completion Port. Poll the IOCP via GetQueuedCompletionResult() in a thread.
As for your question regarding messaging, TCP is a byte stream, it has no concept of messages. You have to frame your messages yourself. You can either:
give each message a fixed header that contains the message length. Read the header first, then read however many bytes it says, then read the next header, and so on.
separate each message with a unique delimiter that does not appear in the message data. Read until you encounter that delimiter, then read until the next delimiter, and so on.
Have your event loop call either poll or select to determine if there is data that can be read on the socket(s). Then read it, and call the appropriate callback function.
I have been working on a project and boy oh boy does my head hurt on this one. I am using a networking library called "enet" and I am trying to assign the client who connects information. Using the tutorial on the site, I use: server.event.packet->data = "client info"; However, enet complains that the string is not an "unsigned char *". Here is the build log (using clang++ to compile):
./network.h:9:14: warning: in-class initialization of non-static data member accepted as a C++11 extension
[-Wc++11-extensions]
int clients = 0;
^
main.cpp:142:28: error: assigning to 'enet_uint8 *' (aka 'unsigned char *') from incompatible type
'const char [12]';
server.event.packet->data = "client info";
^ ~~~~~~~~~~~~~
I have tried every type of casting I can think of and that I have searched for, but nothing seems to work. I can't make the darn thing happy.
Main.cpp:
#include <iostream>
#include <string>
#include <sstream>
#include <istream>
#include <cstring>
#include <cstdio>
#include <unistd.h>
#include <stdio.h>
#include "network.h"
#include "clients.h"
#include "config.h"
void runCommand(std::string command);
int startNetwork();
void shutdownNetwork();
void addClientRecord();
std::string getUsername();
std::string username;
bool manualInput = false;
bool debug = true;
int iPeerCount = 0;
Server server;
int main(int argc, char ** argv){
std::string currentCommand;
if(manualInput==true){
std::cout << "Please type a command: ";
std::getline(std::cin,currentCommand);
if(debug == true){
std::cout << currentCommand << std::endl;
}
runCommand(currentCommand);
}
startNetwork();
atexit(shutdownNetwork);
return 0;
}
int startNetwork(){
if (enet_initialize () != 0){
std::cout << "\nAn error has occured while initializing ENet.\n";
return EXIT_FAILURE;
}
server.startServer();
return 1;
}
void shutdownNetwork(){
enet_deinitialize();
}
int Server::startServer(){
// server.serverOne = enet_host_create (& server.address, 32, 2, 0, 0);
// if(CUSTOM_HOST == true){
// enet_address_set_host(&address, HOST);
// } else {
server.address.host = ENET_HOST_ANY;
// }
server.address.port = PORT;
server.serverOne = enet_host_create( & server.address, 32, 2, 0, 0);
if(debug==true){
printf("[NETWORK] Host: %x \n[NETWORK] Port: %u\n", server.address.host, address.port);
}
if(server.serverOne==NULL){
std::cout << "\nAn error has occured while starting the ENet server.\n";
exit (EXIT_FAILURE);
}
monitor();
return 1;
}
void Server::monitor(){
int clients = 0;
if(debug==true){
printf( "[NETWORK] Waiting for commands...\n" );
}
printf("[NETWORK] Server online, awaiting commands and/or connections...\n");
scan_network:
while(enet_host_service (serverOne, & event, 1000) > 0){
switch(event.type){
case ENET_EVENT_TYPE_CONNECT:
clients++;
printf("[INFO] New connection from: %x:%u.\n", event.peer -> address.host, event.peer -> address.port);
addClientRecord();
/* for(int x=0;x<32;x++){
if(clients[x].name != ""){ }
else{
clients[x].name = "full";
}
}*/
break;
case ENET_EVENT_TYPE_RECEIVE:
if(debug==true){ printf("A packet of length %lu containing %s was received from %s on channel %u.\n", event.packet -> dataLength, event.packet -> data, event.peer -> data, event.channelID); }
runCommand(reinterpret_cast<const char*>(event.packet -> data));
enet_packet_destroy(event.packet);
/* printf("Disconnect client %s ? ", event.peer -> data);
gets(buffer);
std::cout<<buffer<<std::endl;
if(buffer=="yes"){
enet_peer_disconnect(event.peer, 0);
}*/ //Do not use until fixed or spam!
break;
case ENET_EVENT_TYPE_DISCONNECT:
clients--;
printf("%s disconnected.\n", event.peer -> data);
event.peer -> data = NULL;
case ENET_EVENT_TYPE_NONE:
break;
}
}
goto scan_network;
}
void runCommand(std::string command){
if((command == "disconnect") || (command == "Disconnect") || (command=="DISCONNECT")){
enet_peer_disconnect(server.event.peer,0);
printf("[INFO] Client %s issued the disconnect command.\n", server.event.peer -> data);
}
}
std::string getUsername(){
return username;
}
void addClientRecord(){
std::string bufferName = ("client " + server.clients);
server.event.packet->data = "client info";
}
Network.h:
#include <enet/enet.h>
class Server {
public:
ENetAddress address;
ENetHost * serverOne;
ENetEvent event;
int clients = 0;
int startServer();
void monitor();
};
Any ideas and help is appreciated greatly. Cheers!
As far as I can see server is a struct of type Server, its field event is ENetEvent, and its field packet is of type _ENetPacket* and its field data is a unsigned char*. So now what is hapening: you create a сstring on the stack, then assign address of the first element to the data field of global object, then you leave the function and pointer is still alive while data stored there is no longer avaliable. That is why you get segfault when using correct typecast to unsigned char*.
So you should do the following:
void addClientRecord()
{
std::string bufferName = ("client " + server.clients);
char* clientName = "client info";
// Allocate mem
char* data = new unsigned char[strlen(clientName)];
// Copy string there
strcpy(data, clientName);
// Store pointer
server.event.packet->data = (unsigned char*)data;
}
and do no not forget to clear that allocated mem. That is for you should always check if server.event.packet->data contains non-nullptr value and if it does - delete and only then assign. And you should provide a destructor for Server where it will delete that string if any present and constructor to write there a nullptr on start so you won't delete some trash address, which most certainly will lead to crash. But first of all you need to figure out whether _ENetPacket or ENetEvent classes provide any functionality for data mentioned above. This is how cstrings work.
P.S. There should be a compiler flag that will toggle char to be unsigned by default.
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!