2017-08-11 25 views
0

我写了一个基于UNIX域套接字进行通信的C++服务器和PHP客户端。在任何一个方向阅读或写作都很好。一旦我尝试读取AND然后写入(反之亦然),通信不会结束:客户端在一段时间后返回“504网关超时”,并且服务器在打印“等待认证...”后仍然等待”。UNIX域套接字服务器和客户端不能双向通信

PHP客户端:

<?php 
$sock = socket_create(AF_UNIX, SOCK_STREAM, 0); 
$conn = socket_connect($sock, '/tmp/some.socket'); 


$pass = "pass\0"; 

if ($sock == FALSE) 
    echo "Error: <br /> $errstr ($errno)<br />\n"; 
else 
{ 
    echo "Sending the password... "; 

    $ret = socket_write($sock, $pass, strlen($pass)); 

    if ($ret == FALSE) 
     echo "error! " . socket_strerror(socket_last_error()); 
    else 
    { 
     echo "Password was sent.<br /> "; 

     $auth = socket_read($sock, 256); 

     if (FALSE === $auth) 
      echo "sending password failed; reason: " . socket_strerror(socket_last_error($sock)) . "\n"; 
     else if ($auth != "authenticated") 
      echo "Authentication failed: $auth."; 
     else 
      echo "Authentication was successful. <br />"; 
    } 
} 

socket_close($sock); 

?> 

主服务器CPP文件:

#include <iostream> 

#include "UnixDomainSocket.hpp" 


int main() 
{ 
    try 
    { 
     UnixDomainSocket uds("/tmp/some.socket"); 

     std::cout << "Server started." << std::endl; 

     while (true) 
     { 
      //if a new connection stablished, read and process data 
      if (uds.newConnectionEstablished()) 
      { 
       std::cout << "Got a new connection. Waiting for authentication..." << std::endl; 

       std::string command = uds.getClientMsg().getValue(); 

       if (command != "pass") 
       { 
        std::cout << "401" << std::endl; 
        uds.sendMsg("401"); 
       } 
       else 
       { 
        std::cout << "authenticated" << std::endl; 

        auto msgRet = uds.sendMsg("authenticated"); 
       } 

       uds.closeConnection(); 
      } 
     } 
    } 
    catch (const std::string & err) 
    { 
     std::cout << "Error: " << err << std::endl; 

     return -1; 
    } 
    catch (const std::exception & err) 
    { 
     std::cout << "Error: " << std::string(err.what()) << std::endl; 

     return -1; 
    } 
    catch (...) 
    { 
     std::cout << "Unhandled error occured. Daemon stopped." << std::endl; 

     return -1; 
    } 
    std::cout << "Server shut down." << std::endl; 
} 

Server头:

#ifndef UNIXDOMAINSOCKET_HPP 
#define UNIXDOMAINSOCKET_HPP 

#include <sys/un.h> 
#include <sys/socket.h> 
#include <unistd.h> 

#include <string> 

#include "Expected.hpp" 


const int BUFFSIZE = 1024; 

class UnixDomainSocket 
{ 
public: 
    UnixDomainSocket (const std::string & socketPath); 
    ~UnixDomainSocket(); 

    bool newConnectionEstablished(); 
    Expected<std::string> getClientMsg(); 
    Expected<bool> sendMsg (const std::string & msg); 
    void closeConnection(); 
    void closeConnection (const std::string & quitMessage); 

protected: 
    std::string socketPath; 
    unsigned long maxConnections; 
    bool connectionEstablished; 

    struct sockaddr_un addr; 
    int serverFileDescriptor, clientFileDescriptor; 
    ssize_t bytes; 
    char buf[BUFFSIZE]; 
}; 

#endif 

服务器CPP:

#include "UnixDomainSocket.hpp" 

#include <iostream> 


UnixDomainSocket::UnixDomainSocket (const std::string & socketPath) 
    : socketPath(socketPath), maxConnections(100) 
{ 
    if ((serverFileDescriptor = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) 
     throw "socket error"; 

    memset (&addr, 0, sizeof(addr)); 

    //ensure that all fields, including non−standard ones, are initialized to 0 
    addr.sun_family = AF_UNIX; 

    //we copy one byte less, ensuring a trailing 0 exists 
    strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1); 


    if (access(addr.sun_path, F_OK) == 0) 
     unlink(addr.sun_path); 

    if (bind(serverFileDescriptor, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) 
     throw "bind error"; 

    if (listen(serverFileDescriptor, maxConnections) < 0) 
     throw "listen error"; 
} 

UnixDomainSocket::~UnixDomainSocket() 
{ 
    closeConnection(); 
} 

bool UnixDomainSocket::newConnectionEstablished() 
{ 
    if ((clientFileDescriptor = accept(serverFileDescriptor, NULL, NULL)) < 0) 
     throw "accept error"; 

    connectionEstablished = true; 

    return true; 
} 

Expected<std::string> UnixDomainSocket::getClientMsg() 
{ 
    if (!connectionEstablished) 
     return Expected<std::string>::fromException(std::logic_error("No connection established yet.")); 

    std::string msg; 

    while ((bytes = read (clientFileDescriptor, buf, BUFFSIZE)) > 0) 
     msg += buf; 

    // if (msg.length()) 
    //  throw "empty msg from client"; 

    if (bytes < 0) 
     throw "read error"; 

    return msg; 
} 

Expected<bool> UnixDomainSocket::sendMsg (const std::string & msg) 
{ 
    if (!connectionEstablished) 
     return Expected<bool>::fromException(std::logic_error("No connection established yet.")); 

    if (msg.empty()) 
     return Expected<bool>::fromException(std::logic_error("The message must be not empty.")); 


    auto bytesSent = send(clientFileDescriptor, (msg + "\n").c_str(), msg.length(), MSG_CONFIRM); 

    ////Also tried: 
    // long bytesSent = -1; 
    // while (bytesSent < 0) 
    //  bytesSent = write(clientFileDescriptor, msg.c_str(), msg.length()); 

    if (bytesSent != msg.length()) 
     return Expected<bool>::fromException(std::logic_error("Error occured while sending.")); 

    return true; 
} 


void UnixDomainSocket::closeConnection (const std::string & quitMessage) 
{ 
    sendMsg(quitMessage); 
    closeConnection(); 
} 

void UnixDomainSocket::closeConnection() 
{ 
    if (close(clientFileDescriptor) < 0) 
     throw "close error"; 

    connectionEstablished = false; 
} 
+2

'getClientMsg()'一直保持读取状态直到出现错误('bytes <0')或EOF('bytes == 0')。但是客户端在发送密码后没有关闭连接,所以它永远不会得到EOF。它只是阻止,等待更多的输入。 – Barmar

+0

您需要某种方式在流中分隔消息,'getClientMsg()'应该读取一条消息。 – Barmar

+0

我添加了一个循环来接收比“socket_write()”中的最大缓冲区大小更长的消息... –

回答

1

问题是您在

getClientMsg() { 
... 
while ((bytes = read (clientFileDescriptor, buf, BUFFSIZE)) > 0) 
    msg += buf; 
... 

read() 

的实施,功能股(再次)曾经的“通行证”被成功接收。因此,while循环中,你需要检查的每个

send() 

我修改你的代码一点点的边界和它的作品现在发现:

while ((bytes = read (clientFileDescriptor, buf, BUFFSIZE)) > 0) 
{ 
    msg += buf; 

    if (buf[bytes] == 127) break; // completion of one send() 
} 

server screenshot

client screenshot

我注意到的另一件事是,在主循环的每次迭代结束时关闭套接字:

uds.closeConnection(); 

这将禁用未来之间的通信。因此,最好删除这一行。