UNIX 域套接字服务器和客户端未通信 bi-directionally
UNIX domain socket server and client not communicating bi-directionally
我写了一个 C++ 服务器和一个 PHP 客户端,它们都基于 UNIX 域套接字进行通信。在任一方向上阅读或写作都很好。一旦我尝试读取然后写入(反之亦然),通信就不会完成:一段时间后客户端 returns “504 网关 Time-out” 并且服务器在打印后一直等待 "Waiting for authentication...".
PHP 客户:
<?php
$sock = socket_create(AF_UNIX, SOCK_STREAM, 0);
$conn = socket_connect($sock, '/tmp/some.socket');
$pass = "pass[=11=]";
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;
}
服务器header:
#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;
}
问题是你的
的执行
getClientMsg() {
...
while ((bytes = read (clientFileDescriptor, buf, BUFFSIZE)) > 0)
msg += buf;
...
其中,函数库存(再次)在
read()
一旦 "pass" 成功接收。所以在 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();
这将禁用两者之间的未来通信。因此,最好删除此行。
我写了一个 C++ 服务器和一个 PHP 客户端,它们都基于 UNIX 域套接字进行通信。在任一方向上阅读或写作都很好。一旦我尝试读取然后写入(反之亦然),通信就不会完成:一段时间后客户端 returns “504 网关 Time-out” 并且服务器在打印后一直等待 "Waiting for authentication...".
PHP 客户:
<?php
$sock = socket_create(AF_UNIX, SOCK_STREAM, 0);
$conn = socket_connect($sock, '/tmp/some.socket');
$pass = "pass[=11=]";
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;
}
服务器header:
#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;
}
问题是你的
的执行getClientMsg() {
...
while ((bytes = read (clientFileDescriptor, buf, BUFFSIZE)) > 0)
msg += buf;
...
其中,函数库存(再次)在
read()
一旦 "pass" 成功接收。所以在 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();
这将禁用两者之间的未来通信。因此,最好删除此行。