I'm developing server-client application which manages req/res topics and does fan-out of some messages. Messages sends out with WSASend in overlapped mode with provided Completion routine. Noticed that very next send with WSASend after WSARead triggers sender's completion routine. While the sends originated in the server w/o preceding WSARead doesn't trigger completion routine. Why does that happen?
bool CCommunicationServer::ManageReadMessage(UCHAR index)
{
OVERLAPPED_EX *over = m_readov[index];
DWORD bytes = 0, flags = 0;
if (WSARecv(over->socket, &(over->wsabuf), 1, &bytes, &flags, over, &CCommunicationServer::WorkerReadRoutine) == SOCKET_ERROR) {
bytes = WSAGetLastError();
if (bytes != WSA_IO_PENDING) {
LOG(ERROR) << std::endl << "WSARecv() failed w/err " << bytes;
shutdown(m_readov[index]->socket, SD_BOTH);
return FALSE;
}
}
return TRUE;
}
bool CCommunicationServer::ManageSendMessage(UCHAR index, char* msg, OVERLAPPED_EX *moreover)
{
OVERLAPPED_EX *over = moreover;
DWORD bytes = 0;
SOCKET socket = m_readov[index] != NULL ? m_readov[index]->socket : NULL;
if (!over) {
ScopedLock lock(&m_lock);
if (!msg) {
LOG(WARNING) << "Empty send! Ignoring!";
return FALSE;
} else if (m_readov[index] != NULL) {
LOG(DEBUG) << ++created << "\t:\t" << deleted << "\t\t\t\r";
over = new OVERLAPPED_EX(this, m_readov[index]->socket, index, msg);
} else {
return FALSE;
}
}
if (WSASend(socket, &(over->wsabuf), 1, &bytes, 0, (LPWSAOVERLAPPED)over, &CCommunicationServer::WorkerSendRoutine) == SOCKET_ERROR) {
bytes = bytes = WSAGetLastError();
if (bytes != WSA_IO_PENDING) {
LOG(ERROR) << std::endl << "WSASend() failed w/err " << bytes;
shutdown(socket, SD_BOTH);
LOG(DEBUG) << created << "\t:\t" << ++deleted << "\t\t\t\r";
delete over;
return FALSE;
}
}
return TRUE;
}
And here are Completion routines:
void CALLBACK CCommunicationServer::WorkerReadRoutine(DWORD Error, DWORD BytesTransferred, LPWSAOVERLAPPED Overlapped, DWORD InFlags)
{
OVERLAPPED_EX *over = (OVERLAPPED_EX*)Overlapped;
if (Error)
return;
if (BytesTransferred > 0) {
over->server->GetRequest(over);
}
over->server->ManageReadMessage(over->index);
}
void CALLBACK CCommunicationServer::WorkerSendRoutine(DWORD Error, DWORD BytesTransferred, LPWSAOVERLAPPED Overlapped, DWORD InFlags)
{
OVERLAPPED_EX *over = (OVERLAPPED_EX*)Overlapped;
if (Error || over->wsabuf.len == BytesTransferred) {
LOG(DEBUG) << over->server->created << "\t:\t" << ++over->server->deleted << "\t\t\t\r";
delete over;
} else {
over->wsabuf.buf += BytesTransferred;
over->wsabuf.len -= BytesTransferred;
over->server->ManageSendMessage(over->index, NULL, over);
}
}
The variables created and deleted shows how many OVERLAPPED_EX structures were created and then released to avoid memory leakage.
Related
I'm developing a driver for a device with Qt. I have already done that many times and I have a code that I use every time. But, this time, when I open the QserialPort, it seems to work correctly, but it is not : I can write, the device receives commands, but I cannot receive on the soft : the signal QSerialPort::ReadyRead is never triggered.
When I open the serial port with Putty (just open it without sending anything) and close it just after, my Qt soft work perfectly when I reconnect it : I can now receive correctly...
Do you have an idea of what putty do of different/more than my soft when opening the port ?
(I have the same parameters and I'm on windows and Qt 5.15.2).
My code for opening :
_serial->setPortName(com);
_serial->setBaudRate(QSerialPort::Baud115200);
_serial->setDataBits(QSerialPort::Data8);
_serial->setParity(QSerialPort::NoParity);
_serial->setStopBits(QSerialPort::OneStop);
_serial->setFlowControl(QSerialPort::NoFlowControl);
if(!_serial->open(QIODevice::ReadWrite))
{
emit error(tr("Unable to open port"));
return;
}
_serial->clear();
My code for write (simple string like "hello") :
_serial->write("Hello");
My code for connect the signal :
connect(_serial, &QSerialPort::readyRead, this, &device::processCommand);
My code for read serial (processCommand()):
QString bufferData;
if (_serial->isOpen())
{
_datas.append(_serial->readAll());
bufferData = _datas.constData();
}
EDIT : The Qt exemple 'Terminal' do not works on windows with my device but works on ubuntu...
EDIT 2 : SOLUTION : I have finally find the solution, just add _serial->setDataTerminalReady(true); after opening the QSerialPort.
Thanks.
I ran into the same problem where the read signal was not detected in the virtual USB port. In the end I came to the conclusion that the QSerialPort class shouldn't.
I solved it using QThread and Win32 API.
#pragma once
#include <windows.h>
#include <QMutex>
#include <QThread>
#include <QWaitCondition>
#define SERIAL_RX_BUF_SIZE 2047
class SerialThread : public QThread
{
Q_OBJECT
public:
explicit SerialThread(QObject *parent = nullptr);
~SerialThread();
bool startThread(const QString& portName);
void stopThread();
void request(const QString& command);
signals:
void response(char* text);
void timeout();
private:
void run() override;
bool writeCommand(const QString& command);
QString m_portName;
QString m_command;
QMutex m_mutex;
QWaitCondition m_wait;
volatile bool m_quit = false;
HANDLE m_hComm;
char m_buf[SERIAL_RX_BUF_SIZE + 1];
};
#include "serial_thread.h"
#include <QDebug>
SerialThread::SerialThread(QObject *parent) :
QThread(parent)
{
memset(m_buf, 0, sizeof(m_buf));
}
SerialThread::~SerialThread()
{
}
bool SerialThread::startThread(const QString &portName)
{
const QMutexLocker locker(&m_mutex);
m_hComm = CreateFileA(portName.toStdString().c_str(), // PORT NAME
GENERIC_READ | GENERIC_WRITE, // READ/WRITE
0, // NO SHARING
NULL, // NO SECURITY
OPEN_EXISTING, // OPEN EXISTING PORT ONLY
0, // NON OVERLAPPED I/O
NULL); // NULL FOR COMM DEVICES
if (m_hComm == INVALID_HANDLE_VALUE)
{
return false;
}
m_portName = portName;
if (!SetCommMask(m_hComm, EV_RXCHAR | EV_ERR))
{
qCritical() << "SetCommMask failed";
CloseHandle(m_hComm);
return false;
}
COMMTIMEOUTS comm_timeouts;
if (!GetCommTimeouts(m_hComm, &comm_timeouts))
{
qCritical() << "GetCommTimeouts failed";
CloseHandle(m_hComm);
return false;
}
comm_timeouts.ReadIntervalTimeout = 1;
comm_timeouts.ReadTotalTimeoutMultiplier = 0;
comm_timeouts.ReadTotalTimeoutConstant = 500;
comm_timeouts.WriteTotalTimeoutMultiplier = 0;
comm_timeouts.WriteTotalTimeoutConstant = 0;
if (!SetCommTimeouts(m_hComm, &comm_timeouts))
{
qCritical() << "SetCommTimeouts failed";
CloseHandle(m_hComm);
return false;
}
start();
return true;
}
void SerialThread::stopThread()
{
m_mutex.lock();
m_quit = true;
m_mutex.unlock();
m_wait.wakeAll();
wait();
}
void SerialThread::request(const QString& command)
{
m_mutex.lock();
m_command = command;
m_mutex.unlock();
m_wait.wakeAll();
}
void SerialThread::run()
{
DWORD dwEvtMask, nRead;
while (!m_quit)
{
m_mutex.lock();
m_wait.wait(&m_mutex);
m_mutex.unlock();
{
const QMutexLocker locker(&m_mutex);
if (m_command.isEmpty())
{
continue;
}
if (!writeCommand(m_command))
{
continue;
}
if (WaitCommEvent(m_hComm, &dwEvtMask, NULL))
{
if (dwEvtMask & EV_ERR)
{
qCritical() << "Wait failed with error: " << GetLastError();
break;
}
if (dwEvtMask & EV_RXCHAR)
{
if (!ReadFile(m_hComm, &m_buf, SERIAL_RX_BUF_SIZE, &nRead, NULL))
{
qCritical() << "ReadFile error: " << GetLastError();
}
else
{
m_buf[nRead] = 0;
qDebug() << "Read: " << nRead;
emit response(m_buf);
}
}
}
else
{
DWORD dwRet = GetLastError();
if (ERROR_IO_PENDING == dwRet)
{
qDebug() << "RX timeout";
emit timeout();
}
else
{
qCritical() << "WaitCommEvent failed: " << dwRet;
}
}
m_command.clear();
}
}
CloseHandle(m_hComm);
m_quit = false;
}
bool SerialThread::writeCommand(const QString& command)
{
std::string s = command.toStdString();
DWORD n;
if (!WriteFile(m_hComm, s.data(), s.length(), &n, NULL))
{
qCritical() << "WriteFile error";
return false;
}
return true;
}
I have written code to find installed modems on a computer. This can find the name of the device, which is useful for display purposes, but it can't yet find the COM port of the modem. I need this to use the device. How can I get this information, given what I can discover using the Microsoft Device and Drive installation API?
Here is the code:
/*
use CM_Get_Device_Interface_ListW and CM_Get_Device_Interface_List_SizeW for get
list of interfaces for GUID_DEVINTERFACE_COMPORT. query CM_Get_Device_Interface_PropertyW
with DEVPKEY_Device_InstanceId for get device id, than CM_Locate_DevNodeW and finally
you can query devnode for many properties - like DEVPKEY_NAME, etc.
*/
#include <Windows.h>
#include <cfgmgr32.h> // Device and Driver Installation header
#include <initguid.h> // Put this in to get rid of linker errors - unresolved external symbol _DEVPKEY_Device_DeviceDesc
#include <devpkey.h> // Property keys defined here are now defined inline. DEVPKEY_Device_DeviceDesc
// Microsoft Device and Driver Installation library
#pragma comment(lib, "Cfgmgr32.lib")
#include <Ntddmodm.h>
#include <iostream>
#include <string>
using namespace std;
const char* error2string(int err) {
switch (err) {
case CR_SUCCESS: return "CR_OK";
case CR_DEFAULT: return "CR_DEFAULT";
case CR_OUT_OF_MEMORY: return "CR_OUT_OF_MEMORY";
case CR_INVALID_POINTER: return "CR_INVALID_POINTER";
case CR_INVALID_FLAG: return "CR_INVALID_FLAG";
case CR_INVALID_DEVNODE: return "CR_INVALID_DEVNODE";
case CR_INVALID_RES_DES: return "CR_INVALID_RES_DES";
case CR_INVALID_LOG_CONF: return "CR_INVALID_LOG_CONF";
case CR_INVALID_ARBITRATOR: return "CR_INVALID_ARBITRATOR";
case CR_INVALID_NODELIST: return "CR_INVALID_NODELIST";
case CR_DEVNODE_HAS_REQS: return "CR_DEVNODE_HAS_REQS";
case CR_INVALID_RESOURCEID: return "CR_INVALID_RESOURCEID";
case CR_DLVXD_NOT_FOUND: return "CR_DLVXD_NOT_FOUND";
case CR_NO_SUCH_DEVNODE: return "CR_NO_SUCH_DEVNODE";
case CR_NO_MORE_LOG_CONF: return "CR_NO_MORE_LOG_CONF";
case CR_NO_MORE_RES_DES: return "CR_NO_MORE_RES_DES";
case CR_ALREADY_SUCH_DEVNODE: return "CR_ALREADY_SUCH_DEVNODE";
case CR_INVALID_RANGE_LIST: return "CR_INVALID_RANGE_LIST";
case CR_INVALID_RANGE: return "CR_INVALID_RANGE";
case CR_FAILURE: return "CR_FAILURE";
case CR_NO_SUCH_LOGICAL_DEV: return "CR_NO_SUCH_LOGICAL_DEV";
case CR_CREATE_BLOCKED: return "CR_CREATE_BLOCKED";
case CR_NOT_SYSTEM_VM: return "CR_NOT_SYSTEM_VM";
case CR_REMOVE_VETOED: return "CR_REMOVE_VETOED";
case CR_APM_VETOED: return "CR_APM_VETOED";
case CR_INVALID_LOAD_TYPE: return "CR_INVALID_LOAD_TYPE";
case CR_BUFFER_SMALL: return "CR_BUFFER_SMALL";
case CR_NO_ARBITRATOR: return "CR_NO_ARBITRATOR";
case CR_NO_REGISTRY_HANDLE: return "CR_NO_REGISTRY_HANDLE";
case CR_REGISTRY_ERROR: return "CR_REGISTRY_ERROR";
case CR_INVALID_DEVICE_ID: return "CR_INVALID_DEVICE_ID";
case CR_INVALID_DATA: return "CR_INVALID_DATA";
case CR_INVALID_API: return "CR_INVALID_API";
case CR_DEVLOADER_NOT_READY: return "CR_DEVLOADER_NOT_READY";
case CR_NEED_RESTART: return "CR_NEED_RESTART";
case CR_NO_MORE_HW_PROFILES: return "CR_NO_MORE_HW_PROFILES";
case CR_DEVICE_NOT_THERE: return "CR_DEVICE_NOT_THERE";
case CR_NO_SUCH_VALUE: return "CR_NO_SUCH_VALUE";
case CR_WRONG_TYPE: return "CR_WRONG_TYPE";
case CR_INVALID_PRIORITY: return "CR_INVALID_PRIORITY";
case CR_NOT_DISABLEABLE: return "CR_NOT_DISABLEABLE";
case CR_FREE_RESOURCES: return "CR_FREE_RESOURCES";
case CR_QUERY_VETOED: return "CR_QUERY_VETOED";
case CR_CANT_SHARE_IRQ: return "CR_CANT_SHARE_IRQ";
case CR_NO_DEPENDENT: return "CR_NO_DEPENDENT";
case CR_SAME_RESOURCES: return "CR_SAME_RESOURCES";
case CR_NO_SUCH_REGISTRY_KEY: return "CR_NO_SUCH_REGISTRY_KEY";
case CR_INVALID_MACHINENAME: return "CR_INVALID_MACHINENAME";
case CR_REMOTE_COMM_FAILURE: return "CR_REMOTE_COMM_FAILURE";
case CR_MACHINE_UNAVAILABLE: return "CR_MACHINE_UNAVAILABLE";
case CR_NO_CM_SERVICES: return "CR_NO_CM_SERVICES";
case CR_ACCESS_DENIED: return "CR_ACCESS_DENIED";
case CR_CALL_NOT_IMPLEMENTED: return "CR_CALL_NOT_IMPLEMENTED";
case CR_INVALID_PROPERTY: return "CR_INVALID_PROPERTY";
case CR_DEVICE_INTERFACE_ACTIVE: return "CR_DEVICE_INTERFACE_ACTIVE";
case CR_NO_SUCH_DEVICE_INTERFACE: return "CR_NO_SUCH_DEVICE_INTERFACE";
case CR_INVALID_REFERENCE_STRING: return "CR_INVALID_REFERENCE_STRING";
case CR_INVALID_CONFLICT_LIST: return "CR_INVALID_CONFLICT_LIST";
case CR_INVALID_INDEX: return "CR_INVALID_INDEX";
case CR_INVALID_STRUCTURE_SIZE: return "CR_INVALID_STRUCTURE_SIZE";
case NUM_CR_RESULTS: return "NUM_CR_RESULTS";
default: return "unknown";
}
}
void printerror(int line, int code) {
cout << "error: " << error2string(code) << " on line " << line << endl;
}
void print_filtered_devices() {
/* find properties for GUID_DEVINTERFACE_COMPORT and GUID_DEVINTERFACE_MODEM */
LPGUID InterfaceClassGuid = (LPGUID)&GUID_DEVINTERFACE_MODEM;
PWSTR DeviceList = NULL;
ULONG DeviceListLength = 0;
CONFIGRET cr = CM_Get_Device_Interface_List_SizeW(&DeviceListLength, InterfaceClassGuid, 0, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
if (cr == CR_SUCCESS) {
DeviceList = (PWSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, DeviceListLength * sizeof(WCHAR));
cr = CM_Get_Device_Interface_ListW(InterfaceClassGuid, NULL, DeviceList, DeviceListLength, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
if (cr == CR_SUCCESS) {
for (PWSTR CurrentDevice = DeviceList; *CurrentDevice; CurrentDevice += wcslen(CurrentDevice) + 1) {
wcout << L"interface = " << CurrentDevice << endl;
DEVPROPTYPE PropertyType;
WCHAR buf[1024];
ULONG BufferSize = sizeof(buf);
cr = CM_Get_Device_Interface_PropertyW(CurrentDevice, &DEVPKEY_Device_InstanceId, &PropertyType, (PBYTE)buf, &BufferSize, 0);
cout << "CM_Get_Device_Interface_PropertyW returned: " << error2string(cr) << endl;
DEVINST Devinst;
cr = CM_Locate_DevNodeW(&Devinst, buf, CM_LOCATE_DEVNODE_NORMAL);
cout << "CM_Locate_DevNodeW returned: " << error2string(cr) << endl;
if (cr == CR_SUCCESS) {
// Query a property on the device. For example, the device description.
WCHAR DeviceDesc[2048];
ULONG PropertySize = sizeof(DeviceDesc);
cr = CM_Get_DevNode_PropertyW(Devinst,
&DEVPKEY_Device_DeviceDesc,
&PropertyType,
(PBYTE)DeviceDesc,
&PropertySize,
0);
cout << "CM_Get_DevNode_PropertyW returned: " << error2string(cr) << endl;
if (cr == CR_SUCCESS && PropertyType == DEVPROP_TYPE_STRING) {
// print device
wcout << "DEVPKEY_Device_DeviceDesc=" << DeviceDesc << endl;
wcout << "";
}
else {
printerror(__LINE__, cr);
}
//WCHAR DeviceDesc[2048];
PropertySize = sizeof(DeviceDesc);
cr = CM_Get_DevNode_PropertyW(Devinst,
&DEVPKEY_NAME,
&PropertyType,
(PBYTE)DeviceDesc,
&PropertySize,
0);
cout << "CM_Get_DevNode_PropertyW returned: " << error2string(cr) << endl;
if (cr == CR_SUCCESS && PropertyType == DEVPROP_TYPE_STRING) {
// print device
wcout << "DEVPKEY_NAME=" << DeviceDesc << endl;
wcout << "";
}
else {
printerror(__LINE__, cr);
}
//Need a port property???
}
else {
printerror(__LINE__, cr);
}
}
}
else {
printerror(__LINE__, cr);
}
}
else {
printerror(__LINE__, cr);
}
if (DeviceList != NULL) {
HeapFree(GetProcessHeap(), 0, DeviceList);
}
}
int main() {
// This call fails to retrieve any devices of class GUID_DEVINTERFACE_COMPORT
print_filtered_devices();
}
And on my PC I get this output:
interface = \\?\USB#VID_0572&PID_1340#12345678#{2c7089aa-2e0e-11d1-b114-00c04fc2aae4}
CM_Get_Device_Interface_PropertyW returned: CR_OK
CM_Locate_DevNodeW returned: CR_OK
CM_Get_DevNode_PropertyW returned: CR_OK
DEVPKEY_Device_DeviceDesc=Conexant USB CX93010 ACF Modem
CM_Get_DevNode_PropertyW returned: CR_OK
DEVPKEY_NAME=Conexant USB CX93010 ACF Modem
I'm using an QOpenGL widget to draw frames. However, I'm struggling to get frames by using avcodec_receive_frame. It ended within the block else if (ret == AVERROR(EAGAIN)) and returned -11. I have no idea what made this happen. Also I checked that codecs were fine, so I guess the problem wasn't caused by codecs.
MyDemux.cpp
AVPacket* MyDemux::allocatePacket()
{
AVPacket* packet = av_packet_alloc();
return packet;
}
AVFrame* MyDemux::allocateFrame()
{
AVFrame* frame = av_frame_alloc();
return frame;
}
int MyDemux::readFrame(AVPacket* packet)
{
mux.lock();
if (!formatCtx) {
std::cout << "formaetCtx is null" << std::endl;
mux.unlock();
return -1;
}
int ret = av_read_frame(formatCtx, packet);
if (ret == AVERROR_EOF) {
return -2;
}
if (ret != 0) {
mux.unlock();
av_packet_free(&packet);
return -1;
}
media_type = packet->stream_index;
mux.unlock();
return 0;
}
MyDecode.cpp
void MyDecode::decode(AVFrame* frame, AVPacket* packet)
{
int ret;
ret = avcodec_send_packet(codecCtx, packet); // this returned 0
while (ret == 0) {
ret = avcodec_receive_frame(codecCtx, frame); // this returned -11
if (ret == AVERROR(EINVAL)) {
std::cout << "codec issue" << std::endl;
av_frame_free(&frame);
return;
}
else if (ret == AVERROR(EAGAIN)) { // program ends here
std::cout << "output is not available this state" << std::endl;
av_frame_free(&frame);
return;
}
else if (ret == AVERROR(EINVAL)) {
std::cout << "no more frames" << std::endl;
av_frame_free(&frame);
return;
}
}
}
main.cpp
class MyThread : public QThread
{
public:
MyDemux demux;
MyDecode video_decode;
myDecode audio_decode;
MyVideoWidget* videoWidget;
AVPacket* packet;
AVFrame* frame;
void initThread()
{
char* url = "demo.mp4";
demux.openFile(url);
video_decode.openCodec(demux.copy_video_codec_par());
audio_decode.openCodec(demux.copy_audio_codec_par());
packet = demux.allocatePacket();
frame = demux.allocateFrame();
}
void run()
{
while (demux.readFrame(packet) != -2) {
if (demux.get_media_type() == 0) {
video_decode.decode(frame, packet);
videoWidget->paintFrame(frame);
}
else if (demux.get_media_type() == 1) {
}
}
video_decode.decode(frame, nullptr);
demux.clear();
demux.close();
}
};
When you call avcodec_receive_frame and get EAGAIN error that means your decoder does not get enough data to decode (e.x you send a B-frame in video). So each time you get that error you should ignore it and go to next avcodec_send_packet
There is a delay between sending and receiving a frame. When receive returns EAGAIN, you should call send with the next encoded frame, then call receive again.
I have created a basic client/server c++ socket program. I can write integers over the socket reliably, but when I try to write a string over the socket the receiving end only receives the data about 25% of the time. I have used Wireshark to verify that the data is being sent. I am new to c++ and network programming so any input would be appreciated. The code is below.
class compressionServer{
int PORT = 4000;
int LISTEN_QUEUE = 1024;
int sock;
int conn;
struct sockaddr_in socketAddress;
int receivedMagicData[8+1];
int receivedStatusCode[2];
int receivedPayloadLength[2];
char receivedCompressionData[2048];
int requestCode = 0;
int payloadLength;
uint32_t inBytes = 0;
uint32_t outBytes = 0;
double compressionRatio = 0;
string dataToCompress;
public:
void start(){
if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0){
printf("Socket Error");
exit(1);
}
bzero(&socketAddress, sizeof(socketAddress));
socketAddress.sin_family = AF_INET;
socketAddress.sin_addr.s_addr = htonl(INADDR_ANY);
socketAddress.sin_port = htons(PORT);
int enable = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0){ // so we can rebind quickly while testing
printf("setsockopt(SO_REUSEADDR) failed");
}
if(::bind(sock, (struct sockaddr *) &socketAddress, sizeof(socketAddress)) < 0){
printf("Bind Error\n");
std::cout << std::strerror(errno) << std::endl;
exit(1);
}
if(listen(sock, LISTEN_QUEUE) < 0){
printf("Listen Error");
exit(1);
}
for ( ; ; ) {
if((conn = accept(sock, (struct sockaddr *) NULL, NULL)) < 0){
printf("Accept Error");
exit(1);
}
ssize_t n;
if((n = read(conn, receivedMagicData, 8)) > 0){
int magicWord = ntohl(*receivedMagicData);
if(magicWord == 53545259){
std::cout << "DOES NOT MATCH" << std::endl;
break;
}
}
if((n = read(conn, receivedPayloadLength, 2)) > 0){
payloadLength = ntohs(*receivedPayloadLength);
std::cout << "payloadLength = " << std::hex << payloadLength << std::endl;
}
if((n = read(conn, receivedStatusCode, 2)) > 0){
requestCode = ntohs(*receivedStatusCode);
std::cout << "requestCode = " << std::hex << requestCode << std::endl;
}
while((n = read(conn, receivedCompressionData, 2048)) > 0){
receivedCompressionData[n] = 0;
cout << "data: " << receivedCompressionData << endl;
dataToCompress = receivedCompressionData;
if(strlen(receivedCompressionData) > 0){
break;
}
}
}
if (close(conn) < 0){
printf("Close Error");
exit(1);
}
}
};
The server class essential is expecting to receive 12 bytes of data as the header and after that up to 2048 bytes as the characters. This is the part only coming through a fraction of the time.
class compressionClient{
public:
void start(std::string ipAddr){
int MAX_LINE = 4096;
int sockfd;
struct sockaddr_in servaddr;
if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
printf("socket error");
exit(1);
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(4000);
if (inet_pton(AF_INET, ipAddr.c_str(), &servaddr.sin_addr) <= 0){
printf("inet_pton error for %s", ipAddr.c_str());
exit(1);
}
if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0){
printf("connect error");
exit(1);
}
long headerHex = htonl(0x53545259);
long header2Hex = htonl(0x31300034);
std::cout << "send: " << std::hex << headerHex << std::endl;
std::cout << "send2: " << std::hex << header2Hex << std::endl;
//server receives this
if(send(sockfd, (char *) (&headerHex), sizeof(headerHex), 0) < 0){
printf("Write Error");
exit(1);
}
//server receives this too
if(send(sockfd, (char *) (&header2Hex), sizeof(header2Hex), 0) < 0){
printf("Write Error2");
exit(1);
}
char data[100];
snprintf(data, sizeof(data), "aaabbcddd");
//server only recieves this sometimes
if(write(sockfd, data, strlen(data)) < 0){
printf("write error3");
exit(1);
}
}
};
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!