I have the following code, not minimal, but it's unclear how to further reduce it without masking the effect.
I have a single class representing both server and client, but I don't think this is the problem.
Problem arises because I try to check in server constructor if one server is already running and, if not, I fork and detach (actually "daemonize") the server.
This seems to work but spawned server is always the base class, even if I am trying to start a child.
Actually base class is "empty" and it's supposed to be subclassed overriding the actual handing of messages... which doesn't work at all.
I suspect at time of "fork()" child class is not set up and I fork the parent, but I would like confirmation and, if possible, a workaround.
Note: printing on the child (server) does not (currently) work I tried to convert to standard iostream from spdlog, but I must have goofed somewhere.
//=============== HEADER ======================
#include <cstdint>
#include <vector>
#include <netinet/in.h>
#include <fstream>
#include <iostream>
#define DAEMON_BUFSIZE (1024)
class daemon {
private:
int sockfd;
struct sockaddr_in servaddr;
void init_logger(const char *fn="log.txt") {
std::ofstream out("out.txt");
std::streambuf *coutbuf = std::cout.rdbuf(); //save old buf
std::cout.rdbuf(out.rdbuf()); //redirect std::cout to out.txt!
}
void server();
void client();
protected:
enum server_cmd {
server_reply,
server_noreply,
server_teardown
};
virtual void init_server() {}
virtual server_cmd handle_msg(std::vector<char> &msg) {
std::cout << "base class called!" << std::endl;
return server_noreply;
};
public:
class exception : public std::exception {};
explicit daemon(uint16_t port);
daemon(const char *addr, uint16_t port);
virtual ~daemon() = default;
int send_msg(std::vector<char> &msg, bool needs_reply= false);
};
//=============== IMPLEMENTATION ======================
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <cstring>
static int check_daemon(uint16_t port) {
int sockfd;
// Creating socket file descriptor
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
std::cout << "[CRITICAL] server socket creation failed (should never happen): " << strerror(errno) << std::endl;
exit(EXIT_FAILURE);
}
struct sockaddr_in servaddr{AF_INET, htons(port), INADDR_ANY};
// Bind the socket with the server address
if (bind(sockfd, (const struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) {
// port is busy, so daemon is running!
std::cout << "daemon found" << std::endl;
return -1;
}
return sockfd;
}
/**
* Server constructor
* #param port
*/
daemon::daemon(uint16_t port) {
sockfd = check_daemon(port);
if (sockfd <= 0) {
throw exception();
}
auto pid = fork();
if (pid < 0) {
std::cout << "[CRITICAL] fork failed: " << strerror(errno) << std::endl;
} else if (pid == 0) {
init_logger();
// child; daemonize
server();
exit(EXIT_SUCCESS);
}
// parent; wait for child
sleep(1); // should be done better
}
void daemon::server() {
std::cout << "[INFO] starting daemon" << std::endl;
::daemon(1, 1);
// here we are in daemon
std::cout << "[INFO] server_daemon: initializing" << std::endl;
init_server();
struct sockaddr_in cliaddr{};
char buff[DAEMON_BUFSIZE];
bool running = true;
std::cout << "[TRACE] server_daemon: entering loop" << std::endl;
while (running) {
socklen_t len = sizeof(cliaddr);
auto n = recvfrom(sockfd, buff, DAEMON_BUFSIZE, MSG_WAITALL, (struct sockaddr*) &cliaddr, &len);
if (n == -1) {
std::cout << "[CRITICAL] recvfrom error (" << errno << "): " << strerror(errno) << std::endl;
throw exception();
}
std::vector<char> msg(buff, buff+n);
std::cout << "[TRACE] server_daemon: message received (" << n << " bytes)" << std::endl;
auto v = handle_msg(msg);
switch (v) {
case server_reply:
sendto(sockfd, msg.data(), msg.size(), MSG_CONFIRM, (const struct sockaddr *) &cliaddr, len);
std::cout << "[TRACE] reply sent: " << msg.size() << " bytes" << std::endl;
break;
case server_noreply:
std::cout << "[TRACE] no reply sent" << std::endl;
break;
case server_teardown:
running = false;
break;
}
}
std::cout << "[INFO] server_daemon: at exit" << std::endl;
}
daemon::daemon(const char *addr, uint16_t port) {
// Creating socket file descriptor
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
std::cout << "[CRITICAL] socket creation failed (" << errno <<"): " << strerror(errno) << std::endl;
throw exception();
}
// Filling server information
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);
servaddr.sin_addr.s_addr = inet_addr(addr);
}
int daemon::send_msg(std::vector<char> &msg, bool needs_reply) {
auto ret = sendto(sockfd, msg.data(), msg.size(), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
if (ret < 0) {
std::cout << "[ERROR] send failed: " << strerror(errno) << std::endl;
return -1;
}
if (needs_reply) {
char buff[DAEMON_BUFSIZE];
socklen_t len;
auto n = recvfrom(sockfd, buff, DAEMON_BUFSIZE, MSG_WAITALL, (struct sockaddr *)&servaddr, &len);
msg.assign(buff, buff+n);
std::cout << "[TRACE] received answer (" << n << " bytes)" << std::endl;
return n;
}
return 0;
}
void daemon::client() {
}
#define test_me
#ifdef test_me
#include <sstream>
class server : public daemon {
public:
explicit server(uint16_t port)
: daemon(port)
{
std::cout << "[INFO] server constructor" << std::endl;
}
protected:
void init_server() override {
std::cout << "[INFO] init_server" << std::endl;
}
server_cmd handle_msg(std::vector<char> &msg) override {
std::string s(msg.begin(), msg.end());
std::cout << "[INFO] received '" << s << "'" << std::endl;
return (s == "END")? server_teardown: server_noreply;
};
public:
};
class client : public daemon {
public:
explicit client(const char *addr, uint16_t port)
: daemon(addr, port)
{}
protected:
server_cmd handle_msg(std::vector<char> &msg) override {
std::cout << "[CRITICAL] client handle_msg() called" << std::endl;
return server_teardown;
};
public:
};
int main() {
try {
server server(12345);
} catch (daemon::exception &e) {
std::cout << "[INFO] daemon already running" << std::endl;
}
client client("127.0.0.1", 12345);
for (auto i : {1, 2, 3}) {
std::ostringstream msg;
msg << "message No." << i << " sent";
auto str = msg.str();
std::vector<char> vec(str.begin(), str.end());
client.send_msg(vec);
}
std::vector<char> v({'E', 'N', 'D'});
client.send_msg(v);
}
#endif
This has nothing to do with fork(). If you replace fork() with a random number generator every time the random number picked the server code path you will get the same results.
class server : public daemon
In C++ when you construct a class that inherits from a base class, the base class gets constructed first. Until the base class finishes constructing the derived class does not exist. This is fundamental to C++, that's how C++ works.
In the base class's constructor:
daemon::daemon(uint16_t port) {
// ...
auto pid = fork();
if (pid < 0) {
std::cout << "[CRITICAL] fork failed: " << strerror(errno) << std::endl;
} else if (pid == 0) {
init_logger();
// child; daemonize
server();
This is the base class, the child class does not get constructed until the base constructor returns. The server() function expects to invoke virtual methods that are overridden in the child class. But the child class does not exist. It is yet to be born, in a manner of speaking.
This is just how C++ works fundamentally. You'll need to change the design of your classes, accordingly, in order to achieve the desired results.
Related
I am creating a server proxy between a client and a server. Currently I have found a base of the idea in python, and I'm trying to convert it to C++. However, I'm struggling since my code doesn't seem to work correctly.
Apparently recv keeps returning SOCKET_ERROR, so I am thinking that maybe I've done something wrongly with winsock.
#include <iostream>
#include <string>
#include <WinSock2.h>
#include <ws2tcpip.h>
#include <thread>
#include <vector>
#include <sstream>
#pragma comment(lib, "Ws2_32.lib")
void parse(char* data, int len, int port, const std::string& origin) {
std::cout << "[" << origin << "(" << port << ")] " << data << std::endl;
}
struct ProxyToServer {
SOCKET m_client;
SOCKET m_server;
int m_port;
ProxyToServer() = default;
ProxyToServer(const char* host, int port) {
m_port = port;
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) == 0) {
sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
inet_pton(AF_INET, host, &server_addr.sin_addr);
if ((m_server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {
std::cerr << "[Proxy2Server] Could not create socket!\n";
}
if (connect(m_server, reinterpret_cast<sockaddr*>(&server_addr), sizeof(server_addr)) != 0) {
std::cerr << "[Proxy2Server] Could not connect socket!\n";
}
if (WSACleanup() != 0) {
std::cerr << "Cleanup failed!\n";
}
}
else {
std::cerr << " Winsock startup failed!\n";
}
}
void execute() {
char data[4096];
while (true) {
int totalRecv = recv(m_server, data, 4096, 0);
if (totalRecv == SOCKET_ERROR) std::cout << "Socket error... (recv)" << std::endl;
if (totalRecv > 0) {
parse(data, 4096, m_port, "server");
send(m_client, data, totalRecv, 0);
}
}
}
};
struct ClientToProxy {
SOCKET m_server;
SOCKET m_client;
int m_port;
ClientToProxy() = default;
ClientToProxy(const char* host, int port) {
m_port = port;
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) == 0) {
if (LOBYTE(wsaData.wVersion) >= 2) {
SOCKET sock;
if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {
std::cerr << "[Client2Proxy] Could not create socket!\n";
}
const int enable = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*) & enable, sizeof(enable));
sockaddr_in sock_addr;
sock_addr.sin_family = AF_INET;
sock_addr.sin_port = htons(port);
inet_pton(AF_INET, host, &sock_addr.sin_addr);
if (bind(sock, (sockaddr*)&sock_addr, sizeof(sock_addr)) != 0) {
std::cerr << "[Client2Proxy] Could not bind socket!\n";
}
if (listen(sock, 100) != 0) {
std::cerr << "[Client2Proxy] Could not listen!\n";
}
sockaddr_in clientSockAddr;
int clientSockSize = sizeof(clientSockAddr);
m_client = accept(sock,reinterpret_cast<sockaddr*>(&clientSockAddr),&clientSockSize);
if (m_client == INVALID_SOCKET) std::cerr << "Invalid Client! " << std::endl;
}
else {
std::cerr << "Requested winsock ver is not supported! \n";
}
if (WSACleanup() != 0) {
std::cerr << "Cleanup failed!\n";
}
}
else {
std::cerr << " Winsock startup failed!\n";
}
}
void execute() {
while (true) {
char data[4096];
int totalRecv = recv(m_client, data, sizeof(data), 0);
if (totalRecv == SOCKET_ERROR) std::cout << "Socket error... (recv) " << std::endl;
if (totalRecv > 0) {
parse(data, 4096, m_port, "client");
send(m_server, data, sizeof(data), 0);
}
}
}
};
struct Proxy {
const char* m_from_host;
const char* m_to_host;
int m_port;
ClientToProxy g2p;
ProxyToServer p2s;
Proxy(const char* from_host, const char* to_host, int port) : m_from_host(from_host), m_to_host(to_host), m_port(port) {}
void execute() {
while (true) {
std::cout << "[proxy(" << m_port << ")] setting up" << std::endl;
g2p = ClientToProxy(m_from_host, m_port);
p2s = ProxyToServer(m_to_host, m_port);
std::cout << "[proxy(" << m_port << ")] connection established" << std::endl;
p2s.m_client = g2p.m_client;
g2p.m_server = p2s.m_server;
std::thread t1(&ClientToProxy::execute, &g2p);
std::thread t2(&ProxyToServer::execute, &p2s);
t1.join();
t2.join();
}
}
};
int main() {
Proxy master_server("127.0.0.1", "183.211.202.94", 13011);
std::thread t3(&Proxy::execute, &master_server);
t3.join();
/*
std::vector<Proxy> game_servers;
for (int port = 13000; port < 13006; port++) {
Proxy game_server("127.0.0.1", "185.212.200.90", port);
std::thread t4(&Proxy::execute, &game_server);
game_servers.push_back(game_server);
t4.join();
}*/
}
I don't know how to reduce this code unfortunately. The issues are in all recv calls in both classes seemingly...
N.B I am very sure that all the IPs are correcty and the port as well.
The way my code is currently written only allows a message from the server to be read directly after input is taken and a message is sent. However, this code is for a chat server and must allow a read to occur at any time a message is sent.
#include <iostream>
#include <string>
#include <cstring>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define SERVER_ADDRESS "127.0.0.1"
constexpr int server_port = 15555;
#define SERVER_SUCCESS "1"
#define SERVER_FAILURE "-1"
constexpr int msg_buffer_size = 4096;
int main(int argc, char *argv[])
{
struct sockaddr_in serv_addr;
int sock;
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
std::cerr << "Socket creation failed!" << std::endl;
return 1;
}
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(server_port);
if (inet_pton(AF_INET, SERVER_ADDRESS, &serv_addr.sin_addr) <= 0)
{
std::cerr << "Invalid server address!" << std::endl;
return 1;
}
if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
{
std::cerr << "Failed to connect to chat server!" << std::endl;
return 1;
}
int valread;
while (true)
{
std::cout << ">> ";
char msg[msg_buffer_size];
char return_msg[msg_buffer_size];
std::string input;
std::getline(std::cin, input);
if (input == "quit")
return 0;
if (input.length() > 4000)
{
std::cout << "Input length must be less than 4000 characters." << std::endl;
continue;
}
strcpy(msg, input.c_str());
if (send(sock, msg, strlen(msg), 0) < 0)
{
std::cout << "Error sending data." << std::endl;
continue;
}
if (recv(sock, return_msg, msg_buffer_size, 0) < 0)
{
std::cout << "Error receiving data." << std::endl;
continue;
}
std::string code(strtok(return_msg, " "));
if (code == SERVER_FAILURE)
std::cout << "Failure: " << strtok(NULL, "") << std::endl;
else
std::cout << strtok(NULL, "") << std::endl;
memset(msg, 0, msg_buffer_size);
memset(return_msg, 0, msg_buffer_size);
}
std::cout << "Exiting." << std::endl;
close(sock);
return 0;
}
What would be a correct way to allow the client to receive a message as soon as one is sent from the server? I was thinking about making a thread, but it seemed kind of redundant since I would be receiving in two places.
I'm trying to build a client and a server in the same program. For example, user 1 sends a packet of data to user 2, user 2 after receiving the packet sends back a different packet to user 1. The problem is, after running the program neither user receives the packets.If I build the client in a separate program from the server it works
#pragma comment(lib,"ws2_32.lib")
#include <WinSock2.h>
#include <iostream>
#include <thread>
static const int num_threads = 2;
static char buffer[8096 + 1];
char a[256] = {};
char MOTD[256];
void call_from_thread(int tid) {
// ---------- Server code: ---------- //
if( tid == 0 ){
WSAData wsaData;
WORD DllVersion = MAKEWORD(2, 1);
if (WSAStartup(DllVersion, &wsaData) != 0){MessageBoxA(NULL, "WinSock startup failed", "Error", MB_OK | MB_ICONERROR);}
SOCKADDR_IN addr;
int addrlen = sizeof(addr);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(1112);
addr.sin_family = AF_INET;
SOCKET sListen = socket(AF_INET, SOCK_STREAM, NULL);
bind(sListen, (SOCKADDR*)&addr, sizeof(addr));
listen(sListen, SOMAXCONN);
SOCKET newConnection;
newConnection = accept(sListen, (SOCKADDR*)&addr, &addrlen);
if (newConnection == 0){
std::cout << "Failed to accept the client's connection." << std::endl;
}else{
std::cout << "Client Connected!" << std::endl;
}
while (true){
std::cin >> a;
send(newConnection, a, sizeof(a), NULL);
}
}else if( tid == 1 ){
// ---------- Client code: ---------- //
WSAData wsaData;
WORD DllVersion = MAKEWORD(2, 1);
if (WSAStartup(DllVersion, &wsaData) != 0){MessageBoxA(NULL, "Winsock startup failed", "Error", MB_OK | MB_ICONERROR);}
SOCKADDR_IN addr;
int sizeofaddr = sizeof(addr);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(1111);
addr.sin_family = AF_INET;
SOCKET Connection = socket(AF_INET, SOCK_STREAM, NULL); //Set Connection socket
if (connect(Connection, (SOCKADDR*)&addr, sizeofaddr) != 0) //If we are unable to connect...
{
MessageBoxA(NULL, "Failed to Connect", "Error", MB_OK | MB_ICONERROR);
//return 0; //Failed to Connect
}
std::cout << "Connected!" << std::endl;
char MOTD[256];
while (true){
recv(Connection, MOTD, sizeof(MOTD), NULL); //Receive Message of the Day buffer into MOTD array
std::cout << "MOTD:" << MOTD << std::endl;
}
}
// ---------- Thread selection: ---------- //
int main() {
std::thread t[num_threads];
for (int i = 0; i < num_threads; ++i) {
t[i] = std::thread(call_from_thread, i);
}
for (int i = 0; i < num_threads; ++i) {
t[i].join();
}
}
The server is listening on port 1112, but the client is connecting to port 1111. This means the client and server cannot possibly be connected to each other at all, whether they are in the same application or not.
I also see a number of other problems with your code, including:
WSAStartup() should be called once at app startup, not per thread.
Neither thread quits cleanly if any WinSock function fails.
The client thread does not wait for the server thread to open its listening port before the client can then connect to it.
Potential buffer overflows.
Resource leaks.
A lack of decent error handling.
On the server side, you are completely ignoring errors reported by socket(), bind(), listen(), and send(), and you are not checking the return value of accept() correctly.
On the client side, you are completely ignoring errors reported by socket() and recv(). And, you are not ensuring the MOTD buffer is null-terminated before printing it to std::cout.
Try something more like this:
#include <winsock2.h>
#include <windows.h>
#pragma comment(lib,"ws2_32.lib")
#include <iostream>
#include <thread>
#include <string>
#include <cstdint>
#include <stdexcept>
#include <limits>
#include <algorithm>
// The below variables are used to let the client wait for the server
// port to be opened. If you don't want to use these, especially if the
// client and server are ever run on different machines, you can omit
// these and instead just have the client call connect() in a loop
// until successful...
//
#include <mutex>
#include <condition_variable>
#include <chrono>
static std::condition_variable server_cv;
static std::mutex server_mtx;
static bool server_is_listening = false;
//
static const int num_threads = 2;
static const u_short server_port = 1111;
class winsock_error : public std::runtime_error
{
public:
int errCode;
std::string funcName;
winsock_error(int errCode, const char *funcName) : std::runtime_error("WinSock error"), errCode(errCode), funcName(funcName) {}
};
void WinSockError(const char *funcName, int errCode = WSAGetLastError())
{
throw winsock_error(errCode, funcName);
}
class connection_closed : public std::runtime_error
{
public:
connection_closed() : std::runtime_error("Connection closed") {}
};
class socket_ptr
{
public:
socket_ptr(SOCKET s) : m_sckt(s) {}
socket_ptr(const socket_ptr &) = delete;
socket_ptr(socket_ptr &&src) : m_sckt(src.m_sckt) { src.m_sckt = INVALID_SOCKET; }
~socket_ptr() { if (m_sckt != INVALID_SOCKET) closesocket(m_sckt); }
socket_ptr& operator=(const socket_ptr &) = delete;
socket_ptr& operator=(socket_ptr &&rhs) { m_sckt = rhs.m_sckt; rhs.m_sckt = INVALID_SOCKET; return *this; }
operator SOCKET() { return m_sckt; }
bool operator!() const { return (m_sckt == INVALID_SOCKET); }
private:
SOCKET m_sckt;
}
template <typename T>
T LimitBufferSize(size_t size)
{
return (T) std::min(size, (size_t) std::numeric_limits<T>::max());
}
void sendRaw(SOCKET sckt, const void *buffer, size_t buflen)
{
const char *ptr = static_cast<const char*>(buffer);
while (buflen > 0)
{
int numToSend = LimitBufferSize<int>(buflen);
int numSent = ::send(sckt, ptr, numToSend, 0);
if (numSent == SOCKET_ERROR)
WinSockError("send");
ptr += numSent;
buflen -= numSent;
}
}
void recvRaw(SOCKET sckt, void *buffer, size_t buflen)
{
char *ptr = static_cast<char*>(buffer);
while (buflen > 0)
{
int numToRecv = LimitBufferSize<int>(buflen);
int numRecvd = ::recv(sckt, ptr, numToRecv, 0);
if (numRecvd == SOCKET_ERROR)
WinSockError("recv");
if (numRecvd == 0)
throw connection_closed();
ptr += numRecvd;
buflen -= numRecvd;
}
}
void sendStr(SOCKET sckt, const std::string &str)
{
uint32_t len = LimitBufferSize<uint32_t>(str.size());
uint32_t tmp = htonl(len);
sendRaw(sckt, &tmp, sizeof(tmp));
if (len > 0)
sendRaw(sckt, str.c_str(), len);
}
std::string recvStr(SOCKET sckt)
{
std::string str;
uint32_t len;
recvRaw(sckt, &len, sizeof(len));
len = ntohl(len);
if (len > 0)
{
str.resize(len);
recvRaw(sckt, &str[0], len);
}
return str;
}
void server_thread()
{
std::cout << "[Server] Starting ..." << std::endl;
try
{
socket_ptr sListen(::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
if (!sListen)
WinSockError("socket");
SOCKADDR_IN addr = {};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = ::inet_addr("127.0.0.1");
addr.sin_port = ::htons(server_port);
int addrlen = sizeof(addr);
if (::bind(sListen, (SOCKADDR*)&addr, sizeof(addr)) == SOCKET_ERROR)
WinSockError("bind");
if (::listen(sListen, 1) == SOCKET_ERROR)
WinSockError("listen");
// this is optional...
{
std::unique_lock<std::mutex> lk(server_mtx);
server_is_listening = true;
}
server_cv.notify_all();
//
std::cout << "[Server] Listening for Client ..." << std::endl;
socket_ptr newConnection(::accept(sListen, (SOCKADDR*)&addr, &addrlen));
if (!newConnection)
WinSockError("accept");
std::cout << "[Server] Client Connected!" << std::endl;
try
{
std::string a;
while (std::cin >> a)
sendStr(newConnection, a);
}
catch (const connection_closed &)
{
}
catch (const winsock_error &e)
{
std::cerr << "[Server] Client error: " << e.errCode << " on WinSock function: " << e.funcName << std::endl;
}
std::cout << "[Server] Client Disconnected!" << std::endl;
}
catch (const winsock_error &e)
{
std::cerr << "[Server] Error: " << e.errCode << " on WinSock function: " << e.funcName << std::endl;
}
catch (const std::exception &e)
{
std::cerr << "[Server] Unexpected Error! " << e.what() << std::endl;
}
std::cout << "[Server] Stopped!" << std::endl;
}
void client_thread()
{
std::cout << "[Client] Starting ..." << std::endl;
try
{
// this is optional, could call connect() below in a loop instead...
std::cout << "[Client] Waiting for Server ..." << std::endl;
{
std::unique_lock<std::mutex> lk(server_mtx);
if (!server_cv.wait_for(lk, std::chrono::seconds(5), []{ return server_is_listening; }))
throw std::runtime_error("Server not listening after 5 seconds!");
}
//
socket_ptr sConnection(::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
if (!sConnection)
WinSockError("socket");
SOCKADDR_IN addr = {};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = ::inet_addr("127.0.0.1");
addr.sin_port = ::htons(server_port);
if (::connect(sConnection, (SOCKADDR*)&addr, sizeof(addr)) == SOCKET_ERROR)
WinSockError("connect");
std::cout << "[Client] Connected!" << std::endl;
try
{
std::string MOTD;
while (true)
{
MOTD = recvStr(sConnection);
std::cout << "[Client] MOTD: " << MOTD << std::endl;
}
}
catch (const connection_closed &)
{
}
catch (const winsock_error &e)
{
std::cerr << "[Client] Server error: " << e.errCode << " on WinSock function: " << e.funcName << std::endl;
}
std::cout << "[Client] Disconnected!" << std::endl;
}
catch (const winsock_error &e)
{
std::cerr << "[Client] Error: " << e.errCode << " on WinSock function: " << e.funcName << std::endl;
}
catch (const std::exception &e)
{
std::cerr << "[Client] Unexpected Error! " << e.what() << std::endl;
}
std::cout << "[Client] Stopped!" << std::endl;
}
int main()
{
WSADATA wsaData;
int ret = ::WSAStartup(MAKEWORD(2, 1), &wsaData);
if (ret != 0)
{
std::cerr << "WinSock Startup failed with error: " << ret << std::endl;
return -1;
}
std::cout << "WinSock Startup successful" << std::endl;
std::thread t[num_threads];
t[0] = std::thread(server_thread);
t[1] = std::thread(client_thread);
for (int i = 0; i < num_threads; ++i) {
t[i].join();
}
::WSACleanup();
return 0;
}
Hello I need to receive messages from the socket as string, I am having issues with the recv() it requires a void* but i need it in string i am fairly new to c++ any help would be greatly appreciated. the code I am using is listed below.
#include "server.h"
using namespace std;
//Actually allocate clients
vector<Client> Server::chatClients;
Server::Server() {
//Initialize the mutex
Thread::initMut();
int yes = 1;
//https://www.cs.cmu.edu/afs/cs/academic/class/15213-f99/www/class26/tcpserver.c
serverSocket = socket(AF_INET, SOCK_STREAM, 0);
memset(&serverAddr, 0, sizeof(sockaddr_in));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = INADDR_ANY;
serverAddr.sin_port = htons(PORT);
//check if socket was closed last time;
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
if(::bind(serverSocket, (struct sockaddr *) &serverAddr, sizeof(sockaddr_in)) < 0)
cerr << "Failed during the binding process";
listen(serverSocket, 5);
}
void Server::receiveAndSend() {
Client *client;
Thread *thread;
socklen_t cliSize = sizeof(sockaddr_in);
//run forever
while(true) {
client = new Client();
thread = new Thread();
client->socket = accept(serverSocket, (struct sockaddr *) &clientAddr, &cliSize);
if(client->socket < 0) {
cerr << "Error while accepting";
}
else {
thread->generate((void *) Server::ClientHandler, client);
}
}
}
//todo use one lock, and change everything to a string.
void *Server::ClientHandler(void *args) {
//client Pointer
Client *client = (Client *) args;
string buffer;
string message;
int index;
int n;
//Add client to the clients vector
Thread::lockMut((const string) client->clientName);
//set the id of the client by getting the largest index of the vector
client->setClientId(Server::chatClients.size());
client->setClientName(buffer);
cout << "Adding client with id: " << client->clientId << endl;
Server::chatClients.push_back(*client);
Thread::unlockMut((const string) client->clientName);
while(1) {
// memset(buffer, 0, sizeof buffer);
n = recv(client->socket, buffer.c_str(), sizeof buffer, 0);
//Client disconnected?
if(n == 0) {
cout << "Client " << client->clientName << " diconnected" << endl;
close(client->socket);
//Remove client in Static clients <vector> (Critical section!)
Thread::lockMut((const char *) client->clientName);
index = Server::getClientIndex(client);
cout << "earse user with index: " << index << "andid is: "
<< Server::chatClients[index].clientId << endl;
Server::chatClients.erase(Server::chatClients.begin() + index);
Thread::unlockMut((const char * ) client->clientName);
break;
}
else if(n < 0) {
cerr << "receive error: " << client->clientName << endl;
}
else {
//Message is here now broadcast.
cout << message.c_str() << " " << client->clientName << " " << buffer;
cout << "Will send to all: " << message << endl;
Server::PublicBroadcast(message);
}
}
//End thread
return NULL;
}
void Server::PublicBroadcast(string message) {
int size;
//Acquire the lock
Thread::lockMut("'PublicBroadcast()'");
for(size_t i=0; i<chatClients.size(); i++) {
// ssize_t send(int, const void *, size_t, int) __DARWIN_ALIAS_C(send);
cout << message << endl ;
size = send(Server::chatClients[i].socket, message.c_str(), message.length(),0);
cout << size << " sent bytes." << endl;
}
//Release the lock
Thread::unlockMut("'PublicBroadcast()'");
}
void Server::ListClients() {
for(size_t i=0; i<chatClients.size(); i++) {
cout << chatClients.at(i).clientName << endl;
}
}
/*
Should be called when vector<Client> clients is locked!
*/
int Server::getClientIndex(Client *client) {
for(size_t i=0; i<chatClients.size(); i++) {
if((Server::chatClients[i].clientId) == client->clientId) return (int) i;
}
cerr << "clientId not found." << endl;
return -1;
}
std::string buffer.c_str() is a constant. So it works only in this way described here: C++ Read From Socket into std::string
I'm building a ping function that, based on a destination and maximum number of hops, tries to reach the destination and report the stats of the response.
However I seem to have some difficulties decoding the received packet and extracting ttl, icmp-type and icmp-code and identification of the inner ip-packet. I'd also like to be able to set the identification of the transmitted packet, read the source port of the inner udp-packet or other means to identify packets for this function.
TL;DR
I'm struggeling and need help with:
Extracting ttl, icmp-type and icmp-code from received packet;
Extracting identification from received inner packet;
Setting identification of transmitted packet, read the source port of the inner udp-packet or other means to identify packets for this function.
Source:
#define SOCKET_ERROR -1
#include <cstdlib> //EXIT_SUCCES & EXIT_FAILURE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/ip.h> //iphdr
#include <netinet/ip_icmp.h> //icmphdr
#include <netinet/in.h> //sockaddr, sockaddr_in
#include <sys/socket.h>
#include <unistd.h>
//Additional network headers (taken from boost 1.53.0)
#include "../includes/icmp_header.hpp"
#include "../includes/ipv4_header.hpp"
int perform_ping(const char *destination, int max_hops) {
int msg_size = 32;
int transmission_socket;
if ((transmission_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == SOCKET_ERROR) {
printf("Failed to setup transmission socket.\n");
return EXIT_FAILURE;
}
int receiver_socket;
if ((receiver_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)) == SOCKET_ERROR) { //Non-Priviledged
printf("Failed to setup receiver socket.\n");
return EXIT_FAILURE;
}
struct timeval timeout;
timeout.tv_sec = 3;
timeout.tv_usec = 0;
if (setsockopt(receiver_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) == SOCKET_ERROR) {
printf("Failed to setup receiver socket with timeout: %ld.%06d\n", timeout.tv_sec, timeout.tv_usec);
return EXIT_FAILURE;
}
struct sockaddr_in destination_address;
destination_address.sin_addr.s_addr = inet_addr(destination);
destination_address.sin_family = AF_INET;
destination_address.sin_port = htons(33434);
if (setsockopt(transmission_socket, IPPROTO_IP, IP_TTL, (char *)&max_hops, sizeof(max_hops)) == SOCKET_ERROR) {
printf("Failed to setup transmission socket with max_hops: %d\n", max_hops);
return EXIT_FAILURE;
}
char *transmission_buffer = NULL;
transmission_buffer = new char [sizeof (icmp) + msg_size];
int bytes_send = sendto(transmission_socket, transmission_buffer, sizeof(icmp) + msg_size, 0, (struct sockaddr *)&destination_address, sizeof(struct sockaddr_in));
if (bytes_send == SOCKET_ERROR) {
printf("An error has occured while sending: %s.", strerror(errno));
delete transmission_buffer;
return EXIT_FAILURE;
}
char *response_buffer;
if ((response_buffer = (char *)malloc(sizeof(struct ip) + sizeof(struct icmp))) == NULL) {
fprintf(stderr, "Could not allocate memory for packet\n");
return EXIT_FAILURE;
}
struct sockaddr remoteAddr;
socklen_t remoteAddrLen = sizeof(remoteAddr);
int bytes_received = recvfrom(receiver_socket, response_buffer, sizeof(ip) + sizeof(icmp), 0, &remoteAddr, &remoteAddrLen);
if (bytes_received != SOCKET_ERROR) {
printf("%s (%d)\n", inet_ntoa(((struct sockaddr_in *)&remoteAddr)->sin_addr), max_hops);
struct ip *ipheader = (struct ip *)response_buffer;
struct icmp *icmpheader = (struct icmp *)(response_buffer + sizeof(struct ip));
std::cout << "ip_ttl : " << std::dec << ipheader->ip_ttl << std::endl;
std::cout << "ip_ttl(hex): " << std::hex << ipheader->ip_ttl << std::endl;
std::cout << "ip_id : " << std::dec << ntohs(ipheader->ip_id) << std::endl;
std::cout << "ip_id(hex): " << std::hex << ntohs(ipheader->ip_id) << std::endl;
std::cout << "icmp_id : " << std::dec << ntohs(icmpheader->icmp_hun.ih_idseq.icd_id) << std::endl;
std::cout << "icmp_id(hex): " << std::hex << icmpheader->icmp_hun.ih_idseq.icd_id << std::endl;
std::cout << "icmp_type : " << std::dec << icmpheader->icmp_type << std::endl;
std::cout << "icmp_type(hex): " << std::hex << icmpheader->icmp_type << std::endl;
std::cout << "icmp_code : " << std::dec << icmpheader->icmp_code << std::endl;
std::cout << "icmp_code(hex): " << std::hex << icmpheader->icmp_code << std::endl;
} else {
printf("Socket error\n");
free(response_buffer);
return EXIT_FAILURE;
}
delete transmission_buffer;
free(response_buffer);
return EXIT_SUCCESS;
}
Received packet
ip (
identification: 0xe0ab (57515),
time to live: 250
),
icmp (
type: 11
code: 0
ip:(
identification: 0x7ec7 (32455),
time to live: 1
),
udp:(
source port: 56086
)
)
Expected output
90.145.29.174 (5)
ip_ttl : 250
ip_id : 32455
ip_id(hex): 7ec7
icmp_type: 11
icmp_code: 0
Actual output
90.145.29.174 (5)
ip_ttl : ? (actual question mark)
ip_ttl(hex): ? (action question mark)
ip_id : 57515
ip_id(hex): e0ab
icmp_type :
(newline)
icmp_type(hex):
(newline)
icmp_code :
icmp_code(hex):
PS: This is my first post, please forgive any mistakes :)