I am working on a primitive multiplayer battleship game for my CS class. I am able to get it running and send out information over LAN. To test whether this communication is working, I created a loop with a listener function that will stop when the packet is verified to have been received from the other computer's listen thread.
When I do this, it seems to send the packet, but the loop keeps running forever without any errors and without receiving confirmation that the packets were received by the other computer. Do you have any idea what might be happening?
//SFML headers
#include <SFML/Network.hpp>
//standard headers
#include <vector>
#include <iostream>
#include <string>
#include <thread>
#include <atomic>
#include <algorithm>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <exception>
std::atomic_bool running;
unsigned short port;
//Functions for threading
void startListen(sf::UdpSocket *sock, std::atomic_bool *r, sf::IpAddress *ip)
{
std::cout<<"Listener started"<<std::endl;
while(*r == true)
{
sf::sleep(sf::microseconds(50));
std::cout<<"Wait done"<<std::endl;
std::cout<<std::to_string(port)<<" port from the listen thread."<<std::endl;
sf::IpAddress sender;
sf::Packet p;
std::cout<<"tryna receive packet"<<std::endl;
if (sock->receive(p, sender, port) != sf::Socket::Done)
{
std::cout<<"Attempt at packet retrieval but packet retrieval failed! Error code 23./nPlease contact developer at mldevelopingstudios#gmail.com to report this failure."<<std::endl;
}
std::cout<<"Packet received!"<<std::endl;
std::string type;
p >> type;
std::cout<<"packet recieved, type: "<<type<<std::endl;
}
}
int main()
{
port = 4343;
sf::UdpSocket sock;
sf::UdpSocket recSock;
if (recSock.bind(port) != sf::Socket::Done)
{
std::cout<<"Whoops we crashed!!!!!!!!!!";
return 0;
}
//sock.unbind();
std::string ip;
sf::IpAddress IP1 = sf::IpAddress::getLocalAddress();
sf::String ip11 = IP1.toString();
std::cout<<"You LAN IP is: "<<ip11.toAnsiString()<<std::endl;
std::cout<<"Please enter the target IP address of the computer you'd like to play with."<<std::endl;
std::string iiii;
std::cin>>iiii;
sf::String o(iiii);
ip = o;
sf::IpAddress IP(ip);
std::thread listen(startListen, &recSock, &running, &IP);
listen.detach();
std::cout<<"Connection check"<<std::endl;
running = true;
std::string name;
std::cout<<"Name? "<<std::endl;
std::cin>>name;
while(running == true)
{
std::string mess = name;
sf::Packet packk;
std::string temp;
std::cout<<"Message: "<<std::endl;
std::cin>>temp;
if(temp == "!!STOP!!")
{
running = false;
}
else
{
mess = mess+ ": "+ temp;
packk<<mess;
if(sock.send(packk, IP, port)!= sf::Socket::Done)
{
std::cout<<"Sending error in test loop!!!!"<<std::endl;
}
}
}
recSock.unbind();
return 0;
}
Any help is greatly appreciated,
thank you and have a nice day.
EDIT: New code is the "minimalist code" and still no errors but nothing is received on either end.
EDIT: Added code to show whats happening
//SFML headers
#include <SFML/Network.hpp>
//standard headers
#include <vector>
#include <iostream>
#include <string>
#include <thread>
#include <atomic>
#include <algorithm>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <exception>
std::atomic_bool running;
unsigned short port;
//Functions for threading
void startListen(sf::UdpSocket *sock, std::atomic_bool *r, sf::IpAddress *ip)
{
std::cout<<"Listener started"<<std::endl;
while(*r == true)
{
unsigned short por = 4343;
sock->setBlocking(false);
std::cout<<std::to_string(por)<<" port from the listen thread."<<std::endl;
sf::IpAddress sender;
sf::Packet p;
std::cout<<"tryna receive packet"<<std::endl;
sf::Socket::Status stat = sock->receive(p, sender, por);
switch(stat)
{
case(sf::Socket::Done):
{
std::cout<<"Packet receiving completed!"<<std::endl;
std::cout<<"Packet received!"<<std::endl;
std::string type;
p >> type;
std::cout<<"packet recieved, type: "<<type<<std::endl;
break;
}
case(sf::Socket::NotReady):
std::cout<<"Socket not ready to received!"<<std::endl;
break;
case(sf::Socket::Partial):
std::cout<<"socket Kinda received?"<<std::endl;
break;
case(sf::Socket::Disconnected):
std::cout<<"Socket disconnected!"<<std::endl;
break;
case(sf::Socket::Error):
std::cout<<"Errorrrrrrrrrrrrrrrrrrr"<<std::endl;
break;
default:
std::cout<<"Oh noooooo"<<std::endl;
break;
}
}
}
int main()
{
port = 4343;
sf::UdpSocket sock;
sf::UdpSocket recSock;
if (recSock.bind(port) != sf::Socket::Done)
{
std::cout<<"Whoops we crashed!!!!!!!!!!";
return 0;
}
std::string ip;
sf::IpAddress IP1 = sf::IpAddress::getLocalAddress();
sf::String ip11 = IP1.toString();
std::cout<<"You LAN IP is: "<<ip11.toAnsiString()<<std::endl;
std::cout<<"Please enter the target IP address of the computer you'd like to play with."<<std::endl;
std::string iiii;
std::cin>>iiii;
sf::String o(iiii);
ip = o;
sf::IpAddress IP(ip);
std::thread listen(startListen, &recSock, &running, &IP);
listen.detach();
std::cout<<"Connection check"<<std::endl;
running = true;
std::string name;
std::cout<<"Name? "<<std::endl;
std::cin>>name;
while(running == true)
{
std::string mess = name;
sf::Packet packk;
std::string temp;
std::cout<<"Message: "<<std::endl;
std::cin>>temp;
if(temp == "!!STOP!!")
{
running = false;
}
else
{
mess = mess+ ": "+ temp;
packk<<mess;
sf::Socket::Status stat = sock.send(packk, IP, port);
switch(stat)
{
case(sf::Socket::Done):
std::cout<<"Packet Sending completed!"<<std::endl;
break;
case(sf::Socket::NotReady):
std::cout<<"Socket not ready to send!"<<std::endl;
break;
case(sf::Socket::Partial):
std::cout<<"socket Kinda sent?"<<std::endl;
break;
case(sf::Socket::Disconnected):
std::cout<<"Socket disconnected!"<<std::endl;
break;
case(sf::Socket::Error):
std::cout<<"Errorrrrrrrrrrrrrrrrrrr"<<std::endl;
break;
default:
std::cout<<"Oh noooooo"<<std::endl;
}
}
}
recSock.unbind();
return 0;
}
The socket on the receiving end is constantly reporting sf::Socket::NotReady
The sending reports sf::Socket::Done
Nothing is received on either end, when blocking is set to true or false
I somehow solved the problem through repetitive testing. I believe my error came from the operator overloads to load my classes into the packet. Or it was my IP entry. One of the two, fixed it. Sadly I did not document the fix though I am sure that was one of the two issues.
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 create a TCP server, I always get following error when I was run. How I get rid of this error, I do not understand where wrong?
Also it say data received while connection refused, I do not run client. Why?
Error:
Socket Created
Error connect to server: Connection refused
Error to accept: Invalid argument
Data received
Server
#include <iostream>
#include "serverh.hpp"
#include "glimpsepackages.h"
#include "soupbintcppackages.h"
int main() {
//soupBinTCP structures
DebugPacket D; //"+"
LoginAcceptedPacket A;
LoginRejectPacket J;
SequencedDataPacket S;
ServerHeartBeatPacket H;
EndOfSessionPacket Z;
//glimpse structures
SecondsMessage T;
CombinationOrderBookDelivery M;
TickSizeTableEntryPackage L;
OrderBookDirectoryPackage R;
OrderBookStatePackage O;
AddOrderMessageNoMPIDPackage A1;
AddOrderMessageMPIDPackage F;
std::string ip="127.0.0.1";
int port=7888;
ssize_t size=1024;
void *data={};
TcpServer server(ip, port);
server.connectToClient();
server.accept();
for (;;) {
server.receivingData(size);
switch (server.pop()) {
case '+':
// D.length=server.pop();
D.code = server.pop8();
D.text = server.pop();
break;
case 'A' :
A.to_little_endian();
A.code = server.pop8();
A.sequence_number = server.pop32();
for (int i = 0; i < 10; i++) {
A.session[i] = server.pop();
}
break;
case 'J':
J.code = server.pop8();
J.rejectreasoncode = server.pop();
break;
case 'S':
S.code = server.pop8();
S.message = server.pop();
break;
case 'H':
H.code = server.pop8();
case 'Z':
Z.code = server.pop8();
default:
return 0;
}
}
serverh.cpp
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <string>
#include "serverh.hpp"
#include "util.h"
#include <iostream>
TcpServer::TcpServer(std::string ip, int port):_ip(ip),
_port(port), _addrSize(sizeof _serverAddress)
{
_sockFd=-1;
};
bool TcpServer::connectToClient(){
_sockFd=socket(AF_INET,SOCK_STREAM,0);
if(_sockFd==-1)
perror("Error creating socket");
else
std::cerr<<"Socket Created\n";
_serverAddress.sin_addr.s_addr=inet_addr(_ip.c_str());
_serverAddress.sin_family=AF_INET;
_serverAddress.sin_port=htons(_port);
if(::connect(_sockFd,(struct sockaddr *)&_serverAddress, sizeof(_serverAddress))<0) {
perror("Error connect to server");
return false;
}
else
std::cerr<<"Connected\n";
if(::bind(_sockFd,(struct sockaddr *)&_serverAddress,_addrSize)<0)
perror("Error bind to server");
else
std::cerr<<"Socket bind\n";
if(::listen(_sockFd,5)==0)
std::cerr<<"Listining\n";
else
perror("Error on Listening");
return true;
}
bool TcpServer::accept(){
if(::accept(_sockFd,(struct sockaddr *) &_serverAddress, &_addrSize)<0)
perror("Error to accept");
else
std::cerr<<"Accepted\n";
return true;
}
void *TcpServer::receivingData(ssize_t size){
char *buffer=new char[size];
if(::recv(_sockFd,buffer, size,0)>0);
std::cerr<<"Data received\n";
return buffer;
}
void TcpServer::incOffset(int rest_length) {
void *restbuff = alloca(rest_length);
if (::recv(_sockFd, restbuff, rest_length, 0) < 0)
std::cerr << "Data received\n";
}
char TcpServer::pop(){
char ch;
if(::recv(_sockFd, &ch, 1, 0)>0)
return ch;
}
int8_t TcpServer::pop8() {
int8_t ch;
if (::recv(_sockFd, &ch, 8, 0) > 0)
return getLeValue(ch); // func include endian functions
}
int16_t TcpServer::pop16(){
int16_t ch;
if(::recv(_sockFd, &ch, 16, 0)>0)
return getLeValue(ch);
}
int32_t TcpServer::pop32(){
int32_t ch;
if(::recv(_sockFd, &ch, 32, 0)>0)
return getLeValue(ch);
}
int64_t TcpServer::pop64() {
int64_t ch;
if (::recv(_sockFd, &ch, 64, 0) > 0)
return getLeValue(ch);
}
Serverh.hpp
#ifndef SERVERH_H
#define SERVERH_H
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string>
class TcpServer{
public:
TcpServer(const std::string _ip,int _port);
bool connectToClient();
bool accept();
void *receivingData(ssize_t size);
bool sendToClient();
char pop();
int8_t pop8();
int16_t pop16();
int32_t pop32();
int64_t pop64();
void incOffset(int rest_length);
private:
std::string _ip;
int _port;
int _sockFd;
sockaddr_in _serverAddress;
socklen_t _addrSize;
};
#endif
The problem
Error: Socket Created Error connect to server: Connection refused Error to accept: Invalid argument Data received
Two errors here, a failure to connect because the other address is not accepting connections and a failure to accept because one or more of the input parameters are bad.
The failure to connect is understandable. Clients are rarely listening on a port for connections. Clients usually initiate the connection.
The failure to accept is caused by connectToClient exiting after failing to connect. It never binds and listens, so the socket has not been set up for accept.
Fixing this
A server should not attempt to connect. It should listen for attempts to connect to it. This makes connectToClient bizarre. It tries to connect to a client and then, on the same socket, bind and listen. You can't do both, so I'd discard
if(::connect(_sockFd,(struct sockaddr *)&_serverAddress, sizeof(_serverAddress))<0) {
perror("Error connect to server");
return false;
}
else
std::cerr<<"Connected\n";
and
reconsider
_serverAddress.sin_addr.s_addr=inet_addr(_ip.c_str());
as this will only listen to one incoming address which might be what you want, but isn't usually. I would also rename the function to reflect the change in purpose.
Note:
if(::accept(_sockFd,(struct sockaddr *) &_serverAddress, &_addrSize)<0)
passes in a pointer to the server address. This location will be overwritten with the address of the client. It's harmless, but you probably don't want to do this. You aren't using the returned address as far as I can see, so you might as well just pass in NULLs and pass on it.
if(::accept(_sockFd, NULL, NULL)<0)
I've created two simple TCP/IP communication programs (server & client). It basically just sends and receives the same message over and over again.
However when I execute client and server on the same workstation everything runs fine. It is only then, when I execute the same programs via a lokal network that the server doesn't receive any incomming connection requests.
The firewall is disabled on both stations. Has anyone an idea what I'm missing out?
Here is the code
client.cpp
#include "client.h"
#include <QHostAddress>
#include <iostream>
#include <conio.h>
Client::Client(QObject* parent): QObject(parent)
{
connect(&client, SIGNAL(connected()), this, SLOT(startTransfer()));
connect(&client, SIGNAL(readyRead()), this, SLOT(receive()));
QHostAddress addr = QHostAddress("127.0.0.1");
client.connectToHost(addr, 5200);
}
Client::~Client()
{
client.close();
}
void Client::startTransfer()
{
client.write("Hello, world", 13);
send("start client communication");
}
void Client::send(const char *buffer)
{
std::cout<<"OUT: "<<buffer<<std::endl;
client.write(buffer,strlen(buffer));
}
void Client::receive()
{
char temp[1024] = {0};
int len = client.read(temp,client.bytesAvailable());
std::cout<<"IN: "<< temp<<std::endl;
send("client to server");
}
server.cpp
#include "theserver.h"
#include <iostream>
#include <conio.h>
using namespace std;
Server::Server(QObject* parent): QObject(parent)
{
connect(&server, SIGNAL(newConnection()), this, SLOT(acceptConnection()));
server.listen(QHostAddress::Any, 5200);
}
Server::~Server()
{
server.close();
}
void Server::acceptConnection()
{
client = server.nextPendingConnection();
if(client)
{
connect(client, SIGNAL(readyRead()), this, SLOT(receive()));
}
}
void Server::startRead()
{
char buffer[1024] = {0};
client->read(buffer, client->bytesAvailable());
cout << "IN: "<<buffer << endl;
}
void Server::receive()
{
char buffer[1024] = {0};
client->read(buffer, client->bytesAvailable());
cout << "IN: "<<buffer << endl;
}
void Server::sendData(const char* buffer)
{
cout <<"OUT: "<<buffer<<endl;
if(client)
client->write(buffer);
}
I'm first initiating the server program following by the client
You need to use the remote machine's IP address to connect. Now you are always connecting to 127.0.0.1, which is the machine where the application is being run. That is why the connection is never made to a remote machine.
I'm trying to create a socket for my SDL server.
Problem is that I get an access violation crash because my socket called server is unable to open itself properly.
My class:
#pragma once
#include <SDL_net.h>
#include <thread>
#include <vector>
#include <string>
#include <iostream>
using namespace std;
const Uint16 SERVER_PORT = 1234;
const int TICKS_PER_SECOND = 1000;
const int REQUIRED_PLAYERS = 1;
class ServerTCP {
private:
//Thread data
thread *threadListen;
bool threadExit;
//Server data
IPaddress serverIP;
TCPsocket server;
vector <string> feedback;
//Client data
vector <TCPsocket> clients;
vector <string> events;
static void threadLoop(ServerTCP *self);
public:
ServerTCP();
~ServerTCP();
};
Source:
#include "ServerTCP.h"
ServerTCP::ServerTCP() {
printf("Starting server...\n");
if (SDLNet_ResolveHost(&serverIP, NULL, SERVER_PORT) == -1) {
printf("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
}
server = SDLNet_TCP_Open(&serverIP);
if (!server) {
printf("SDLNet_TCP_Open: %s\n", SDLNet_GetError());
}
threadExit = false;
threadListen = new thread(&ServerTCP::threadLoop, this);
}
ServerTCP::~ServerTCP() {
printf("Shutting down server...\n");
threadExit = true;
threadListen->join();
for (int i = 0; i < clients.size(); i++) {
string warning = "Server has shut down, you was disconnected!\n";
SDLNet_TCP_Send(clients[i], warning.c_str(), warning.size());
SDLNet_TCP_Close(clients[i]);
}
SDLNet_TCP_Close(server);
}
void ServerTCP::threadLoop(ServerTCP *self) {
printf("Waiting for players...\n");
TCPsocket newClient;
//Run thread until orderered to stop
while (!self->threadExit) {
//Look for new clients
newClient = SDLNet_TCP_Accept(self->server);
if (newClient) {
self->clients.push_back(newClient);
string warning = "You have connected to the server!\n";
SDLNet_TCP_Send(newClient, warning.c_str(), warning.size());
printf("Player %i has connected!\n", self->clients.size());
}
if (self->clients.size() >= REQUIRED_PLAYERS) {
for (int i = 0; i < REQUIRED_PLAYERS; i++) {
string warning = "You found an opponent!\n";
SDLNet_TCP_Send(self->clients[i], warning.c_str(), warning.size());
SDLNet_TCP_Close(self->clients[i]);
}
}
}
}
Output:
Starting server...
SDLNet_TCP_Open: Couldn't create socket
Never mind, I forgot I had the SDLNet_Init function in my sub class i removed in the server file.
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.