I use libevent to write client-server application, on client side i want to continiusly wait for intput from console. I tried to run event_base_dispatch in thread, and in main thread ask for input string and add it to bufferevent.
std::thread libevthr(libev_start, base);
std::string s;
do
{
cin >> s;
bufferevent_write(bev, "Hello, world!", 13);
} while(s != "xxx");
libevthr.join();
For some reason this doesn't work, but if i put bufferevent_write inside one of callbacks, it works fine
void event_cb(struct bufferevent *bev, short events, void *ptr)
{
if (events & BEV_EVENT_CONNECTED) {
/* We're connected to 127.0.0.1 Ordinarily we'd do
something here, like start reading or writing. */
bufferevent_write(bev, "Hello, world!", 13);
std::cout << "Connected" << std::endl;
}
if (events & BEV_EVENT_ERROR) {
if (EVUTIL_SOCKET_ERROR() == 10054)
cout << "Server stopped working!" << endl;
else
cout << "Error has happened" << endl;
}
if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR))
{
bufferevent_free(bev);
}
}
Can you explain how should i write this correctly?
Sorry if i have any mistakes in english.
Full code here:
#include "UClient.h"
#include <iostream>
#include "event2/event.h"
#include "event2/listener.h"
#include "event2/bufferevent.h"
#include "event2/buffer.h"
#include <thread>
using std::cout, std::cin, std::endl;
void event_cb(struct bufferevent *bev, short events, void *ptr)
{
if (events & BEV_EVENT_CONNECTED) {
/* We're connected to 127.0.0.1 Ordinarily we'd do
something here, like start reading or writing. */
bufferevent_write(bev, "Hello, world!", 13);
std::cout << "Connected" << std::endl;
}
if (events & BEV_EVENT_ERROR) {
if (EVUTIL_SOCKET_ERROR() == 10054)
cout << "Server stopped working!" << endl;
else
cout << "Error has happened" << endl;
}
if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR))
{
bufferevent_free(bev);
}
}
void write_cb(struct bufferevent *bev, void *ctx)
{
cout << 'Data was written' << endl;
}
void libev_start(event_base *base)
{
event_base_dispatch(base);
}
int main()
{
int port = 9554;
struct event_base *base;
struct bufferevent *bev;
struct sockaddr_in cl_inf;
if (!initWinsock()) {
perror("Failed to initialize Winsock");
return 1;
}
base = event_base_new();
ZeroMemory(&cl_inf, sizeof(cl_inf));
in_addr serv_ip;
inet_pton(AF_INET, "127.0.0.1", &serv_ip);
cl_inf.sin_family = AF_INET;
cl_inf.sin_addr = serv_ip;
cl_inf.sin_port = htons(port);
bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bev, NULL, write_cb, event_cb, NULL);
if (bufferevent_socket_connect(bev,
(struct sockaddr *)&cl_inf, sizeof(cl_inf)) < 0) {
/* Error starting connection */
std::cout << "Can't connect to server!" << std::endl;
bufferevent_free(bev);
return -1;
}
bufferevent_enable(bev, EV_READ | EV_WRITE);
std::thread libevthr(libev_start, base);
std::string s;
do
{
cin >> s;
} while(s != "xxx");
libevthr.join();
std::cout << "client finished working";
return 0;
}
Related
Able to send UDP message to a particular IP port using Poco Lib socket communication, But unable to receive the UDP message as it is getting stuck at receiveFrom API of DatagramSocket as in below code.
I am sending message every second and also have to receive acknowledgement every second, for that i have timer , Client and Server Threads running parallelly. The problem here is I am unable to receive the UDP packets which are being captured on wireshark. It is getting stuck at receiveFrom.
Please find below Client Server and main files.
` Server.hpp
#pragma once
#include "Poco/Net/StreamSocket.h"
#include "Poco/Net/DatagramSocket.h"
#include "Poco/Net/SocketAddress.h"
#include "Poco/Net/MulticastSocket.h"
#include "Poco/RunnableAdapter.h"
#include "Poco/Thread.h"
#include <cstring>
#include <iostream>
using namespace std;
using namespace Poco;
using namespace Poco::Net;
struct Server
{
int bufferSize;
SocketAddress sockets;
static bool debugModeEnabled;
Server() :
bufferSize(1024) { //sockets = SocketAddress(10000);
}
Server(const UInt16& port, const int& bufferSize)
{
sockets = SocketAddress(port);
this->bufferSize = bufferSize;
}
void receiveMessages()
{
char buffer[bufferSize];
try
{
Poco::Net::DatagramSocket datagram(sockets);//(socket);
datagram.bind(sockets);
cout << "Server started socket" << endl;
while (!datagram.available())
{
SocketAddress sender;
cout << "Server started socket 2" << endl;
int size = datagram.receiveFrom(buffer, bufferSize, sender);
//int size = datagram.receiveBytes(buffer, bufferSize);
cout << "received bytes size" << size << endl;
buffer[size] = '\0';
//std::string str(buffer);
//cout << (debugModeEnabled ? (sender.toString() + ": ") : "- ") << buffer << endl;
cout << "received: " << size << buffer << endl;
//cout << buffer << "Server adasdasd" << endl;
if (string(buffer) == "\\end")
{
//cerr << "\nUser: " << sender.toString() << " ended connection" << endl;
datagram.close(); // Closes the server
}
}
}
catch (const Poco::Exception& exc)
{
std::cerr << exc.displayText() << std::endl;
}
}
};
bool Server::debugModeEnabled = false;
`
`Client.hpp
#pragma once
#include "Poco/Net/DatagramSocket.h"
#include "Poco/Net/SocketAddress.h"
#include "Poco/RunnableAdapter.h"
#include "Poco/Thread.h"
#include <iostream>
#include <string>
using namespace std;
using namespace Poco;
using namespace Poco::Net;
struct Client
{
SocketAddress socket;
string str;
// By default the client connects to itself
Client() { socket = SocketAddress("127.0.0.1", 10000); }
Client(const Poco::Net::IPAddress& IP, const UInt16& port, const string& val) :
str(val)
{
socket = SocketAddress(IP, port);
}
void sendMessages()
{
DatagramSocket datagram;
datagram.connect(socket);
string message = str;
//cout << "sending: " << hex << hexify(message) << endl;
unsigned int bytes_sent = 0;
while (!datagram.available())
{
//getline(cin, message);
//bytes_sent = datagram.sendBytes(message.data(), static_cast<int>(message.size()));
bytes_sent = datagram.sendTo(message.data(), static_cast<int>(message.size()),socket);
cout << "number of bytes sent: " << std::dec << bytes_sent << endl;
if (bytes_sent >= message.size())
{
datagram.close();
}
}
}
string IP() { return socket.host().toString(); }
UInt16 port() { return socket.port(); }
static void sendMessage(const Poco::Net::IPAddress& IP, const UInt16& port, const string& message)
{
SocketAddress socket(IP, port);
DatagramSocket datagram;
datagram.connect(socket);
datagram.sendBytes(message.data(), int(message.size()));
}
};
`
` main.cpp
int bufferSize = 1024;
int exit_status = 0;
Client client(IP, ciPort, str);
Server server(mdilPort, bufferSize);
RunnableAdapter<Client> clientRunnable(client, &Client::sendMessages);
RunnableAdapter<Server> serverRunnable(server, &Server::receiveMessages);
Thread clientThread, serverThread;
// Client::sendMessage(IP, ciPort, "hello!!");
try
{
Timer t = Timer();
t.setInterval([&]() {
cout << "client Tick" << endl;
// pApp->SendIndications();
clientThread.start(clientRunnable);
clientThread.join();
},
1000);
t.setInterval([&]() {
cout<< "server Tick" << endl;
serverThread.start(serverRunnable);
serverThread.join();
},
1000);
t.setTimeout([&]() {
std::cout << "Hey.. After 30s. But I will stop the timer!" << std::endl;
t.stop();
exit(exit_status);
},
30000);
std::cout << "I am Timer" << std::endl;
while (true); // Keep main thread active
}
catch (...)
{
std::cout << "catched exception" << std::endl;
//return -1;
}
`
I tried the conventional Socket Programming API's to receive the UDP packets but there also it is getting stuck at receiveFrom API. also tried running both client and server on different process to make sure there is no issue with the multi threading synchronization, but both the approach didnt help. I am able to capture the response at Wireshark but unable to receive on the application side using Poco Lib socket API's. Also allowed visual studio code through firewall as well
I'm writing a multithreaded sockets applications (for windows), however, I have a problem when connecting multiple clients. I can send messages from the server to the clients, however, I can only send messages from one client to the server. The other clients can't send messages to the server. I've googled some stuff and found that Overlapping/Asynchronous I/O is the way to go. There's just one problem with that, I don't know how to implement that, so I'm basically asking how I would go about doing this or if this approach is wrong.
The RecieveFromClients() function is what I want to make Asynchronous.
Thanks in advance.
Here is my code:
main.cpp
#include "server.h"
int main()
{
Server server;
server.StartServer();
return 0;
}
Server.h
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <iphlpapi.h>
#include <stdio.h>
#include <string>
#include <iostream>
#include <vector>
#include <thread>
#pragma comment(lib, "Ws2_32.lib")
#define DEFAULT_PORT 4566
#define BACKLOG 10
class Server
{
public:
Server();
~Server();
int StartServer();
private:
int Socket();
int Bind();
int Listen();
void AcceptConnections();
int StopServer();
int CloseClientSocket(int client_num);
void GetClients(int server_socket, std::vector<int>* clients,
std::vector<sockaddr_in> *client_addrs);
void SendToClients();
void RecieveFromClients(int id);
private:
sockaddr_in server_addr;
int connected_clients, counted_clients;
int server_socket;
int result;
int msgSize;
std::vector<int> clients;
std::vector<sockaddr_in> client_addrs;
private:
std::thread get_clients;
std::thread send_messages;
std::thread recieve_messages;
};
Server.cpp
#include "server.h"
Server::Server()
:
connected_clients(0),
counted_clients(0),
server_socket(0),
result(0)
{
ZeroMemory(&server_addr, 0);
WSAData wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
printf("wsastartip falied\n");
}
}
Server::~Server()
{
WSACleanup();
}
int Server::StartServer()
{
if (Socket() != 0)
{
return 1;
}
if (Bind() != 0)
{
return 1;
}
if (Listen() != 0)
{
return 1;
}
AcceptConnections();
return 0;
}
int Server::Socket()
{
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket == INVALID_SOCKET)
{
std::cout << "Failed to create socket" << std::endl;
return 1;
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(DEFAULT_PORT);
server_addr.sin_addr.S_un.S_addr = INADDR_ANY;
return 0;
}
int Server::Bind()
{
result = bind(server_socket, (sockaddr*)&server_addr,
sizeof(server_addr));
if (result == SOCKET_ERROR)
{
std::cout << "Failed to bind socket" << std::endl;
return 1;
}
return 0;
}
int Server::Listen()
{
result = listen(server_socket, BACKLOG);
if (result == SOCKET_ERROR)
{
std::cout << "Listening failed" << std::endl;
return 1;
}
return 0;
}
void Server::AcceptConnections()
{
get_clients = std::thread(&Server:: GetClients, this, server_socket,
&clients, &client_addrs);
send_messages = std::thread(&Server::SendToClients, this);
recieve_messages = std::thread(&Server::RecieveFromClients, this, counted_clients);
get_clients.join();
send_messages.join();
recieve_messages.join();
}
int Server::StopServer()
{
std::terminate();
for (int client : clients)
{
result = closesocket(client);
if (result == SOCKET_ERROR)
{
std::cout << "Failed to close client socket" << std::endl;
return 1;
}
}
return 0;
}
int Server::CloseClientSocket(int client_num)
{
result = closesocket(clients[client_num]);
if (result == SOCKET_ERROR)
{
std::cout << "Failed to close client socket" << std::endl;
return 1;
}
return 0;
}
void Server::GetClients(int server_socket, std::vector<int>* clients,
std::vector<sockaddr_in>* client_addrs)
{
while(true)
{
sockaddr_in client_addr = { 0 };
socklen_t client_addrstrlen = sizeof(client_addr);
int client;
client = accept(server_socket, (sockaddr*)&client_addr,
&client_addrstrlen);
clients->push_back(client);
client_addrs->push_back(client_addr);
++connected_clients;
char ip[INET_ADDRSTRLEN] = "";
char port[100] = "";
inet_ntop(AF_INET, &client_addr.sin_addr, ip, sizeof(ip));
std::cout << "Client connected from " << ip << ":" <<
client_addr.sin_port << std::endl;
}
}
void Server::SendToClients()
{
std::string msg;
do
{
msg.clear();
getline(std::cin, msg);
if (msg.size() > 255)
{
std::cout << "Message must be less than 256 bytes"
<< std::endl;
continue;
}
for (int client : clients)
{
int size;
size = send(client, msg.data(), msg.size(), 0);
if (size == SOCKET_ERROR)
{
std::cout << "Failed to send message to client"
<< std::endl;
}
}
} while (msg != "exit");
if (StopServer() != 0)
{
std::cout << "Failed to close client sockets" << std::endl;
}
}
void Server::RecieveFromClients(int id)
{
std::vector<char> msgBuffer(256);
do
{
if (connected_clients > 0)
{
msgBuffer.clear();
msgBuffer.resize(256);
char ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &client_addrs[id].sin_addr, ip, sizeof(ip));
if (msgSize = recv(clients[id], msgBuffer.data(),
msgBuffer.size(), 0) > 0)
{
std::cout << ip << ": ";
for (char c : msgBuffer)
{
if (c != 0)
{
std::cout << c;
}
}
std::cout << std::endl;
}
else
{
if (msgSize == SOCKET_ERROR)
{
std::cout << "Failed to recieve data" << std::endl;
break;
}
else if (clients[id] > 0)
{
std::cout << "Client " << ip << " has disconnected" << std::endl;
CloseClientSocket(0);
break;
}
}
}
else
{
continue;
}
} while (true);
}
Using Overlapped I/O will be a fairly large change to the design of your code. But fortunately, there is a simpler solution. In your RecieveFromClients() method, you can use select() to determine which client sockets actually have pending data to read before you attempt to read from them. You are using blocking sockets, so a call to recv() will block the calling thread until data is received, so you don’t want to perform a blocking read until you are actually ready to read something.
Also, since you are not creating a new thread for each accepted client, the id parameter of RecieveFromClients(), and the client_num parameter of CloseClientSocket(), are being used incorrectly and should just be removed completely. The receive function should run a loop over the list of connected clients. And the close function should take the specific socket handle to close.
That being said, another major design problem with your code is that you are sharing variables and containers across multiple threads, but you are not synchronizing access to any of them. That will cause you big problems in the long run.
I am coding simple networking system based on sfml-network liblary and tcp sockets.
When i compile and run my program i am getting that output on screen(screenshot).
It looks like selector.wait() (Server.cpp:20) is not waiting for any packet and selector.isReady(TCPSOCKET) (Server.cpp:43) is not checking correctly if client is sending package to server.
Code:
main.cpp
#include <iostream>
#include "Server.h"
int main()
{
int mode = 0;
std::cin >> mode;
if(mode == 0)
{
Server server(55200);
}else if(mode == 1){
sf::TcpSocket socket;
if (socket.connect("localhost", 55200) != sf::Socket::Done)
{
std::cout << "Error1\n";
}
//Sleep(2000);
sf::Packet packet;
packet << 11;
if (socket.send(packet) != sf::Socket::Done)
{
std::cout << "Error2\n";
}
}
std::cin.get();
std::cin.get();
}
Server.h
#pragma once
#include <SFML/Network.hpp>
#include <vector>
class Server
{
private:
sf::TcpListener listener;
std::vector<sf::TcpSocket*> clients;
sf::SocketSelector selector;
unsigned short port;
public:
Server(unsigned short port);
~Server();
};
Server.cpp
#include "Server.h"
#include <iostream>
#include <SFML/Network.hpp>
#include <vector>
Server::Server(unsigned short pport)
{
port = pport;
std::cout << "Starting Server....\n";
if (listener.listen(port) != sf::Socket::Done)
{
std::cout << "Failed while starting listening on port: " << port << std::endl;
return;
}
selector.add(listener);
while(true)
{
if (selector.wait())
{
//new connection
if (selector.isReady(listener))
{
std::cout << "New connection!!\n";
sf::TcpSocket* tmp = new sf::TcpSocket; // !!!!!!!!!!!
if (listener.accept(*tmp) != sf::Socket::Done)
{
std::cout << "Error while accepting new connection\n";
delete tmp;
}
else {
selector.add(*tmp);
clients.push_back(tmp);
}
}
else {
for (int i = 0; i < clients.size(); i++)
{
//new incoming packet
if(selector.isReady(*(clients[i])))
{
sf::Packet pakiet;
if (clients[i]->receive(pakiet) != sf::Socket::Done)
{
std::cout << "Error while receiving packet\n";
}
else {
int x;
pakiet >> x;
std::cout << "Recived new data!!!: " << x << std::endl;
}
}
}
}
}
}
}
Server::~Server()
{
for (int i = 0; i < clients.size();i++)
{
delete clients[i];
}
}
You created a client which connected to the server, sent one package and at the end of the scope was destroyed. Socket at the client side doesn't exist and the connection between client-server is closed. So, how do you want to get the information about closed connection at server side ?
Function isReady returns true, then you call receive for this socket and as output you get one of Status codes: Done, NotReady, Disconnected, Error. You need to check if status is Disconnected, if so, the client socket should be removed from selector.
if(selector.isReady(*(clients[i])))
{
sf::Packet pakiet;
if ( clients[i]->receive(pakiet) == sf::Socket::Done)
{
// process data
}
else if ( clients[i]->receive(pakiet) == sf::Socket::Disconnected ) {
// delete socket, remove from selector
}
else {
//
}
}
I have no idea why send data is 48 bytes 010,0,0..., someone can explain? the problem is buffer for data received, I don't know how big he should be, and even if I receive data, how to make normal time from it?
Here's the code:
#include <iostream>
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define WIN32_MEAN_AND_LEAN
#include <winsock2.h>
#include <windows.h>
#include <time.h>
using namespace std;
class HRException
{
public:
HRException() :
m_pMessage("") {}
virtual ~HRException() {}
HRException(const char *pMessage) :
m_pMessage(pMessage) {}
const char * what() { return m_pMessage; }
private:
const char *m_pMessage;
};
const int REQ_WINSOCK_VER = 2; // Minimum winsock version required
const char DEF_SERVER_NAME[] = "0.pl.pool.ntp.org";
const int SERVER_PORT = 123;
const int TEMP_BUFFER_SIZE = 128;
const char msg[48] = { 010,0,0,0,0,0,0,0,0 };
// IP number typedef for IPv4
typedef unsigned long IPNumber;
IPNumber FindHostIP(const char *pServerName)
{
HOSTENT *pHostent;
// Get hostent structure for hostname:
if (!(pHostent = gethostbyname(pServerName)))
throw HRException("could not resolve hostname.");
// Extract primary IP address from hostent structure:
if (pHostent->h_addr_list && pHostent->h_addr_list[0])
return *reinterpret_cast<IPNumber*>(pHostent->h_addr_list[0]);
return 0;
}
void FillSockAddr(sockaddr_in *pSockAddr, const char *pServerName, int portNumber)
{
// Set family, port and find IP
pSockAddr->sin_family = AF_INET;
pSockAddr->sin_port = htons(portNumber);
pSockAddr->sin_addr.S_un.S_addr = FindHostIP(pServerName);
}
bool RequestHeaders(const char *pServername)
{
SOCKET hSocket = INVALID_SOCKET;
char tempBuffer[TEMP_BUFFER_SIZE];
sockaddr_in sockAddr = { 0 };
bool bSuccess = true;
try
{
// Lookup hostname and fill sockaddr_in structure:
cout << "Looking up hostname " << pServername << "... ";
FillSockAddr(&sockAddr, pServername, SERVER_PORT);
cout << "found.\n";
// Create socket
cout << "Creating socket... ";
if ((hSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)
throw HRException("could not create socket.");
cout << "created.\n";
// Connect to server
cout << "Attempting to connect to " << inet_ntoa(sockAddr.sin_addr)
<< ":" << SERVER_PORT << "... ";
if (connect(hSocket, reinterpret_cast<sockaddr*>(&sockAddr), sizeof(sockAddr)) != 0)
throw HRException("could not connect.");
cout << "connected.\n";
cout << "Sending request... ";
// send request part 1
if (send(hSocket, msg, sizeof(msg) , 0) == SOCKET_ERROR)
throw HRException("failed to send data.");
cout << "request sent.\n";
cout << "Dumping received data...\n\n";
// Loop to print all data
recv(hSocket, tempBuffer, sizeof(tempBuffer), 0); // <-- the problem
///
//part where we take time out of tempBuffer
///
}
catch (HRException e)
{
cerr << "\nError: " << e.what() << endl;
bSuccess = false;
}
if (hSocket != INVALID_SOCKET)
{
closesocket(hSocket);
}
return bSuccess;
}
int main(int argc, char* argv[])
{
int iRet = 1;
WSADATA wsaData;
cout << "Initializing winsock... ";
if (WSAStartup(MAKEWORD(REQ_WINSOCK_VER, 0), &wsaData) == 0)
{
// Check if major version is at least REQ_WINSOCK_VER
if (LOBYTE(wsaData.wVersion) >= REQ_WINSOCK_VER)
{
cout << "initialized.\n";
// Set default hostname:
const char *pHostname = DEF_SERVER_NAME;
// Set custom hostname if given on the commandline:
if (argc > 1)
pHostname = argv[1];
iRet = !RequestHeaders(pHostname);
}
else
{
cerr << "required version not supported!";
}
cout << "Cleaning up winsock... ";
// Cleanup winsock
if (WSACleanup() != 0)
{
cerr << "cleanup failed!\n";
iRet = 1;
}
cout << "done.\n";
}
else
{
cerr << "startup failed!\n";
}
int x;
cin >> x;
return iRet;
}
Most part of code is from madwizard.org
Ok it works, main part of code:
const char msg[48] = { 010,0,0,0,0,0,0,0,0 };
if (send(hSocket, msg, sizeof(msg) , 0) == SOCKET_ERROR)
throw HRException("failed to send data.");
cout << "request sent.\n";
cout << "Dumping received data...\n\n";
char tempBuffer[1024];
int bytes = recv(hSocket, tempBuffer, sizeof(tempBuffer), 0);
cout << "bytes received: " << bytes << endl;
time_t tmit;
tmit = ntohl(((time_t*)tempBuffer)[4]);
tmit -= 2208988800U;
cout << ctime(&tmit);
No idea why data that we send is
msg[48] = { 010,0,0,0,0,0,0,0,0 };
and why received data contains many numbers? for example if change code to
tmit = ntohl(((time_t*)tempBuffer)[6]);
I will get date 2008y, why?
Guys why so many minuses?, still waiting for an explanation :D
Here's whole code http://pastebin.com/Sv3ERGfV , dont forget to link ws2_32.lib
Similar to my issue when trying to query the time from a self-hostet Windows-NTP-Server with the C++ library NTPClient which uses boost for the network tasks, msg[48] = { 010,0,0,0,0,0,0,0,0 }; configures the ntp.flags.mode. After comparing the network traffic of w32tm /stripchart /computer:10.159.96.65 using Wireshark, flag 27 or 11 seem to be the choices for my usecase:
Comparison of NTP network packages
tmit = ntohl(((time_t*)tempBuffer)[6]); extracts the data from the received package. It looks like
4 yields the reference time (last sync with timeserver I assume),
8 the time when server received request and
10 the transmit time (which should be almost equal).
My question is how to set up a WebSocket++ server and create a WebSocket++ client that connects to this server in the same program or function? (for test purpose)
Details:
I would like to use library WebSocket++ in my C++ program to stream data on a websocket. I have a websocket client that sends data to an extern websocket server.
As a good programmer, I try to write some tests to check everything is fine. Therefore I want to setup a WebSocket++ server to test the data I send from the WebSocket++ client.
From the examples, I have managed to create a server in a program and a client in another program. It works like a charm. Problem arises when I try to put the server and the client code in the same program (code is given below): The client can not connect to server, and leads to a timeout handshake.
I guess it is an ASIO problem or a thread problem, but I have no idea how to deal with it.
From the classical example I met, I had to replace echo_server.start() with echo_server.poll(), to have a non stop blocking process. It is not blocking but it prevents the client from connecting to server.
Any advise on how to solve this would be of great help!!
Should I use thread or anything else?
Below is the program I try to get running, where I want the client to connect to the server.
It is based on the merge of tutorials found here and here
#include <websocketpp/config/asio_no_tls_client.hpp>
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/client.hpp>
#include <websocketpp/server.hpp>
#include <websocketpp/common/thread.hpp>
#include <websocketpp/common/memory.hpp>
#include <cstdlib>
#include <iostream>
#include <map>
#include <string>
#include <sstream>
typedef websocketpp::server<websocketpp::config::asio> server;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
// pull out the type of messages sent by our config
typedef server::message_ptr message_ptr;
// Define a callback to handle incoming messages
void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg);
void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg)
{
std::cout << "on_message called with hdl: " << hdl.lock().get()
<< " and message: " << msg->get_payload()
<< std::endl;
try {
s->send(hdl, msg->get_payload(), msg->get_opcode());
} catch (const websocketpp::lib::error_code& e) {
std::cout << "Echo failed because: " << e
<< "(" << e.message() << ")" << std::endl;
}
}
typedef websocketpp::client<websocketpp::config::asio_client> client;
class connection_metadata {
public:
typedef websocketpp::lib::shared_ptr<connection_metadata> ptr;
connection_metadata(int id, websocketpp::connection_hdl hdl, std::string uri)
: m_id(id)
, m_hdl(hdl)
, m_status("Connecting")
, m_uri(uri)
, m_server("N/A")
, m_error_reason("")
,m_messages()
{}
void on_open(client * c, websocketpp::connection_hdl hdl) {
m_status = "Open";
client::connection_ptr con = c->get_con_from_hdl(hdl);
m_server = con->get_response_header("Server");
}
void on_fail(client * c, websocketpp::connection_hdl hdl) {
m_status = "Failed";
client::connection_ptr con = c->get_con_from_hdl(hdl);
m_server = con->get_response_header("Server");
m_error_reason = con->get_ec().message();
}
void on_close(client * c, websocketpp::connection_hdl hdl) {
m_status = "Closed";
client::connection_ptr con = c->get_con_from_hdl(hdl);
std::stringstream s;
s << "close code: " << con->get_remote_close_code() << " ("
<< websocketpp::close::status::get_string(con->get_remote_close_code())
<< "), close reason: " << con->get_remote_close_reason();
m_error_reason = s.str();
}
void on_message(websocketpp::connection_hdl, client::message_ptr msg) {
if (msg->get_opcode() == websocketpp::frame::opcode::text) {
m_messages.push_back("<< " + msg->get_payload());
} else {
m_messages.push_back("<< " + websocketpp::utility::to_hex(msg->get_payload()));
}
}
websocketpp::connection_hdl get_hdl() const {
return m_hdl;
}
int get_id() const {
return m_id;
}
std::string get_status() const {
return m_status;
}
void record_sent_message(std::string message) {
m_messages.push_back(">> " + message);
}
friend std::ostream & operator<< (std::ostream & out, connection_metadata const & data);
private:
int m_id;
websocketpp::connection_hdl m_hdl;
std::string m_status;
std::string m_uri;
std::string m_server;
std::string m_error_reason;
std::vector<std::string> m_messages;
};
std::ostream & operator<< (std::ostream & out, connection_metadata const & data) {
out << "> URI: " << data.m_uri << "\n"
<< "> Status: " << data.m_status << "\n"
<< "> Remote Server: " << (data.m_server.empty() ? "None Specified" : data.m_server) << "\n"
<< "> Error/close reason: " << (data.m_error_reason.empty() ? "N/A" : data.m_error_reason) << "\n";
out << "> Messages Processed: (" << data.m_messages.size() << ") \n";
std::vector<std::string>::const_iterator it;
for (it = data.m_messages.begin(); it != data.m_messages.end(); ++it) {
out << *it << "\n";
}
return out;
}
class websocket_endpoint {
public:
websocket_endpoint () : m_endpoint(), m_thread(), m_connection_list(), m_next_id(0)
{
m_endpoint.clear_access_channels(websocketpp::log::alevel::all);
m_endpoint.clear_error_channels(websocketpp::log::elevel::all);
m_endpoint.init_asio();
m_endpoint.start_perpetual();
m_thread = websocketpp::lib::make_shared<websocketpp::lib::thread>(&client::run, &m_endpoint);
}
~websocket_endpoint() {
m_endpoint.stop_perpetual();
for (con_list::const_iterator it = m_connection_list.begin(); it != m_connection_list.end(); ++it) {
if (it->second->get_status() != "Open") {
// Only close open connections
continue;
}
std::cout << "> Closing connection " << it->second->get_id() << std::endl;
websocketpp::lib::error_code ec;
m_endpoint.close(it->second->get_hdl(), websocketpp::close::status::going_away, "", ec);
if (ec) {
std::cout << "> Error closing connection " << it->second->get_id() << ": "
<< ec.message() << std::endl;
}
}
m_thread->join();
}
int connect(std::string const & uri) {
websocketpp::lib::error_code ec;
client::connection_ptr con = m_endpoint.get_connection(uri, ec);
if (ec) {
std::cout << "> Connect initialization error: " << ec.message() << std::endl;
return -1;
}
int new_id = m_next_id++;
connection_metadata::ptr metadata_ptr = websocketpp::lib::make_shared<connection_metadata>(new_id, con->get_handle(), uri);
m_connection_list[new_id] = metadata_ptr;
con->set_open_handler(websocketpp::lib::bind(
&connection_metadata::on_open,
metadata_ptr,
&m_endpoint,
websocketpp::lib::placeholders::_1
));
con->set_fail_handler(websocketpp::lib::bind(
&connection_metadata::on_fail,
metadata_ptr,
&m_endpoint,
websocketpp::lib::placeholders::_1
));
con->set_close_handler(websocketpp::lib::bind(
&connection_metadata::on_close,
metadata_ptr,
&m_endpoint,
websocketpp::lib::placeholders::_1
));
con->set_message_handler(websocketpp::lib::bind(
&connection_metadata::on_message,
metadata_ptr,
websocketpp::lib::placeholders::_1,
websocketpp::lib::placeholders::_2
));
m_endpoint.connect(con);
return new_id;
}
void close(int id, websocketpp::close::status::value code, std::string reason) {
websocketpp::lib::error_code ec;
con_list::iterator metadata_it = m_connection_list.find(id);
if (metadata_it == m_connection_list.end()) {
std::cout << "> No connection found with id " << id << std::endl;
return;
}
m_endpoint.close(metadata_it->second->get_hdl(), code, reason, ec);
if (ec) {
std::cout << "> Error initiating close: " << ec.message() << std::endl;
}
}
void send(int id, std::string message) {
websocketpp::lib::error_code ec;
con_list::iterator metadata_it = m_connection_list.find(id);
if (metadata_it == m_connection_list.end()) {
std::cout << "> No connection found with id " << id << std::endl;
return;
}
m_endpoint.send(metadata_it->second->get_hdl(), message, websocketpp::frame::opcode::text, ec);
if (ec) {
std::cout << "> Error sending message: " << ec.message() << std::endl;
return;
}
metadata_it->second->record_sent_message(message);
}
connection_metadata::ptr get_metadata(int id) const {
con_list::const_iterator metadata_it = m_connection_list.find(id);
if (metadata_it == m_connection_list.end()) {
return connection_metadata::ptr();
} else {
return metadata_it->second;
}
}
private:
typedef std::map<int,connection_metadata::ptr> con_list;
client m_endpoint;
websocketpp::lib::shared_ptr<websocketpp::lib::thread> m_thread;
con_list m_connection_list;
int m_next_id;
};
int main() {
bool done = false;
std::string input;
websocket_endpoint endpoint;
server echo_server;
// Set logging settings
echo_server.set_access_channels(websocketpp::log::alevel::all);
echo_server.clear_access_channels(websocketpp::log::alevel::frame_payload);
// Initialize ASIO
echo_server.init_asio();
// Register our message handler
echo_server.set_message_handler(bind(&on_message,&echo_server,::_1,::_2));
// Listen on port 9002
echo_server.listen(9002);
// Start the server accept loop
echo_server.start_accept();
// Start the ASIO io_service run loop
echo_server.poll();
// echo_server.run();
//thread t(bind(&WSServer::poll,echo_server));
//t.detach();
while (!done) {
std::cout << "Enter Command: ";
std::getline(std::cin, input);
if (input == "quit") {
done = true;
} else if (input == "help") {
std::cout
<< "\nCommand List:\n"
<< "connect <ws uri>\n"
<< "send <connection id> <message>\n"
<< "close <connection id> [<close code:default=1000>] [<close reason>]\n"
<< "show <connection id>\n"
<< "help: Display this help text\n"
<< "quit: Exit the program\n"
<< std::endl;
} else if (input.substr(0,7) == "connect") {
int id = endpoint.connect(input.substr(8));
if (id != -1) {
std::cout << "> Created connection with id " << id << std::endl;
}
} else if (input.substr(0,4) == "send") {
std::stringstream ss(input);
std::string cmd;
int id;
std::string message = "";
ss >> cmd >> id;
std::getline(ss,message);
endpoint.send(id, message);
} else if (input.substr(0,5) == "close") {
std::stringstream ss(input);
std::string cmd;
int id;
int close_code = websocketpp::close::status::normal;
std::string reason = "";
ss >> cmd >> id >> close_code;
std::getline(ss,reason);
endpoint.close(id, (websocketpp::close::status::value)close_code, reason);
} else if (input.substr(0,4) == "show") {
int id = atoi(input.substr(5).c_str());
connection_metadata::ptr metadata = endpoint.get_metadata(id);
if (metadata) {
std::cout << *metadata << std::endl;
} else {
std::cout << "> Unknown connection id " << id << std::endl;
}
} else {
std::cout << "> Unrecognized Command" << std::endl;
}
}
return 0;
}
The CMakeLists.txt needed to compile this program looks like this
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.8)
FIND_PACKAGE(Boost 1.53 COMPONENTS random system thread REQUIRED)
IF(Boost_FOUND)
MESSAGE(STATUS "Boost_INCLUDE_DIRS : ${Boost_INCLUDE_DIRS}")
MESSAGE(STATUS "Boost_LIBRARIES : ${Boost_LIBRARIES}")
ENDIF()
INCLUDE_DIRECTORIES(SYSTEM ${Boost_INCLUDE_DIRS})
INCLUDE_DIRECTORIES(SYSTEM websocketpp)
ADD_EXECUTABLE(DemoWebSocket DemoWebSocket.cpp)
TARGET_LINK_LIBRARIES(DemoWebSocket
${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${Boost_RANDOM_LIBRARY})
IF(WIN32)
TARGET_LINK_LIBRARIES(DemoWebSocket wsock32 ws2_32)
ELSE()
TARGET_LINK_LIBRARIES(DemoWebSocket pthread rt)
ENDIF()
The solutions consists in creating a thread that creates a WebSocket server and launches its runnning. Then the client code can be used in the same function.
Below is the code that allows to use a WebSocket++ server and a a WebSocket++ client in the same function/program
void createServerEcho();
void createServerEcho()
{
server echo_server;
// Set logging settings
echo_server.set_access_channels(websocketpp::log::alevel::all);
echo_server.clear_access_channels(websocketpp::log::alevel::frame_payload);
// Initialize ASIO
echo_server.init_asio();
// Register our message handler
echo_server.set_message_handler(bind(&on_message,&echo_server,::_1,::_2));
// Listen on port 9002
echo_server.listen(9002);
// Start the server accept loop
echo_server.start_accept();
// Start the ASIO io_service run loop
echo_server.run();
}
int main()
{
websocket_endpoint endpoint;
std::thread serverThread (createServerEcho);
/*
* Client code part with variable endpoint, with creation, connection, ...
*/
serverThread.join();
}