boost::asio::async_write not writing to clients properly, weird behaviour - boost

I have written a program that accepts N client connections and then writes data into them. The problem I am having now is: I can only write to N-1 clients, the first one is never written to. I have no idea why this is happening and so I wish some of you might be able to provide some assistance.
I have provided the portion of code that may be associated with this problem:
void ClientPartitionServer::AcceptClientConnections(int port) {
cout << "Listening to connections..." << endl;
cout << "Number of PartitionInstanceConnections: " <<
m_partitionInstanceConnections.size() << endl;
m_acceptor = new boost::asio::ip::tcp::acceptor(m_IOService);
m_endpoint = new boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(),
m_port);
m_acceptor->open(m_endpoint->protocol());
m_acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
m_acceptor->bind(*m_endpoint);
m_acceptor->listen();
boost::asio::ip::tcp::socket* acceptingSocket =
new boost::asio::ip::tcp::socket(m_IOService);
m_acceptor->async_accept(*acceptingSocket, boost::bind(
&ClientPartitionServer::HandleAccept, this, acceptingSocket,
boost::asio::placeholders::error));
}
void ClientPartitionServer::HandleAccept(boost::asio::ip::tcp::socket* socket,
const boost::system::error_code& error) {
cout << "Connection established..." << endl;
m_clientSockets.push_back(new boost::asio::ip::tcp::socket(m_IOService));
cout << m_clientSockets.back()->is_open() << endl;
++m_clientSocketsCounter;
cout << "ClientPartitionServer identifier: " << m_identifier << endl;
cout << "Client connected on port: " << m_port << endl;
cout << "Number of clients on port: " << m_clientSocketsCounter <<
endl;
m_acceptor->async_accept(*m_clientSockets.back(), boost::bind(
&ClientPartitionServer::HandleAccept, this, m_clientSockets.back(),
boost::asio::placeholders::error));
}
void ClientPartitionServer::HandleSignal(char* content, int transferSize,
int identifier) {
if(identifier == m_identifier) {
TransferToQueueBuffer(content, transferSize);
if(m_writeCompleteFlag) {
TransferToWriteBuffer(m_queueBuffer, m_queueBufferSize);
if(m_clientSockets.size() != 0) {
for(vector<boost::asio::ip::tcp::socket*>::const_iterator i =
m_clientSockets.begin(); i != m_clientSockets.end(); ++i) {
WriteToClient(m_writeBuffer, m_queueBufferSize, *i);
}
}
}
}
}
void ClientPartitionServer::WriteToClient(char* content, int transferSize,
boost::asio::ip::tcp::socket* clientSocket) {
boost::lock_guard<boost::mutex> lock(m_writeToClientMutex);
m_writeCompleteFlag = false;
boost::asio::async_write(*clientSocket, boost::asio::buffer("ABC ",
4), boost::bind(&ClientPartitionServer::HandleWrite,
this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void ClientPartitionServer::HandleWrite(const boost::system::error_code& ec,
size_t bytes_transferred) {
cout << "handlewrite" << endl;
m_writeCompleteFlag = true;
}
Thank you for any assistance.

The first async_accept() is called on acceptingSocket which is new'd in AcceptClientConnections() and leaked.
The subsequent async_accept()s are called on sockets that are new'd in HandleAccept() and push_back()'ed into m_clientSockets.
WriteToClient() is executed only on the sockets found in m_clientSockets, never on the first socket.
Solution: push_back that first socket in AcceptClientConnections() into m_clientSockets too.

Related

Boost Beast gives "The handle is invalid" error after http:async_write

Initially I got error "The file handle supplied is invalid". This is because my socket was closed. I corrected that error.
Now I get another error "The handle is invalid"....Any idea what could be the issue
Below is my code, which is very simple:
void BeastResponse::write(http::response<http::file_body> responseFile)
{
std::cout << "BeastResponse while file write: " << this << std::endl;
auto self = shared_from_this();
http::async_write(m_stream, responseFile, [self](beast::error_code ec, std::size_t t)
{
if (ec)
{
std::cout << "File Write Failed" << ": " << ec.message() << std::endl;
std::cout << t << std::endl;
}
else
{
std::cout << t << std::endl;
};
});
}
Here , t = 4kb when I get the error. So I think async_write after it does the first block of 4kb, my handler or socket is going to a bad state.
If the change to http:write instead of http:async_write, there is no issues
Below code works for me...
void BeastResponse::write(http::response<http::file_body>&& responseFile)
{
std::cout << "BeastResponse while file write: " << this << std::endl;
auto self = shared_from_this();
// copy file into the member variable, m_response_file
m_response_file = std::move(responseFile)
// 2nd parameter must be a member variable of BeastResponse
http::async_write(m_stream, m_response_file, [self](beast::error_code ec, std::size_t t)
{
if (ec)
{
std::cout << "File Write Failed" << ": " << ec.message() << std::endl;
std::cout << t << std::endl;
}
else
{
std::cout << t << std::endl;
};
});
}

how to implement onvif CreatePullPointSubscription operation by gsoap

Referring to onvif core specification: If the subscription is accepted, the response contains a WS-EndpointReference to the
instantiated pull point. This WS-Endpoint provides a PullMessages operation, which is
used by the client to retrieve Notifications.
But I can't see the codes about instancing pull point, and I don't know how to implement it. Here is my coding.
SOAP_FMAC5 int SOAP_FMAC6 __tev__CreatePullPointSubscription(struct soap* soap, struct _tev__CreatePullPointSubscription *tev__CreatePullPointSubscription, struct _tev__CreatePullPointSubscriptionResponse *tev__CreatePullPointSubscriptionResponse)
{
tev__CreatePullPointSubscriptionResponse->SubscriptionReference.Address = (char *)soap_malloc(soap, sizeof(char) * 128);
strcpy(tev__CreatePullPointSubscriptionResponse->SubscriptionReference.Address, "http://192.168.12.1/Subscription?Idx=0");
tev__CreatePullPointSubscriptionResponse->wsnt__CurrentTime=time(NULL);
tev__CreatePullPointSubscriptionResponse->wsnt__TerminationTime=tev__CreatePullPointSubscriptionResponse->wsnt__CurrentTime+60;
return SOAP_OK;
}
Can anyone brighten me? Thank you in advance.
void CreatePullPointSubscription() {
struct soap *m_soap = soap_new();
m_soap->connect_timeout = SOAP_REQUEST_TIMEOUT_IN_SECONDS;
m_soap->recv_timeout = SOAP_REQUEST_TIMEOUT_IN_SECONDS;
m_soap->send_timeout = SOAP_REQUEST_TIMEOUT_IN_SECONDS;
PullPointSubscriptionBindingProxy subscriptionProxy(m_soap);
subscriptionProxy.soap_endpoint = xAddr;
if (addCredentialsToCall(m_soap)) {
_tev__CreatePullPointSubscription request;
_tev__CreatePullPointSubscriptionResponse response;
auto ret = subscriptionProxy.CreatePullPointSubscription(&request, response);
if (ret != SOAP_OK) {
soap_stream_fault(m_soap, std::cerr);
} else {
auto address = response.SubscriptionReference.Address;
std::cout << address << std::endl;
std::cout << "Subscription metadata: " << response.SubscriptionReference.Metadata << std::endl;
std::cout << "Termination time " << response.wsnt__TerminationTime << std::endl;
std::cout << "Current time " << response.wsnt__CurrentTime << std::endl;
std::string uuid = std::string(soap_rand_uuid(m_soap, "urn:uuid:"));
struct SOAP_ENV__Header header;
header.wsa5__MessageID = (char *) uuid.c_str();
header.wsa5__To = response.SubscriptionReference.Address;
m_soap->header = &header;
if (addCredentialsToCall(m_soap)) {
_tev__PullMessages tev__PullMessages;
tev__PullMessages.Timeout = "PT600S";
tev__PullMessages.MessageLimit = 100;
_tev__PullMessagesResponse tev__PullMessagesResponse;
auto ret = subscriptionProxy.PullMessages(&tev__PullMessages, tev__PullMessagesResponse);
for (auto msg : tev__PullMessagesResponse.wsnt__NotificationMessage) {
std::cout << "\tMessage is :" << msg->Topic->__mixed << std::endl;
}
} else {
std::cout << "Couldn't set credentials!!!" << std::endl;
}
}
}
subscriptionProxy.destroy();
}
This worked for me atleast to pull the event initializers.

Sending Cap'n Proto messages over TCP in C++

I'm totally new to networking and just started to use Cap'n Proto too.
This is some sample program from here:
void writeAddressBook(int fd) {
::capnp::MallocMessageBuilder message;
AddressBook::Builder addressBook = message.initRoot<AddressBook>();
::capnp::List<Person>::Builder people = addressBook.initPeople(2);
Person::Builder alice = people[0];
alice.setId(123);
alice.setName("Alice");
alice.setEmail("alice#example.com");
// Type shown for explanation purposes; normally you'd use auto.
::capnp::List<Person::PhoneNumber>::Builder alicePhones =
alice.initPhones(1);
alicePhones[0].setNumber("555-1212");
alicePhones[0].setType(Person::PhoneNumber::Type::MOBILE);
alice.getEmployment().setSchool("MIT");
Person::Builder bob = people[1];
bob.setId(456);
bob.setName("Bob");
bob.setEmail("bob#example.com");
auto bobPhones = bob.initPhones(2);
bobPhones[0].setNumber("555-4567");
bobPhones[0].setType(Person::PhoneNumber::Type::HOME);
bobPhones[1].setNumber("555-7654");
bobPhones[1].setType(Person::PhoneNumber::Type::WORK);
bob.getEmployment().setUnemployed();
writePackedMessageToFd(fd, message);
}
The last line uses writePackedMessageToFd() which takes fd as a file descriptor and message created by MallocMessageBuilder.
I work on Windows with Visual Studio 2017.
I would like to send message to a remote server which will answer with a similar Cap'nP object.
The question is how can I send it and receive the answer?
I tried to initialize and create a socket in the following way:
//Initialize WinSock
WSAData data;
WORD ver = MAKEWORD(2, 2);
int wsResult = WSAStartup(ver, &data);
if (wsResult != 0) {
cerr << "Can't start WinSock, Error #" << wsResult << endl;
return;
}
else {
cout << "Socket initialized!" << endl;
}
//Create socket
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET) {
cerr << "Can't create socket, Error #" << WSAGetLastError() << endl;
WSACleanup();
return;
}
else {
cout << "Socket created!" << endl;
}
If everything went fine socket() return a file descriptor.
So I just used
writePackedMessageToFd(sock, message), but didn't work.
By the way I don't understand this concept since the socket doesn't "know" which IP and port I want to use. I should specify them when I use the connect() function.
I tried to skip the writePackedMessageToFd() function. Connected to the server with connect() and just used Windows' send() function to send the message. Something like this:
string ipAddress = "127.0.0.1"; //IP Address of server
int port = 58661; //Listening port of server
//Initialize WinSock
WSAData data;
WORD ver = MAKEWORD(2, 2);
int wsResult = WSAStartup(ver, &data);
if (wsResult != 0) {
cerr << "Can't start WinSock, Error #" << wsResult << endl;
return;
}
else {
cout << "Socket initialized!" << endl;
}
//Create socket
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET) {
cerr << "Can't create socket, Error #" << WSAGetLastError() << endl;
WSACleanup();
return;
}
else {
cout << "Socket created!" << endl;
}
//Fill in a hint structure
sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(port);
inet_pton(AF_INET, ipAddress.c_str(), &hint.sin_addr);
//Connect to server
int connResult = connect(sock, (sockaddr*)&hint, sizeof(hint));
if (connResult == SOCKET_ERROR) {
cerr << "Can't connect to server, Error #" << WSAGetLastError() << endl;
closesocket(sock);
WSACleanup();
return;
}
else {
cout << "Connected to " << ipAddress << " on port " << port << endl;
}
//Send and receive data
char buf[4096];
//string mess = "Hello";
//Send message
//int sendResult = send(sock, mess.c_str(), mess.size() + 1, 0);
int sendResult = send(sock, (const char*)&message, sizeof(message) + 1, 0);
//Wait for response
ZeroMemory(buf, 4096);
int bytesReceived = recv(sock, buf, 4096, 0);
if (bytesReceived > 0) {
cout << "SERVER>" << string(buf, 0, bytesReceived) << endl;
}
//Close down everything
closesocket(sock);
WSACleanup();
cout << "Connection closed!" << endl;
This one sended something but it was definitely wrong because the server didn't respond.
In brief: I would like to send and receive Cap'n Proto packed messages over TCP connection to a specified IP:port.
How could I accomplish this? I really need some help.
I would really really appreciate Your help! Thanks in advance!
On Windows, socket() does not return a file descriptor. It returns a Windows SOCKET, which is actually a HANDLE cast to an integer. On Windows, "file descriptors" are implemented as a compatibility layer in the C runtime library; they are not directly supported by the OS.
You can use kj::HandleInputStream and kj::HandleOutputStream to perform I/O on sockets on Windows.
kj::HandleOutputStream out((HANDLE)sock);
capnp::writeMessage(out, message);
And:
kj::HandleInputStream in((HANDLE)sock);
capnp::InputStreamMessageReader message(in);

libwebsocket: How to disconnect client if there is no server response for a while (with timeout)?

I have a client using libwebsocket to establish a connection to a server. Whenever the client sends a request, the server sends a response and after receiving the response the client closes the connection. Works fine.
But when the server does not answer to the request i have the problem that the client keeps waiting for a response forever. When nothing happens the callback is never called and its not possible to close connection with returning -1 from callback function.
Is there any way to enable a timeout for the connection to close? Or any possibility to close connection from outside the callback function?
Here is my code so far:
int callback_function(libwebsocket_context* context, libwebsocket* wsi, enum libwebsocket_callback_reasons reason, void* user, void* in, size_t len) {
switch (reason) {
case LWS_CALLBACK_CLIENT_ESTABLISHED: {
std::cout << "LWS_CALLBACK_CLIENT_ESTABLISHED" << std::endl;
libwebsocket_callback_on_writable(context, wsi);
}
break;
case LWS_CALLBACK_CLOSED:{
std::cout << "LWS_CALLBACK_CLOSED" << std::endl;
}
break;
case LWS_CALLBACK_CLIENT_RECEIVE_PONG:
case LWS_CALLBACK_CLIENT_RECEIVE:{
std::cout << "LWS_CALLBACK_CLIENT_RECEIVE" << endl;
((char *)in)[len] = '\0';
answers_[current_request] = answers_[current_request] + string((char *)in);
if (libwebsocket_is_final_fragment(wsi)){
std::cout << "request:" << requests_[current_request] << std::endl;
std::cout << "answer:" << answers_[current_request] << std::endl;
current_request++;
if(current_request >= answers_.size()) {
ready = true;
return -1;
}
libwebsocket_callback_on_writable(context, wsi);
}
}
break;
case LWS_CALLBACK_CLIENT_WRITEABLE:{
std::cout << "LWS_CALLBACK_CLIENT_WRITEABLE" << endl;
unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 4096 + LWS_SEND_BUFFER_POST_PADDING];
const std::string message = std::string(requests_[current_request]);
std::copy(message.begin(), message.end(), &buf[LWS_SEND_BUFFER_PRE_PADDING]);
buf[LWS_SEND_BUFFER_PRE_PADDING+(int)message.size()]='\0';
int n = libwebsocket_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING], (size_t)message.size(), static_cast<libwebsocket_write_protocol>(LWS_WRITE_BINARY));
if (n < 0){
std::cout << kLogErr << "bad things are happening" << std::endl;
return -1;
}
if (n < (int)message.size()) {
std::cout << kLogErr << "Partial write LWS_CALLBACK_CLIENT_WRITEABLE" << std::endl;
return -1;
}
}
break;
default:
std::cout << "CALLBACK_DEFAULT: " << reason << endl;
break;
}
return 0;
}
vector<string> sendMessage(const string& server, int port, const string& path, const vector<string>& messages, bool& error) {
ready = error = false;
current_request = 0;
requests_ = vector<string>(messages);
answers_ = vector<string>(requests_.size(), "");
int ietf_version = -1; /* latest */
wsi_ = libwebsocket_client_connect(context_, server.c_str(), port, 2, path.c_str(), server.c_str(), "origin", NULL, ietf_version);
if (wsi_ == NULL) {
std::cout << kLogErr << "libwebsocket connect failed server:" << server << " port: " << port << " path: " << path << std::endl;
error = true;
return vector<string>();
}
bool first_time = true;
int n = 0;
while (n >= 0 && !force_exit && !ready) {
n = libwebsocket_service(context_, 0);
if(first_time) {
libwebsocket_callback_on_writable(context_, wsi_);
first_time = false;
}
if (n < 0){
continue;
}
if (wsi_ == NULL) {
break;
}
}
error = !ready;
wsi_ = NULL;
return vector<string>(answers_);
}
You could try using:
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
return -1;
break;
Or
lws_set_timeout
But I'm not a 100% sure that will work, you could also try creating an issue/question on their GitHub, they tend to answer quite fast/clear.
I'm also not sure if you should implement
I solved the problem.
I programmed a timer in
vector<string> sendMessage(const string& server, int port, const string& path, const vector<string>& messages, bool& error)
and when the timeout is reaches, the timer sets a flag and triggers
libwebsocket_callback_on_writable(context_, wsi_);
again. Then in
int callback_function(libwebsocket_context* context, libwebsocket* wsi, enum libwebsocket_callback_reasons reason, void* user, void* in, size_t len)
in case
LWS_CALLBACK_CLIENT_WRITEABLE
i check the flag and if it is set the callback is aborted with
return -1;
Works fine!

Socket operation on non-socket with zeromq ZMQ_PUB socket

Fairly simple question. I have some code to send a message from a serialised protobuf event (tried also with just a simple char * string). However when i call send i get the error "Socket operation on non-socket". I've tried a lot of things but to no avail.
void send_event(tp::Event event, void * z_pub)
{
assert(z_pub != NULL);
zmq_msg_t msg;
int size = event.ByteSize();
uint8_t sev[size];
event.SerializeWithCachedSizesToArray(sev);
int rc = zmq_msg_init_size(&msg, size);
memcpy(zmq_msg_data(&msg), &sev, size);
if (zmq_sendmsg(z_pub, &msg, 0) != 0)
{
cout << "Send err code: " << " " << zmq_strerror(zmq_errno()) << endl;
}
if (zmq_msg_close(&msg) != 0)
{
cout << "Closing message err code: " << zmq_strerror(zmq_errno()) << endl;
}
}
With things inited thus:
void * z_ctx_pub = zmq_ctx_new();
void * z_pub = zmq_socket(z_ctx_pub, ZMQ_PUB);
if (z_pub == NULL)
cerr << "Error creating output socket for process" << endl;
if (zmq_bind(z_pub, z_pub_uri.c_str()) != 0)
{
cout << "Binding to PUB err code: " << " " << zmq_strerror(zmq_errno()) << endl;
abort();
}
else
cout << "Bound to " << z_pub_uri << endl;
Edit: I've now moved the init to the same thread that i was doing the send in and i get:
Resource temporarily unavailable
http://paste.ubuntu.com/5984515/

Resources