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.
Related
I'm struggling using the mosquitto lib on my RaspberryPI 4 in an non-blocking way.
This is may main method:
#include <stdio.h>
#include <mosquitto.h>
#include "mqtt.h"
#include <string>
int main(int argc, char **argv)
{
printf("Start\n");
MqttConnector * mqtt = new MqttConnector("piClient", "send", "rc", 1883, "localhost", 60);
mqtt->startClient();
printf("MQTT is started\n");
while(1)
{
}
return 0;
}
Important parts of mqtt.cpp
#include "mqtt.h"
#include <stdio.h>
#include <string>
#include <string.h>
#include <mosquitto.h>
MqttConnector::MqttConnector(std::string id, std::string sendTopic, std::string receiveTopic, int port, std::string host, int keepalive)
{
mosquitto_lib_init();
mosqClient = mosquitto_new(id.c_str(), true, this);
if(!mosqClient){
fprintf(stderr, "Error: Out of memory.\n");
}
this->keepalive = keepalive;
this->id = id;
this->host = host;
this->port = port;
this->sendTopic = sendTopic;
this->receiveTopic = receiveTopic;
}
MqttConnector::~MqttConnector()
{
mosquitto_destroy(mosqClient);
mosquitto_lib_cleanup();
}
void MqttConnector::messageCallback(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *message)
{
//MqttConnector * mqttInstance = (MqttConnector *) userdata;
if(message->payloadlen){
std::string payloadString = reinterpret_cast<char*>(message->payload);
printf("Message arriving in %s : %s\n", message->topic, payloadString.c_str());
std::string rp = "rp";
if(payloadString == rp)
{
mosquitto_publish(mosq, NULL, "send", strlen("test"), "test", 2, false);
}
}else{
printf("Empty message arriving in %s\n", message->topic);
}
}
void MqttConnector::connectCallback(struct mosquitto *mosq, void *userdata, int result)
{
if(!result){
printf("Connection established\n");
}else{
fprintf(stderr, "Connect failed\n");
}
}
void MqttConnector::logCallback(struct mosquitto *mosq, void *userdata, int level, const char *str)
{
/* Pring all log messages regardless of level. */
printf("%s\n", str);
}
void MqttConnector::publishCallback(struct mosquitto *mosq, void *userdata, int usernumber)
{
printf("Published a message\n");
}
void MqttConnector::startClient()
{
mosquitto_message_callback_set(mosqClient, messageCallback);
mosquitto_log_callback_set(mosqClient, logCallback);
mosquitto_connect_callback_set(mosqClient, connectCallback);
mosquitto_publish_callback_set(mosqClient, publishCallback);
/*
//Connecting without async works! Publish and receiving messages work!
// loop starts by calling loop_forever
if(mosquitto_connect(mosqClient, host.c_str(), port, keepalive)){
fprintf(stderr, "Unable to connect.\n");
}
mosquitto_subscribe(mosqClient, NULL, receiveTopic.c_str(), 2);
mosquitto_loop_forever(mosqClient, 10, 1);
*/
// According to docu we need to call loop_start instead of forever
// not able to publish/receive message with this!
if(mosquitto_connect_async(mosqClient, host.c_str(), port, keepalive)){
fprintf(stderr, "Unable to connect.\n");
}
mosquitto_subscribe(mosqClient, NULL, receiveTopic.c_str(), 2);
mosquitto_loop_start(mosqClient);
}
Connecting to the server with mosquitto_connect and starting the loop with mosquitto_loop_forever is the blocking way. Therefore I only see the first print statement in the main.cpp ('Start'). But with this way, I'm able to publish and receive message with that client.
As soon as I connect with mosquitto_connect_async, I need to start the loop with mosquitto_loop_start as it is mentioned in the documentation. The client connects to the server without an error. And I also see the second print statement from the main.cpp now. But I'm not able to publish oder receive any messages with the mqtt client.
My first guess was that because of the threading, the messages are not printed on the console, but even other clients startet with mosquitto_sub don't receive a message from that c++ client.
Do I need to install something for threading support on the RPI4?
I don't understand why it is not working, because the RPI4 supports multithreading.
Thanks for helping
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);
}
}
Im trying to do multi client chat in c++ using TCP socket.
i have download the source of the socket implementation from this site.
The problem is when i try to send message to the server from the client,
the "ecko" that i recive from the server is endless string of spaces.
i tried to debug the client code, the client read the input properly.
In the first few message the server send back to the client his message,
but after few messages the client get back endless spaces.
i tried to use memset to nullify(put zero in all the array), but its making it worse, the server dont recive messages at all.
Would appreciate help (:
This is the server side:
#include "PracticalSocket.h"
#include <stdio.h>
#include <process.h>
#include <Windows.h>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
TCPSocket* MyClients[20];
int ClientCount = 0;
void connectCLient(void* pValue){
int nI,Flag;
char st[1024];
//memset(st,0,1024); // doing problems
TCPSocket* pServerClient = (TCPSocket*)pValue;
MyClients[ClientCount] = pServerClient;
ClientCount++;
try{
while (true)
{
Flag = pServerClient->recv(st,strlen(st));
if(Flag>1){
printf("%s\n",st);
for(nI = 0; nI< ClientCount ; nI++){
MyClients[nI]->send(st,strlen(st)+1);
}
}
}
}
catch(...){
puts("one client lefttt");
}
}
int main(int argc, char* argv[])
{
TCPServerSocket* pServer = new TCPServerSocket(8546);
int nClientCounter = 0;
printf("Start TCP Server ... on Port %d\n", 8546);
try{
while(true)
{
printf("Wait for new TCP Clients ... \n");
TCPSocket* pClient = pServer->accept();
_beginthread(connectCLient,0,(void*)pClient);
printf("Client %d Connected ... \n", ++nClientCounter);
}
}
catch(...){
puts("one client left");
}
return 0;
}
This is the Client side:
#include "PracticalSocket.h"
#include <stdio.h>
#include <process.h>
#include <Windows.h>
using namespace std;
#pragma comment (lib, "ws2_32.lib")
void ReciveMessages(void * pValue ){
char recvM[1024];
TCPSocket* pClient = (TCPSocket*)pValue;
while(true){
pClient->recv(recvM,strlen(recvM));
printf("%s\n",recvM);
}
}
int main(int argc, char* argv[])
{
try
{
TCPSocket * cClient = new TCPSocket();
cClient->connect("127.0.0.1",8546);
_beginthread(ReciveMessages,0,(void*)cClient);
char st[1024];
memset(st,0,1024);
while(true)
{
printf("Press Text -->");
fgets(st, sizeof st, stdin);
cClient->send(st,strlen(st)+2);
}
}
catch(...)
{
printf("Socket Error..!");
system("pause");//run cmd comment - stop the system
}
return 0;
}
There are a few mistakes in the code:
MyClients[ClientCount] = pServerClient;
ClientCount++;
Since the above happens in different threads, ClientCount++ is non-atomic and causes race conditions. Make ClientCount atomic or do that in one server thread.
In:
Flag = pServerClient->recv(st,strlen(st));
if(Flag>1) {
printf("%s\n",st);
for(nI = 0; nI< ClientCount ; nI++)
MyClients[nI]->send(st,strlen(st)+1);
st doesn't end with \0, because it can be a partial read, so that strlen(st) returns wrong results. Fix:
ssize_t received = pServerClient->recv(st, sizeof st - 1);
if(received > 0) {
st[received] = 0; // Zero-terminate.
printf("%s\n", st);
for(nI = 0; nI< ClientCount ; nI++)
MyClients[nI]->send(st, received);
Similar issue:
pClient->recv(recvM,strlen(recvM));
printf("%s\n",recvM);
Fix:
ssize_t received = pClient->recv(recvM, sizeof recvM - 1);
if(received > 0) {
recvM[received] = 0;
printf("%s\n",recvM);
}
And in:
cClient->send(st,strlen(st)+2);
No point in sending the zero terminator:
cClient->send(st, strlen(st));
TCP is a stream protocol which means that send and recv may send/receive partial data and there are no message boundaries. You may like to delimit your messages.
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.
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);
^^^^^