16

根据 Qt 文档,如果我们想在 Windows 上使用命名管道,我们可以使用 QLocalSocket。我正在用 Qt 编写服务器和客户端程序。如果我尝试使用 WIN32 API 在管道中写入一些消息,Qt 客户端不会显示它。此外,如果客户端再次使用 WIN32 API 写入,Qt 服务器不会回显发送的消息。QLocalSocket 真的推荐用于命名管道吗?

这是 Win32 服务器代码

     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;

这是 Win32 客户端:

    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;

这是 Qt 服务器端

   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");
}

这是 Qt 客户端

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"));
}
4

1 回答 1

1

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.)

于 2016-08-17T17:24:07.063 回答