According to th Qt documentation if we want to use named pipes on windows, we can use QLocalSocket.
I am writing a server and client program with Qt. If I try to use the WIN32 API to write some message in the pipe line, the Qt client does not show it. Also, if the client writes by using the WIN32 API again, the Qt server does not echo the message sent. Is QLocalSocket really recommended for named pipes?
This is the Win32 Server code
wcout << "Creating an instance of a named pipe..." << endl;
// Create a pipe to send data
HANDLE pipe = CreateNamedPipeW(
L"\\\\.\\pipe\\ServicePipe", // name of the pipe
PIPE_ACCESS_OUTBOUND, // 1-way pipe -- send only
PIPE_TYPE_BYTE, // send data as a byte stream
100, // only allow 1 instance of this pipe
0, // no outbound buffer
0, // no inbound buffer
0, // use default wait time
NULL // use default security attributes
);
if (pipe == NULL || pipe == INVALID_HANDLE_VALUE) {
wcout << "Failed to create outbound pipe instance.";
// look up error code here using GetLastError()
system("pause");
return 1;
}
wcout << "Waiting for a client to connect to the pipe..." << endl;
// This call blocks until a client process connects to the pipe
BOOL result = ConnectNamedPipe(pipe, NULL);
if (!result) {
wcout << "Failed to make connection on named pipe." << endl;
// look up error code here using GetLastError()
CloseHandle(pipe); // close the pipe
system("pause");
return 1;
}
wcout << "Sending data to pipe..." << endl;
// This call blocks until a client process reads all the data
wcout <<endl<<"Input your message: ";
wstring data=L"";
getline(wcin,data);
DWORD numBytesWritten = 0;
result = WriteFile(
pipe, // handle to our outbound pipe
data.c_str(), // data to send
wcslen(data.c_str()) * sizeof(wchar_t), // length of data to send (bytes)
&numBytesWritten, // will store actual amount of data sent
NULL // not using overlapped IO
);
if (result) {
wcout << "Number of bytes sent: " << numBytesWritten << endl;
} else {
wcout << "Failed to send data." << endl;
// look up error code here using GetLastError()
}
// Close the pipe (automatically disconnects client too)
CloseHandle(pipe);
wcout << "Done." << endl;
This is the Win32 Client side:
wcout << "Connecting to pipe..." << endl;
// Open the named pipe
// Most of these parameters aren't very relevant for pipes.
HANDLE pipe = CreateFileW(
L"\\\\.\\pipe\\ServicePipe",
GENERIC_READ, // only need read access
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (pipe == INVALID_HANDLE_VALUE) {
wcout << "Failed to connect to pipe." << endl;
// look up error code here using GetLastError()
system("pause");
return 1;
}
wcout << "Reading data from pipe..." << endl;
// The read operation will block until there is data to read
wchar_t buffer[128];
DWORD numBytesRead = 0;
BOOL result = ReadFile(
pipe,
buffer, // the data from the pipe will be put here
127 * sizeof(wchar_t), // number of bytes allocated
&numBytesRead, // this will store number of bytes actually read
NULL // not using overlapped IO
);
if (result) {
buffer[numBytesRead / sizeof(wchar_t)] = '?'; // null terminate the string
wcout << "Number of bytes read: " << numBytesRead << endl;
wcout << "Message: " << buffer << endl;
} else {
wcout << "Failed to read data from the pipe." << endl;
}
// Close our pipe handle
CloseHandle(pipe);
wcout << "Done." << endl;
This is the Qt Server side
LocalSocketIpcServer::LocalSocketIpcServer(QString servername, QObject *parent)
:QObject(parent) {
m_server = new QLocalServer(this);
if (!m_server->listen(servername)) {
showMessage("Not able to start the Server");
}
connect(m_server, SIGNAL(newConnection()), this, SLOT(socket_new_connection()));
}
LocalSocketIpcServer::~LocalSocketIpcServer() {
}
void LocalSocketIpcServer::socket_new_connection() {
QLocalSocket *clientConnection = m_server->nextPendingConnection();
while (clientConnection->bytesAvailable() < (int)sizeof(quint32))
clientConnection->waitForReadyRead();
//connect(clientConnection,SIGNAL(readyRead()),clientConnection,SLOT(rea));
connect(clientConnection, SIGNAL(disconnected()),clientConnection, SLOT(deleteLater()));
QDataStream in(clientConnection);
in.setVersion(QDataStream::Qt_5_1);
if (clientConnection->bytesAvailable() < (int)sizeof(quint16)) {
return;
}
QString message;
in >> message;
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
QString msg=+"Message recieved with content "+message+"\n";
out.setVersion(QDataStream::Qt_5_1);
out <<msg;
out.device()->seek(0);
clientConnection->write(block);
clientConnection->flush();
clientConnection->disconnectFromServer();
emit messageReceived(message);
}
void LocalSocketIpcServer::showMessage(QString msg)
{
QMessageBox m;
m.setText(msg);
m.exec();
}
LocalSocketIpcServer::FrmMain(QWidget *parent) :QMainWindow(parent),ui(new Ui::FrmMain)
{
ui->setupUi(this);
m_server = new LocalSocketIpcServer("\\\\.\\pipe\ServicePipe", this);
connect(m_server, SIGNAL(messageReceived(QString)), this, SLOT(messageReceived(QString)));
}
LocalSocketIpcServer::~FrmMain()
{
delete ui;
}
void LocalSocketIpcServer::messageReceived(QString message)
{
ui->textBrowser->append(message+"\n");
}
This is the Qt Client side
LocalSocketIpcClient::LocalSocketIpcClient(QString remoteServername, QObject *parent) :
QObject(parent) {
m_socket = new QLocalSocket(this);
m_serverName = remoteServername;
connect(m_socket, SIGNAL(connected()), this, SLOT(socket_connected()));
connect(m_socket, SIGNAL(disconnected()), this, SLOT(socket_disconnected()));
connect(m_socket, SIGNAL(readyRead()), this, SLOT(socket_readReady()));
connect(m_socket, SIGNAL(error(QLocalSocket::LocalSocketError)),
this, SLOT(socket_error(QLocalSocket::LocalSocketError)));
}
LocalSocketIpcClient::~LocalSocketIpcClient() {
m_socket->abort();
delete m_socket;
m_socket = NULL;
}
QString LocalSocketIpcClient::Read()
{
QDataStream in(this->m_socket);
in.setVersion(QDataStream::Qt_5_1);
if (m_socket->bytesAvailable() < (int)sizeof(quint16)) {
return "No data available";
}
QString message;
in >> message;
return message;
}
void LocalSocketIpcClient::send_MessageToServer(QString message) {
m_socket->abort();
m_message = message;
m_socket->connectToServer(m_serverName,QIODevice::ReadWrite);
}
void LocalSocketIpcClient::socket_connected(){
QByteArray block;
QDataStream out(&block, QIODevice::ReadWrite);
out.setVersion(QDataStream::Qt_5_1);
out << m_message;
out.device()->seek(0);
m_socket->write(block);
m_socket->flush();
}
void LocalSocketIpcClient::socket_disconnected() {
//showMessage("Client socket_disconnected");
}
void LocalSocketIpcClient::socket_readReady() {
//showMessage("Client socket read Ready");
QDataStream in(this->m_socket);
in.setVersion(QDataStream::Qt_5_1);
if (m_socket->bytesAvailable() < (int)sizeof(quint16)) {
return;
}
QString message;
in >> message;
emit RecievedDataFromServer(message);
}
void LocalSocketIpcClient::socket_error(QLocalSocket::LocalSocketError e) {
/*
QString errorMessage="Client socket_error:";
switch (e) {
case QLocalSocket::ConnectionRefusedError:
errorMessage+="The connection was refused by the peer (or timed out).";
break;
case QLocalSocket::PeerClosedError:
errorMessage+="The remote socket closed the connection. Note that the client socket (i.e., this socket) will be closed after the remote close notification has been sent.";
break;
case QLocalSocket::ServerNotFoundError:
errorMessage+="The local socket name was not found.";
break;
case QLocalSocket::SocketAccessError:
errorMessage+="The socket operation failed because the application lacked the required privileges.";
break;
case QLocalSocket::SocketResourceError:
errorMessage+="The local system ran out of resources (e.g., too many sockets).";
break;
case QLocalSocket::SocketTimeoutError:
errorMessage+="The socket operation timed out.";
break;
case QLocalSocket::DatagramTooLargeError:
errorMessage+="The datagram was larger than the operating system's limit (which can be as low as 8192 bytes).";
break;
case QLocalSocket::ConnectionError:
errorMessage+="An error occurred with the connection.";
break;
case QLocalSocket::UnsupportedSocketOperationError:
errorMessage+="The requested socket operation is not supported by the local operating system.";
break;
case QLocalSocket::UnknownSocketError:
errorMessage+="An unidentified error occurred.";
break;
default:
break;
}
showMessage(errorMessage);
*/
}
void LocalSocketIpcClient::showMessage(QString msg)
{
QMessageBox m;
m.setText(msg);
m.exec();
}
LocalSocketIpcClient::SingleMessageSend(QWidget *parent) :
QDialog(parent),
ui(new Ui::SingleMessageSend)
{
ui->setupUi(this);
client = new LocalSocketIpcClient("\\\\.\\pipe\ServicePipe", this);
connect(this->client,SIGNAL(RecievedDataFromServer(QString)),this,SLOT(UpdateGUI(QString)));
}
LocalSocketIpcClient::~SingleMessageSend()
{
delete ui;
}
void SingleMessageSend::on_pushButton_clicked()
{
QString msg=this->ui->lineEdit->text().trimmed();
client->send_MessageToServer(msg);
}
void SingleMessageSend::UpdateGUI(QString message)
{
ui->textEdit->insertPlainText(message+"\n");
}
void SingleMessageSend::on_pushButton_2_clicked()
{
ui->textEdit->insertPlainText(client->Read()+QString("\n"));
}
Without going through all of your code, I can answer this in the affirmative. Here is some code from a working app that writes from a Qt app to a named pipe in another Qt app (it restores another app which is minimized):
QLocalSocket ls;
ls.connectToServer("Restore Server", QIODevice::WriteOnly);
if (!ls.waitForConnected(5000))
{
qDebug(ls.errorString().toUtf8());
return false;
}
ls.write("raise");
if (!ls.waitForBytesWritten(5000))
{
qDebug(ls.errorString().toUtf8());
return false;
}
ls.disconnectFromServer();
The app to be restored sets things up thus:
localServer = new QLocalServer(this);
connect(localServer, SIGNAL(newConnection()), this,
SLOT(messageFromOtherInstance()));
localServer->listen("Restore Server");
When it comes time to read the message, I do it like this:
QLocalSocket *localSocket = localServer->nextPendingConnection();
if (!localSocket->waitForReadyRead(5000))
{
qDebug(localSocket->errorString().toLatin1());
return;
}
QByteArray byteArray = localSocket->readAll();
QString message = QString::fromUtf8(byteArray.constData());
if (message == "raise")
bringToTop(this);
It may well be that Qt named pipes and M$ named pipes are somehow incompatible. I suggest writing a M$ framework app to write to the M$ framework client, and a M$ framework app to read, to make sure they are both working right. Then, substitute a Qt app to read from the Qt server. If that doesn't work, it is some sort of framework incompatibility (although I expect they both interact with the OS properly). One thing to be sure of in situations like this is to make sure the processes don't block. That's why e.g. I make sure the read is ready before I read it. You might also have to flush the pipe after writing on the M$ side, although I didn't with Qt.
(Note that I have since found out that if a Print, Print Preview, Page Setup or browse for file dialog is open, it stops the Qt message loop, and the app will be unresponsive to messages like this. The Choose Font dialog, OTOH, doesn't block the parent app. Go figure.)
Related
I'm trying to communicate between 2 devices using bluetooth sockets and rfcomm but my program crashes (see here for error image).
I just listen about 1 or 2 second and then the project just crashes.
This is my client sending audio:
void ClientTalk::startTalk()
{
qDebug() << "Start talk";
audio_output->stop();
connect(audio_input, SIGNAL(stateChanged(QAudio::State)), this, SLOT(handleStateChangedInput(QAudio::State)));
audio_input->start(socket);
qDebug() << "Talking to server";
}
And this is my client listening to server:
void ClientTalk::readSocket()
{
//QBuffer *buffer;
QByteArray buff;
audio_input->stop();
connect(audio_output, SIGNAL(stateChanged(QAudio::State)), this, SLOT(handleStateChangedOutput(QAudio::State)));
while(socket->canReadLine())
{
buff.append(socket->readLine());
QBuffer audio_buffer(&buff);
audio_buffer.open(QIODevice::WriteOnly);
//QDataStream s(buff, QIODevice::ReadWrite);
audio_output->start(&audio_buffer);
qDebug() << buff << endl;
}
buff.clear()
}
And this is my server talk and listen:
void ServerTalk::readSocket()
{
QBluetoothSocket *socket = qobject_cast<QBluetoothSocket *>(sender());
if (!socket)
return;
//QBuffer *buffer;
audio_input->stop();
QByteArray buff;
connect(audio_output, SIGNAL(stateChanged(QAudio::State)), this, SLOT(handleStateChangedOutput(QAudio::State)));
while(socket->canReadLine())
{
buff.append(socket->readLine());
QBuffer audio_buffer(&buff);
audio_buffer.open(QIODevice::WriteOnly);
//QDataStream s(buff, QIODevice::ReadWrite);
audio_output->start(&audio_buffer);
qDebug() << buff << endl;
}
buff.clear();
}
And I have a button to start talk and listen:
connect(btn_talk, SIGNAL(pressed()), server, SLOT(startTalk()));
connect(btn_talk, SIGNAL(released()), server, SLOT(readSocket()));
I would have to send Modbus request for specific data, my problem is that I have to use mobile communication, wifi, connect to a custom electronic card, which is right in Modbus RTU.
My working code connecting to the electronic board:
#include "connessione.h"
#include <QModbusTcpClient>
#include <QVariant>
#include <QModbusDataUnit>
#include <QDebug>
connessione::connessione(QObject *parent) : QObject(parent)
{
qDebug() << "here debug " << "ok";
clientX = new QModbusTcpClient();
clientX->setConnectionParameter(QModbusDevice::NetworkAddressParameter, "192.168.222.1");
clientX->setConnectionParameter(QModbusDevice::NetworkPortParameter, 5555);
if (clientX->connectDevice())
{
qDebug() << "connected: " << clientX->state();
}
else
{
qDebug() << "ERRORE" << clientX->errorString();
}
}
void connessione::clickButton(){
QModbusDataUnit readUnit(QModbusDataUnit::HoldingRegisters, 0, 1); // just read input register 40006
//qDebug() << "readUnit" << readUnit.RegisterType;
qDebug() << "readUnit" << clientX->state();
if (auto *reply = clientX->sendReadRequest(readUnit, 255)) // client id 255
{
if (!reply->isFinished())
{
// connect the finished signal of the request to your read slot
qDebug() << "connected" << reply->errorString();
connect(reply, &QModbusReply::finished, this, &connessione::readReady);
}
else
{
qDebug() << "Errore" << reply->errorString();
delete reply; // broadcast replies return immediately
}
}
else
{
qDebug() << "Errore" << reply->errorString();
// request error
}
}
void connessione::readReady()
{
QModbusReply *reply = qobject_cast<QModbusReply *>(sender());
if (!reply)
return;
if (reply->error() == QModbusDevice::NoError)
{
const QModbusDataUnit unit = reply->result();
int startAddress = unit.startAddress(); // the start address,
int value = unit.value(0); // value of the start address + 0
qDebug() << "NESSUN ERRORE" << reply->errorString();
}
else
{
qDebug() << "Errore readReady" << reply->errorString();
// reply error
}
reply->deleteLater(); // delete the reply
}
log string TCP sent:
D/libmodbusMobile.so( 8042): (null):0 ((null)): qt.modbus: (TCP
client) Sent TCP PDU: 0x0300000001 with tId: 2
this is right: 0x0300000001
But unfortunately, my electronic card, the integrated firmware I can not modify, is right with Modbus RTU, so I should change 0x0300000001 to 0x010300000001C1C2 where C1 and C2 are the checksums.
I believe that QModbusDataUnit generate buffer to send. So how to change it? Exist manual solution where I build the buffer?
how to change it and create custom send buffer like the example?
Thanks
I am using Qt 4.8 GCC 32bit on xUbuntu 14.04.
I have the following piece of code, a TCP server that I use in order to get some remote commands and send back some answers - via TCP socket:
struct _MyRequest
{
unsigned long Request;
unsigned long Data;
} __attribute__((packed));
struct _MyAnswer
{
unsigned long Error;
unsigned long Filler;
} __attribute__((packed));
_MyRequest request;
_MyAnswer answer;
RemoteCmdServer::RemoteCmdServer(QObject * parent)
: QTcpServer(parent)
{
qDebug() << "Server started";
listen(QHostAddress("172.31.250.110"), 5004);
connect(this, SIGNAL(newConnection()), this, SLOT(processPendingRequest()));
}
void RemoteCmdServer::processPendingRequest()
{
qDebug() << "Process request";
QTcpSocket * clientConnection = nextPendingConnection();
connect(clientConnection, SIGNAL(disconnected()), clientConnection, SLOT(deleteLater()));
// get the request
int ret = clientConnection->read((char*)&request, sizeof(request));
qDebug() << "READ: " << ret;
if(ret == sizeof(request))
{
// send answer
clientConnection->write((char*)&answer, sizeof(answer));
}
qDebug() << "Disconnecting...";
clientConnection->disconnectFromHost();
}
I am able to write correctly if I comment the if(ret == sizeof(request)) line.
Yet, I can't read from the socket (I always get 0 bytes).
I am 100% sure that the TCP-tool I use to send packets to my app works ok.
Here is the debug output from my app:
Server started
Process request
READ: 0
Disconnecting...
What am I doing wrong? Please advise!
You should wait for the data either in a non-blocking or blocking way. You can use waitForReadyRead to do it in a blocking way.
void RemoteCmdServer::processPendingRequest()
{
qDebug() << "Process request";
QTcpSocket * clientConnection = nextPendingConnection();
connect(clientConnection, SIGNAL(disconnected()), clientConnection, SLOT(deleteLater()));
if (clientConnection->waitForReadyRead())
{
// get the request
QByteArray message = clientConnection->readAll(); // Read message
qDebug() << "Message:" << QString(message);
}
else
{
qDebug().nospace() << "ERROR: could not receive message (" << qPrintable(clientConnection->errorString()) << ")";
}
qDebug() << "Disconnecting...";
clientConnection->disconnectFromHost();
}
You're trying to read data from the new connection without returning to the Qt event loop -- I don't think that's going to work.
After you've accepted the connect with...
QTcpSocket * clientConnection = nextPendingConnection();
You need to connect to its readyRead signal with something like...
connect(clientConnection, SIGNAL(readyRead()), this, SLOT(my_read_slot()));
Where my_read_slot is the member function that will actually perform the read operation.
I am programming a real-time game in which I need reliable UDP, so I've chosen to work with UDT (UDP-based data transfer protocol - http://sourceforge.net/projects/udt/).
The clients (on browsers) send real-time messages to my server via CGI scripts. The problem is that there are some messages that are being lost, and I don't know why because the server says that it sent all the messages successfully to the corresponding clients, but sometimes the client doesn't receive the message.
In my debug file, I've found that when a message is not received by the client, its script says:
error in recv();
recv: Connection was broken.
I would like to get some help on how the server shall know if the client got its message; should I send a NACK or something from the client side? I thought that UDT should do that for me. Can someone clarify this situation?
The relevant sections of the communication parts of my code are bellow, with some comments:
server's relevant code:
//...
void send_msg_in(player cur, char* xml){
/*this function stores the current message, xml, in a queue if xml!=NULL, and sends the 1st message of the queue to the client*/
/*this function is called when the player connects with the entering xml=NULL to get the 1st message of the queue,
or with xml!=NULL when a new message arrives: in this case the message is stored in the queue, and then the message will be sent in the appropriate time, i.e. the messages are ordered.*/
char* msg_ptr=NULL;
if (xml!=NULL){ //add the message to a queue (FIFO), the cur.xml_msgs
msg_ptr=(char*) calloc(strlen(xml)+1, sizeof(char));
strcpy(msg_ptr, xml);
(*(cur.xml_msgs)).push(msg_ptr);
} //get the 1st message of the queue
if (!(*(cur.xml_msgs)).empty()){
xml=(*(cur.xml_msgs)).front();
}
if (cur.get_udt_socket_in()!=NULL){
UDTSOCKET cur_udt = *(cur.get_udt_socket_in());
// cout << "send_msg_in(), cur_udt: " << cur_udt << endl;
//send the "xml", i.e. the 1st message of the queue...
if (UDT::ERROR == UDT::send(cur_udt, xml, strlen(xml)+1, 0)){
UDT::close(cur_udt);
cur.set_udt_socket_in(NULL);
}
else{ //if no error this else is reached
cout << "TO client:\n" << xml << "\n"; /*if there is no error,
i.e. on success, the server prints the message that was sent.*/
// / \
// /_!_\
/*the problem is that
the messages that are lost don't appear on the client side,
but they appear here on the server! */
if (((string) xml).find("<ack.>")==string::npos){
UDT::close(cur_udt);
cur.set_udt_socket_in(NULL); //close the socket
}
(*(cur.xml_msgs)).pop();
}
}
}
//...
client's relevant code:
//...
#define MSGBUFSIZE 1024
char msgbuf[MSGBUFSIZE];
UDTSOCKET client;
ofstream myfile;
//...
main(int argc, char *argv[]){
//...
// connect to the server, implict bind
if (UDT::ERROR == UDT::connect(client, (sockaddr*)&serv_addr, sizeof(serv_addr))){
cout << "error in connect();" << endl;
return 0;
}
myfile.open("./log.txt", ios::app);
send(xml);
char* cur_xml;
do{
cur_xml = receive(); //wait for an ACK or a new message...
myfile << cur_xml << endl << endl; // / \
/* /_!_\ the lost messages don't appear on the website
neither on this log file.*/
} while (((string) cur_xml).find("<ack.>")!=string::npos);
cout << cur_xml << endl;
myfile.close();
UDT::close(client);
return 0;
}
char* receive(){
if (UDT::ERROR == UDT::recv(client, msgbuf, MSGBUFSIZE, 0)){
// / \
/* /_!_\ when a message is not well received
this code is usually reached, and an error is printed.*/
cout << "error in recv();" << endl;
myfile << "error in recv();" << endl;
myfile << "recv: " << UDT::getlasterror().getErrorMessage() << endl << endl;
return 0;
}
return msgbuf;
}
void* send(string xml){
if (UDT::ERROR == UDT::send(client, xml.c_str(), strlen(xml.c_str())+1, 0)){
cout << "error in send();" << endl;
myfile << "error in send();" << endl;
myfile << "send: " << UDT::getlasterror().getErrorMessage() << endl << endl;
return 0;
}
}
Thank you for any help!
PS. I tried to increase the linger time on close(), after finding the link http://udt.sourceforge.net/udt4/doc/opt.htm, adding the following to the server's code:
struct linger l;
l.l_onoff = 1;
l.l_linger = ...; //a huge value in seconds...
UDT::setsockopt(*udt_socket_ptr, 0, UDT_LINGER, &l, sizeof(l));
but the problem is still the same...
PPS. the other parts of the communication in the server side are: (note: it seams for me that they are not so relevant)
main(int argc, char *argv[]){
char msgbuf[MSGBUFSIZE];
UDTSOCKET serv = UDT::socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in my_addr;
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(PORT);
my_addr.sin_addr.s_addr = INADDR_ANY;
memset(&(my_addr.sin_zero), '\0', sizeof(my_addr.sin_zero));
if (UDT::ERROR == UDT::bind(serv, (sockaddr*)&my_addr, sizeof(my_addr))){
cout << "error in bind();";
return 0;
}
UDT::listen(serv, 1);
int namelen;
sockaddr_in their_addr;
while (true){
UDTSOCKET recver = UDT::accept(serv, (sockaddr*)&their_addr, &namelen);
if (UDT::ERROR == UDT::recv(recver, msgbuf, MSGBUFSIZE, 0)){
//this recv() function is called only once for each aqccept(), because the clients call CGI scripts via a browser, they need to call a new CGI script with a new UDT socket for each request (this in in agreement to the clients' code presented before).
cout << "error in recv();" << endl;
}
char* player_xml = (char*) &msgbuf;
cur_result = process_request((char*) &msgbuf, &recver, verbose); //ACK
}
}
struct result process_request(char* xml, UDTSOCKET* udt_socket_ptr, bool verbose){
//parse the XML...
//...
player* cur_ptr = get_player(me); //searches in a vector of player, according to the string "me" of the XML parsing.
UDTSOCKET* udt_ptr = (UDTSOCKET*) calloc(1, sizeof(UDTSOCKET));
memcpy(udt_ptr, udt_socket_ptr, sizeof(UDTSOCKET));
if (cur_ptr==NULL){
//register the player:
player* this_player = (player*) calloc(1, sizeof(player));
//...
}
}
else if (strcmp(request_type.c_str(), "info_waitformsg")==0){
if (udt_ptr!=NULL){
cur_ptr->set_udt_socket_in(udt_ptr);
if (!(*(cur_ptr->xml_msgs)).empty()){
send_msg_in(*cur_ptr, NULL, true);
}
}
}
else{ //messages that get instant response from the server.
if (udt_ptr!=NULL){
cur_ptr->set_udt_socket_out(udt_ptr);
}
if (strcmp(request_type.c_str(), "info_chat")==0){
info_chat cur_info;
to_object(&cur_info, me, request_type, msg_ptr); //convert the XML string values to a struct
process_chat_msg(cur_info, xml);
}
/* else if (...){ //other types of messages...
}*/
}
}
void process_chat_msg(info_chat cur_info, char* xml_in){
player* player_ptr=get_player(cur_info.me);
if (player_ptr){
int i=search_in_matches(matches, cur_info.match_ID);
if (i>=0){
match* cur_match=matches[i];
vector<player*> players_in = cur_match->followers;
int n=players_in.size();
for (int i=0; i<n; i++){
if (players_in[i]!=msg_owner){
send_msg_in(*(players_in[i]), xml, flag);
}
}
}
}
}
Looking at the UDT source code at http://sourceforge.net/p/udt/git/ci/master/tree/udt4/src/core.cpp, the error message "Connection was broken" is produced when either of the Boolean flags m_bBroken or m_bClosing is true and there is no data in the receive buffer.
Those flags are set in just a few cases:
In sections of code marked "should not happen; attack or bug" (unlikely)
In deliberate close or shutdown actions (don't see this happening in your code)
In expiration of a timer that checks for peer activity (the likely culprit)
In that source file at line 2593 it says:
// Connection is broken.
// UDT does not signal any information about this instead of to stop quietly.
// Application will detect this when it calls any UDT methods next time.
//
m_bClosing = true;
m_bBroken = true;
// ...[code omitted]...
// app can call any UDT API to learn the connection_broken error
Looking at the send() call, I don't see anywhere that it waits for an ACK or NAK from the peer before returning, so I don't think a successful return from send() on the server side is indicative of successful receipt of the message by the client.
You didn't show the code on the server side that binds to the socket and listens for responses from the client; if the problem is there then the server might be happily sending messages and never listening to the client that is trying to respond.
UDP is not a guaranteed-transmission protocol. A host will send a message, but if the recipient does not receive it, or if it is not received properly, no error will be raised. Therefore, it is commonly used in applications that require speed over perfect delivery, such as games. TCP does guarantee delivery, because it requires that a connection be set up first, and each message is acknowledged by the client.
I would encourage you to think about whether you actually need guaranteed receipt of that data, and, if you do, consider using TCP.
UPDATE:
Looking through the protocol here, I can't figure out what goes into the Unsized Envelope Record. I can't find any examples online.
ORIGINAL:
I have the following WCF service
static void Main(string[] args)
{
var inst = new PlusFiver();
using (ServiceHost host = new ServiceHost(inst,
new Uri[] { new Uri("net.pipe://localhost") }))
{
host.AddServiceEndpoint(typeof(IPlusFive), new NetNamedPipeBinding(NetNamedPipeSecurityMode.None), "PipePlusFive");
host.Open();
Console.WriteLine("Service is Available. Press enter to exit.");
Console.ReadLine();
host.Close();
}
}
[ServiceContract]
public interface IPlusFive
{
[OperationContract]
int PlusFive(int value);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class PlusFiver : IPlusFive
{
public int PlusFive(int value)
{
Console.WriteLine("Adding 5 to " + value);
return value + 5;
}
}
I output the adding 5 line so I know if the server processed the
request or not.
I have a .NET client that I used to test this and everything works as
expected.
Now I want to make an unmanaged C++ client for this.
I figured out how to get the name of the pipe, and write to it.
I've downloaded the protocol from here
I can write to the pipe but I can't read to it. Whenever I try to read from it I get a ERROR_BROKEN_PIPE 109 (0x6D) The pipe has been ended. error. If I replace the read with a write, the write is successful, so I don't think that the pipe is closed, at least not until I try to do a read.
Here is how I'm connecting to the pipe.
HANDLE OpenPipe(OLECHAR* bstrGuid)
{
wstring pipeName = L"\\\\.\\pipe\\";
wstring strGuid = bstrGuid;
pipeName.append(strGuid.substr(1,36));
wcout << "Pipe Name " << endl;
wcout << pipeName.c_str() << endl;
HANDLE hPipe = CreateFile(pipeName.c_str(), GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
if(hPipe == INVALID_HANDLE_VALUE)
{
wcout << "failed to create pipe" << endl;
system("pause");
return NULL;
}
return hPipe;
}
this is how i'm creating the first message that I'm sending
std::list<wchar_t> GetFirstMessage()
{
std::list<wchar_t> message;
message.push_back(0x00);// version record
message.push_back(0x01);// major version
message.push_back(0x00);// minor version
message.push_back(0x01);// mode record
message.push_back(0x01);// singleton-unsized mode
message.push_back(0x02);// via record
wstring url = L"net.pipe://localhost/PipePlusFive";
message.push_back(url.length());// via length
for(int x= 0;x<url.length();x++)
{
message.push_back(url[x]); // via
}
message.push_back(0x03);
message.push_back(0x08);
return message;
}
This is how I'm writing it to the file.
int WriteMessage(HANDLE hPipe, LPVOID message, int size)
{
DWORD bytesWritten;
BOOL bWrite = WriteFile(hPipe, &message, size, &bytesWritten, NULL);
wcout << "Bytes Written: " << bytesWritten << endl;
if(bWrite == false)
{
wcout << "fail"<<endl;
CloseHandle(hPipe);
system("pause");
return 1;
}
return 0;
}
list<wchar_t> full_message = GetFirstMessage();
int result = WriteMessage(hPipe, &full_message, full_message.size());
if (result == 1)
{ return 1;}
Here is how I'm writing the end message
wchar_t message = 12;
result = WriteMessage(hPipe, &message, 1);
if (result == 1)
{ return 1;}
here is how I'm trying to read the response
char buffer[10];
DWORD bytesRead;
BOOL bRead = ReadFile(hPipe, buffer, 1, &bytesRead, NULL);
if(bRead == false)
{
wcout << "fail read"<<endl;
wcout << "error: " << GetLastError() << endl;
CloseHandle(hPipe);
system("pause");
return 1;
}
I'm new to c++, so I don't know if I'm not following the protocol correctly or making a stupid mistake in the way I'm trying to do this?
UPDATE:
The problem was that I was writing the pointer address to the named pipe instead of the contents of the list. I've fixed that And I'm now able to read the Preamble Ack Record. Now I have to figure out what needs to be sent for the next part of the protocol.
Check if this works for you
Try to open a named pipe. (CreateFile)
Set the read mode and the blocking mode of the specified named pipe. (SetNamedPipeHandleState)
Send a message to the pipe server and receive its response. (WriteFile, ReadFile)
Close the pipe. (CloseHandle)