when i build my own cpprestsdk server and client,i found that when my server receive a request and reply to it, my client have no reaction to it,and it never goes into the breakpoint where i handle the http_response,here is my code;
i was stuck for so many days,will someone help me fix this,thanks a lot
(client send request,server receives it and reply, client fail to receive http_response)
Server(i just got it from somewhere on internet):
#include "cpprest/json.h"
#include "cpprest/http_listener.h"
#include "cpprest/uri.h"
#include "cpprest/asyncrt_utils.h"
#include "cpprest/http_client.h"
using namespace web::http::experimental::listener;
using namespace web::http;
using namespace web;
void handle_get(http_request message)
{
message.reply(status_codes::OK, U("Hello, World!"));
};
void handle_post(http_request message)
{
message.reply(status_codes::NotFound);
};
void handle_put(http_request message)
{
message.reply(status_codes::NotFound);
};
void handle_delete(http_request message)
{
message.reply(status_codes::NotFound);
};
#define TRACE(msg) std::wcout << msg
#define TRACE_ACTION(a, k, v) std::wcout << a << L" (" << k << L", " << v << L")\n"
int main(int argc, char ** argv)
{
uri_builder uri(U("http://localhost:8888"));
http_listener listener(uri.to_uri());
listener.support(methods::GET, handle_get);
listener.support(methods::POST, handle_post);
listener.support(methods::PUT, handle_put);
listener.support(methods::DEL, handle_delete);
try
{
listener
.open()
.then([&listener](){TRACE(L"\nstarting to listen\n"); })
.wait();
while (true);
}
catch (std::exception const & e)
{
std::wcout << e.what() << std::endl;
}
catch (...)
{
std::wcout << "Unknown exception" << std::endl;
}
return 0;
}
and here is my Client
#include "cpprest/http_client.h"
#include "cpprest/filestream.h"
using namespace utility; // Common utilities like string conversions
using namespace web; // Common features like URIs.
using namespace web::http; // Common HTTP functionality
using namespace web::http::client; // HTTP client features
using namespace concurrency::streams; // Asynchronous streams
int main(int argc, char* argv[])
{
auto fileStream = std::make_shared<ostream>();
// Open stream to output file.
pplx::task<void> requestTask = fstream::open_ostream(U("results.html")).then([=](ostream outFile)
{
*fileStream = outFile;
// Create http_client to send the request.
http_client client(U("http://www.bing.com/"));
http_client localclient(U("http://localhost:8888"));
return localclient.request(methods::GET);
})
.then([=](http_response response)
{
printf("Received response status code:%u\n", response.status_code());
system("pause");
return response.body().read_to_end(fileStream->streambuf());
})
.then([=](size_t)
{
return fileStream->close();
});
try
{
requestTask.wait();
}
catch (const std::exception &e)
{
printf("Error exception:%s\n", e.what());
system("pause");
}
return 0;
}
Related
I have to write a function in c++ which calls shared library which is created from gRPC client which runs the gRPC client and returns the result.
Is there any samples?
Here are Some Examples I hope i would be helpful somehow.
#include <iostream>
#include <memory>
#include <string>
#include <grpcpp/grpcpp.h>
#ifdef BAZEL_BUILD
#include "examples/protos/helloworld.grpc.pb.h"
#else
#include "helloworld.grpc.pb.h"
#endif
using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using helloworld::HelloRequest;
using helloworld::HelloReply;
using helloworld::Greeter;
class GreeterClient
{
public:
GreeterClient(std::shared_ptr<Channel> channel): stub_(Greeter::NewStub(channel)) {}
// Assembles the client's payload, sends it and presents the response back from the server.
std::string SayHello(const std::string &user)
{
// Data we are sending to the server.
HelloRequest request;
request.set_name(user);
// Container for the data we expect from the server.
HelloReply reply;
// Context for the client. It could be used to convey extra information to the server and/or tweak certain RPC behaviors.
ClientContext context;
// The actual RPC.
Status status = stub_->SayHello(&context, request, &reply);
// Act upon its status.
if (status.ok())
{
return reply.message();
}
else
{
std::cout << status.error_code() << ": " << status.error_message() <<
std::endl;
return "RPC failed";
}
}
private:
std::unique_ptr<Greeter::Stub > stub_;
};
int main(int argc, char **argv)
{
// Instantiate the client. It requires a channel, out of which the actual RPCs are created. This channel models a connection to an endpoint (in this case, localhost at port 50051). We indicate that the channel isn't authenticated (use of InsecureChannelCredentials()).
GreeterClient greeter(grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials()));
std::string user("world");
std::string reply = greeter.SayHello(user);
std::cout << "Greeter received: " << reply << std::endl;
return 0;
}
OR
#include <iostream>
#include <string>
#include <dlfcn.h > // for dlopen, dlsym, dlclose
using namespace std;
int main()
{
void *handle; // handle to the library
string(*func)(string); // function pointer
char *error; // error message
handle = dlopen("/home/user/Desktop/grpc_client/libgrpc_client.so", RTLD_NOW); // open the library
if (!handle)
{
fputs(dlerror(), stderr); // print error message
exit(1);
}
func = (string(*)(string)) dlsym(handle, "run_grpc_client"); // get the function pointer
if ((error = dlerror()) != NULL)
{
fputs(error, stderr); // print error message
exit(1);
}
string result = func("hello"); // call the function
cout << result << endl;
dlclose(handle); // close the library
return 0;
}
I've tried to separate my server socket in a singleton. Here's the code:
ServerSocket.h
#pragma once
#include <asio.hpp>
#include <iostream>
using asio::ip::tcp;
class ServerSocket
{
public:
ServerSocket(ServerSocket& otherSingleton) = delete;
void operator=(const ServerSocket& copySingleton) = delete;
tcp::acceptor* InitAcceptor();
tcp::socket* InitSocket();
void StartServerSocket();
void SendData(std::string);
std::array<char, 5000> RecieveData();
static ServerSocket* GetInstance();
private:
static ServerSocket* instance;
tcp::acceptor* acceptor;
tcp::socket* socket;
asio::io_context io_context;
ServerSocket() {
acceptor = InitAcceptor();
socket = InitSocket();
}
~ServerSocket()
{
std::cout << "Server closed";
}
};
ServerSocket.cpp
#include "ServerSocket.h"
tcp::acceptor* ServerSocket::InitAcceptor()
{
try
{
tcp::acceptor* acceptor = new tcp::acceptor(io_context, tcp::endpoint(tcp::v4(), 27015));
return acceptor;
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
tcp::socket* ServerSocket::InitSocket()
{
try
{
tcp::socket* socket = new tcp::socket(io_context);
return socket;
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
void ServerSocket::StartServerSocket()
{
try
{
std::cout << "Server started";
for (;;)
{
acceptor->accept(*socket);
};
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
std::array<char, 5000> ServerSocket::RecieveData()
{
try {
std::array<char, 5000> buf;
asio::error_code error;
size_t len = socket->read_some(asio::buffer(buf), error);
buf[len] = '\0';
return buf;
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
ServerSocket* ServerSocket::instance(nullptr);
ServerSocket* ServerSocket::GetInstance()
{
if (instance == nullptr)
{
instance = new ServerSocket();
}
return instance;
}
Server socket starts, I get:
Server started
when a client connects, I get:
accept: Already open
and the server stops.
I think the error comes from the acceptor being in a for function. But according to the docs, it should work this way. (or at least that's how I understand - https://think-async.com/Asio/asio-1.20.0/doc/asio/tutorial/tutdaytime2.html)
I tried deleting the for loop, like this:
try
{
std::cout << "Server started";
acceptor->accept(*socket);
}
and now there is no problem. But the connection isn't kept open by the server. The client connects once, sends data, and the server stops running.
As far as I understand from the docs, if I set the acceptor in a for(;;), it should be running - but it doesn't work in my case.
So, how can I keep my socket open in my implementation? I want it to be running for more than one SendData - I want it to be able to communicate with the client as long as the client is connected.
Thanks.
//Edit:
Here's the client code:
#include <iostream>
#include <asio.hpp>
#include "../../cereal/archives/json.hpp"
using asio::ip::tcp;
int main(int argc, char* argv[])
{
try
{
if (argc != 2)
{
std::cerr << "Usage: client <host>" << std::endl;
return 1;
}
// Socket Parameters
const unsigned port = 27015;
auto ip_address = asio::ip::make_address_v4(argv[1]);
auto endpoint = tcp::endpoint{ ip_address, port };
// Creating and Connecting the Socket
asio::io_context io_context;
auto resolver = tcp::resolver{ io_context };
auto endpoints = resolver.resolve(endpoint);
auto socket = tcp::socket{ io_context };
asio::connect(socket, endpoints);
std::array<char, 5000> buf;
std::cout << "Message to server: ";
asio::error_code ignored_error;
std::string username = "test", password = "mihai";
std::stringstream os;
{
cereal::JSONOutputArchive archive_out(os);
archive_out(
CEREAL_NVP(username),
CEREAL_NVP(password)
);
}
asio::write(socket, asio::buffer(os.str()), ignored_error);
return 0;
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
return 1;
}
And Communication.h which is responsible to catching the operation from the client and sending it to the server
#pragma once
#include <iostream>
#include "DBUser.h"
#include "DBPost.h"
class Communication
{
public:
enum class Operations {
eLogin,
eRegister
};
void ExecuteOperation(Operations operation,const std::array<char, 5000>& buffer);
};
.cpp
#include "Communication.h"
void Communication::ExecuteOperation(Operations operation,const std::array<char, 5000>& buffer)
{
DBUser* user= DBUser::getInstance();
switch (operation)
{
case Communication::Operations::eLogin:
{
std::string username, password;
std::stringstream is(buffer.data());
{
cereal::JSONInputArchive archive_in(is);
archive_in(username,password);
}
try
{
user->LoginUser(username, password);
}
catch (const std::exception& e)
{
std::cout << e.what();
}
break;
}
case Communication::Operations::eRegister:
{
std::string username, password;
std::stringstream is(buffer.data());
{
cereal::JSONInputArchive archive_in(is);
archive_in(username, password);
}
try
{
user->CreateUser(username, password);
}
catch (const std::exception& e)
{
std::cout << e.what();
}
break;
}
}
}
Main
#include <iostream>
#include <pqxx/pqxx>
#include "DBLink.h"
#include "DBUser.h"
#include "DBPost.h"
#include "../Logging/Logging.h"
#include <iostream>
#include <string>
#include <asio.hpp>
#include "ServerSocket.h"
#include "Communication.h"
int main()
{
ServerSocket* test = ServerSocket::GetInstance();
test->StartServerSocket();
std::array<char, 5000> buf = test->RecieveData();
Communication communicationInterface;
communicationInterface.ExecuteOperation(Communication::Operations::eRegister, buf);
system("pause");
}
There's a lot of antipattern going on.
Overuse of pointers.
Overuse of new (without any delete, a guaranteed leak)
The destructor claims that "Server closed" but it doesn't actually do a single thing to achieve that.
Two-step initialization (InitXXXX functions). Firstly, you should obviously favor initializer lists
ServerSocket()
: acceptor_(InitAcceptor()), socket_(InitSocket())
{ }
And you need to makeInitAcceptor/InitSocket private to the implementation.
I'll forget the Singleton which is anti-pattern 99% of the time, but I guess that's almost debatable.
In your StartServerSocket you have a loop that reuses the same socket all the time. Of course, it will already be connected. You need separate socket instances:
for (;;) {
acceptor_->accept(*socket_);
};
Simplify/Fix
#include <boost/asio.hpp>
#include <iostream>
namespace asio = boost::asio;
using asio::ip::tcp;
struct Listener {
void Start()
{
std::cout << "Server started";
for (;;) {
auto socket = acceptor_.accept();
std::cout << "Accepted connection from " << socket.remote_endpoint()
<< std::endl;
};
}
static Listener& GetInstance() {
static Listener s_instance{27015}; // or use weak_ptr for finite lifetime
return s_instance;
}
private:
asio::io_context ioc_; // order of declaration is order of init!
tcp::acceptor acceptor_;
Listener(uint16_t port) : acceptor_{ioc_, tcp::endpoint{tcp::v4(), port}} {}
};
int main() {
try {
Listener::GetInstance().Start();
} catch (std::exception const& e) {
std::cerr << e.what() << std::endl;
}
}
Now you could hand the socket instances to a thread. I concur with the other commenters that thread-per-request is likely also an anti-pattern, and you should consider using async IO with Asio (hence the name).
Live Demo
EDIT complete and working example based on the server code from the question:
// main.cxx
#include "ServerSocket.hxx"
#include <boost/asio.hpp>
#include <iostream>
#include <string>
using boost::asio::ip::tcp;
int
main ()
{
ServerSocket *test = ServerSocket::GetInstance ();
test->StartServerSocket ();
std::cout << std::endl;
while (auto msg = test->RecieveData ())
{
std::cout << msg.value ();
}
}
// ServerSocket.hxx
#pragma once
#include <boost/asio.hpp>
#include <iostream>
#include <optional>
using boost::asio::ip::tcp;
class ServerSocket
{
public:
ServerSocket (ServerSocket &otherSingleton) = delete;
void operator= (const ServerSocket ©Singleton) = delete;
tcp::acceptor *InitAcceptor ();
tcp::socket *InitSocket ();
void StartServerSocket ();
void SendData (std::string);
std::optional<std::string> RecieveData ();
static ServerSocket *GetInstance ();
private:
static ServerSocket *instance;
tcp::acceptor *acceptor;
tcp::socket *socket;
boost::asio::io_context io_context;
ServerSocket ()
{
acceptor = InitAcceptor ();
socket = InitSocket ();
}
~ServerSocket () {
delete socket;
delete acceptor;
std::cout << "Server closed"; }
};
// ServerSocket.cxx
#include "ServerSocket.hxx"
#include <optional>
tcp::acceptor *
ServerSocket::InitAcceptor ()
{
try
{
return new tcp::acceptor (io_context, tcp::endpoint (tcp::v4 (), 27015));
}
catch (std::exception &e)
{
std::cerr << e.what () << std::endl;
}
return nullptr;
}
tcp::socket *
ServerSocket::InitSocket ()
{
try
{
return new tcp::socket (io_context);
}
catch (std::exception &e)
{
std::cerr << e.what () << std::endl;
}
return nullptr;
}
void
ServerSocket::StartServerSocket ()
{
try
{
std::cout << "Server started";
acceptor->accept (*socket);
}
catch (std::exception &e)
{
std::cerr << e.what () << std::endl;
}
}
std::optional<std::string>
ServerSocket::RecieveData ()
{
try
{
char data[5000];
for (;;)
{
boost::system::error_code error;
size_t length = socket->read_some (boost::asio::buffer (data), error);
if (error == boost::asio::error::eof) return std::nullopt; // Connection closed cleanly by peer.
else if (error)
throw boost::system::system_error (error); // Some other error.
return std::string{ data, length };
}
}
catch (std::exception &e)
{
std::cerr << e.what () << std::endl;
}
return {};
}
ServerSocket *ServerSocket::instance (nullptr);
ServerSocket *
ServerSocket::GetInstance ()
{
if (instance == nullptr)
{
instance = new ServerSocket ();
}
return instance;
}
Note that there are still some problems with the server:
Error handling
More than one connection
The server does not send a message if the operation was successful
If you disconnect the client the server shuts down
We could replace some pointers with optional no need to write "new"
Just make a normal class do not write it as singleton.
If you like to test the server you can run
telnet localhost 27015
and then write some text and press enter
I am trying to parse JSON response using cppRest Library, but an exception is raised instead the JSON object, here is the error that is show below:
Incorrect Content-Type: must be textual to extract_string, JSON to extract_json.
And here is my code that I have tried so far:
#include <iostream>
#include <cpprest/http_client.h>
using namespace web::http;
using namespace web::http::client;
using namespace web;
using namespace std;
int main()
{
uri url(L"http://www.7timer.info/bin/astro.php?lon=113.2&lat=23.1&ac=0&unit=metric&output=json&tzshift=0");
http_client client(url);
http_response response;
http_request req;
req.set_method(methods::GET);
req.headers().set_content_type(L"application/json");
response = client.request(req).get();
try
{
json::object json_object(response.extract_json().get().as_object());
}
catch (exception &e)
{
cout << e.what() << "\n";
}
return 0;
}
I solved my problem, looks like I need to set the response headers to get the received content type as json:
Updated my code:
#include <iostream>
#include <cpprest/http_client.h>
using namespace web::http;
using namespace web::http::client;
using namespace web;
using namespace std;
int main()
{
uri url(L"http://www.7timer.info/bin/astro.php?lon=113.2&lat=23.1&ac=0&unit=metric&output=json&tzshift=0");
http_client client(url);
http_request req;
req.set_method(methods::GET);
pplx::task<json::value> requestTask = client.request(req).then([](http_response response)
{
json::value jsonObject;
try
{
if ( response.status_code() == status_codes::OK )
{
response.headers().set_content_type(L"application/json"); // Set headers to receive content type as JSON
jsonObject = response.extract_json().get();
}
}
catch (const http_exception& e)
{
cout << e.error_code().message() << "\n";
}
return jsonObject; // returned a json value
});
json::array dataseries = requestTask.get().at(L"dataseries").as_array(); // We get the returned response here
for (size_t i = 0; i < dataseries.size(); i++)
{
auto timepoint = dataseries[i].at(L"timepoint");
wcout << timepoint << endl;
}
return 0;
}
I am trying to decompress message returning from WebSocket API(Okex). I tried to use zlib to decompress but fails. Please advise how to do so.
The following are websocket class written to connect to okex and returning data from the websocket server.
#include <websocketpp/config/asio_client.hpp>
#include <websocketpp/client.hpp>
#include <iostream>
typedef websocketpp::client<websocketpp::config::asio_tls_client> client;
typedef std::shared_ptr<boost::asio::ssl::context> context_ptr;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
typedef websocketpp::config::asio_client::message_type::ptr message_ptr;
using websocketpp::lib::bind;
void on_open(client* c, websocketpp::connection_hdl hdl) {
std::string msg = "{\"op\": \"subscribe\", \"args\": [\"spot/depth5:ETH-USDT\"]}";
c->send(hdl,msg,websocketpp::frame::opcode::text);
c->get_alog().write(websocketpp::log::alevel::app, "Sent Message: "+msg);
}
void websocketClient::on_message(websocketpp::connection_hdl hdl, message_ptr msg) {
std::cout << msg->get_payload().size()<< std::endl;
std::cout << msg->get_payload().data()<< std::endl;
string str = utility::inflationAlgorithm::decompress(msg->get_payload());
}
websocketClient::websocketClient() {
client c;
std::string uri = "wss://real.okex.com:8443/ws/v3";
try {
// Set logging to be pretty verbose (everything except message payloads)
c.set_access_channels(websocketpp::log::alevel::all);
c.clear_access_channels(websocketpp::log::alevel::frame_payload);
// Initialize ASIO
c.init_asio();
c.set_tls_init_handler(bind(&on_tls_init));
// Register our message handler
c.set_open_handler(bind(&on_open, &c,::_1));
c.set_message_handler(bind(&websocketClient::on_message,this,::_1,::_2));
websocketpp::lib::error_code ec;
websocketClient::con = c.get_connection(uri, ec);
if (ec) {
std::cout << "could not create connection because: " << ec.message() << std::endl;
}else {
c.connect(websocketClient::con);
// Start the ASIO io_service run loop
// this will cause a single connection to be made to the server. c.run()
// will exit when this connection is closed.
c.run();
}
} catch (websocketpp::exception const & e) {
std::cout << e.what() << std::endl;
}
}
The following code are written to decompress the message returning from Okex, but unable to get decompressed string.
#include <sstream>
#include "inflationAlgorithm.h"
using namespace zlibcomplete;
using namespace std;
std::string utility::inflationAlgorithm::decompress(const std::string & str)
{
z_stream zs; // z_stream is zlib's control structure
memset(&zs, 0, sizeof(zs));
if (inflateInit2(&zs, 47) != Z_OK)
throw(std::runtime_error("inflateInit failed while decompressing."));
zs.next_in = (Bytef*)str.data();
zs.avail_in = (uint)str.size();
int ret;
char outbuffer[32768];
std::string outstring;
// get the decompressed bytes blockwise using repeated calls to inflate
do {
zs.next_out = reinterpret_cast<Bytef*>(outbuffer);
zs.avail_out = sizeof(outbuffer);
ret = inflateInit2(&zs, 47);
if (outstring.size() < zs.total_out) {
outstring.append(outbuffer,
zs.total_out - outstring.size());
}
} while (ret == Z_OK);
inflateEnd(&zs);
if (ret != Z_STREAM_END) { // an error occurred that was not EOF
std::ostringstream oss;
oss << "Exception during zlib decompression: (" << ret << ") "
<< zs.msg;
throw(std::runtime_error(oss.str()));
}
return outstring;
}
I wrote a simple code using cpprestsdk. I use a map (records) as a member of CommandHandler class, and manipulate it in a public method (has_record()).
it works before handler.open().wait() runs, but when I call it in a request, it crashes!
Here is my code:
#define BOOST_DATE_TIME_NO_LIB
#include <string>
#include <vector>
#include <cpprest/uri.h>
#include <cpprest/http_listener.h>
#include <cpprest/asyncrt_utils.h>
using namespace std;
using namespace web;
using namespace http;
using namespace utility;
using namespace http::experimental::listener;
class CommandHandler
{
public:
CommandHandler(utility::string_t url);
pplx::task<void> open() { return m_listener.open(); }
pplx::task<void> close() { return m_listener.close(); }
bool has_record();
private:
std::map< std::string, unsigned int > records;
void handle_get_or_post(http_request message);
http_listener m_listener;
};
bool CommandHandler::has_record()
{
return records.size() > 0 && records.find("1") != records.end();
}
CommandHandler::CommandHandler(utility::string_t url) : m_listener(url)
{
m_listener.support(methods::GET, std::bind(&CommandHandler::handle_get_or_post, this, std::placeholders::_1));
m_listener.support(methods::POST, std::bind(&CommandHandler::handle_get_or_post, this, std::placeholders::_1));
}
void CommandHandler::handle_get_or_post(http_request request)
{
if(this->has_record())
request.reply(status_codes::OK, 1);
else
request.reply(status_codes::OK, 0);
};
int main(int argc, char** argv)
{
try
{
utility::string_t address = U("http://127.0.0.1:9595");
uri_builder uri(address);
auto addr = uri.to_uri().to_string();
CommandHandler handler(addr);
if(handler.has_record())
std::cout<<"work!";
handler.open().wait();
ucout << utility::string_t(U("Listening for requests at: ")) << addr << std::endl;
ucout << U("Press ENTER key to quit...") << std::endl;
std::string line;
std::getline(std::cin, line);
handler.close().wait();
}
catch (std::exception& ex)
{
ucout << U("Exception: ") << ex.what() << std::endl;
ucout << U("Press ENTER key to quit...") << std::endl;
std::string line;
std::getline(std::cin, line);
}
return 0;
}
I found the problem but I don't understand why it throws. The problem is in this line of code:
request.reply(status_codes::OK, 0);
That 0 throws an exception