我正在使用UDP客户端/服务器上的几个应用程序,并且我的发送命令的应用程序运行非常出色-我可以监视通过nc和hexdump发送到端口的内容,并且它们可以完美解码。
在应该接收命令的应用程序上,我使用带有MSG_DONTWAIT标志的recvfrom。我这样做是因为我还需要检查队列中是否有要发送的东西,因此只能将其保留为阻塞状态。如果我删除了MSG_DONTWAIT标志,则可以正确接收和处理消息,但是它将阻止等待,这对我的应用程序无效。使用MSG_DONTWAIT时,它始终返回-1并将errno设置为EAGAIN。虽然什么也没发送就可以预料到,但是它从不接收任何东西。我认为它将返回EAGAIN,直到有可用的东西为止,但事实并非如此。相关代码如下:我想念什么?
uint8_t Receiver::Setup(uint16_t rx_port, uint16_t tx_port)
{
std::stringstream ss;
ss << "UDP session manager, setup ports.";
Logger::Info(ss.str());
tx_socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
rx_socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (rx_socket_fd < 0)
{
Logger::Error("Could not open an rx UDP socket!");
}
else
{
std::cout << "rx_socket_fd is " << rx_socket_fd << "\n";
}
if (tx_socket_fd < 0)
{
Logger::Error("Could not open an tx UDP socket!");
}
else
{
std::cout << "tx_socket_fd is " << tx_socket_fd << "\n";
}
int reuse = 1;
if (setsockopt(tx_socket_fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) < 0)
Logger::Warn("Could not set socket reuse!");
#ifdef SO_REUSEPORT
reuse = 1;
if (setsockopt(tx_socket_fd, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuse, sizeof(reuse)) < 0)
Logger::Warn("setsockopt(SO_REUSEPORT) failed");
#endif
reuse = 1;
if (setsockopt(rx_socket_fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) < 0)
Logger::Warn("Could not set socket reuse!");
#ifdef SO_REUSEPORT
reuse = 1;
if (setsockopt(rx_socket_fd, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuse, sizeof(reuse)) < 0)
Logger::Warn("setsockopt(SO_REUSEPORT) failed");
#endif
memset(&tx_sockaddr, 0, sizeof(tx_sockaddr));
memset(&rx_sockaddr, 0, sizeof(rx_sockaddr));
tx_sockaddr.sin_family = AF_INET;
tx_sockaddr.sin_addr.s_addr = INADDR_ANY;
tx_sockaddr.sin_port = htons(tx_port);
rx_sockaddr.sin_family = AF_INET;
rx_sockaddr.sin_addr.s_addr = INADDR_ANY;
rx_sockaddr.sin_port = htons(rx_port);
int rva = 0;
rva = bind(tx_socket_fd, (const struct sockaddr *) &tx_sockaddr, sizeof(tx_sockaddr) );
if (rva < 0)
{
std::stringstream ss;
ss << "UDP SessionManager: Could not bind to tx socket (bind returned error code " << rva << ", errno is " << errno << ")";
Logger::Error(ss.str());
}
rva = bind(rx_socket_fd, (const struct sockaddr *) &rx_sockaddr, sizeof(rx_sockaddr) );
if (rva < 0)
{
std::stringstream ss;
ss << "UDP SessionManager: Could not bind to rx socket (bind returned error code " << rva << ", errno is " << errno << ")";
Logger::Error(ss.str());
}
return NO_ERROR;
}
uint8_t Receiver::SendTelemetry(const TelemetryBase * telemetry)
{
const uint8_t * bytes = EncodeTelemetryToSend(telemetry);
if (bytes == NULL)
{
Logger::Error("Receiver: Something went wrong trying to encode the telemetry.");
return 1;
}
const UDPHeader * header = (const UDPHeader * ) bytes;
uint16_t numBytes = header->length;
std::stringstream ss;
ss << "Receiver::SendTelemetry - bytesToWrite is " << numBytes << "\n";
Logger::Info(ss.str());
int rva = sendto(tx_socket_fd, (const char *) bytes, numBytes, 0, (const struct sockaddr *) &tx_sockaddr, sizeof(struct sockaddr_in) );
std::this_thread::sleep_for(std::chrono::milliseconds(10));
if (rva == -1 && errno == EINVAL)
{
ss.clear();
ss << "invalid argument!";
Logger::Warn(ss.str());
}
else if (rva < 0)
{
ss.clear();
ss << "Failed to write to the UDP port, errno is " << errno;
Logger::Warn(ss.str());
return 1;
}
delete bytes;
return 0;
}
uint8_t Receiver::SendCommand(const CommandBase * command)
{
const uint8_t * bytes = EncodeCommandToSend(command);
if (bytes == NULL)
{
Logger::Error("Receiver: Something went wrong trying to encode the message.");
return 1;
}
const UDPHeader * header = (const UDPHeader * ) bytes;
uint16_t numBytes = header->length;
std::stringstream ss;
ss << "Receiver::SendCommand - bytesToWrite is " << numBytes << "\n";
Logger::Info(ss.str());
int rva = sendto(tx_socket_fd, (const char *) bytes, numBytes, 0, (const struct sockaddr *) &tx_sockaddr, sizeof(struct sockaddr_in) );
std::this_thread::sleep_for(std::chrono::milliseconds(10));
if (rva < 0)
{
ss.clear();
ss << "Failed to write to the UDP port, errno is " << errno;
Logger::Warn(ss.str());
return 1;
}
delete bytes;
return 0;
}
uint8_t Receiver::Receive()
{
uint8_t inputBuffer[UDP_BUFFER_BYTES];
memset(inputBuffer, '\0', UDP_BUFFER_BYTES);
int totalBytesRead = 0;
//socklen_t addressLength = sizeof(rx_sockaddr);
struct sockaddr_in sender;
socklen_t len;
totalBytesRead = recvfrom(rx_socket_fd, (char *) inputBuffer, UDP_BUFFER_BYTES,
MSG_DONTWAIT, (struct sockaddr *) &sender, &len );
if ( totalBytesRead >= 0 )
{
std::stringstream ss;
ss << "UDP port read " << totalBytesRead << " bytes";
Logger::Info(ss.str() );
const CommandBase * command = DecodeReceivedCommand(inputBuffer);
if (command == NULL)
{
Logger::Warn("Failed to decode received command from commanding app.");
return UDP_ERROR_DECODE_FAILED;
}
EnqueCommand(command);
}
else
{
std::stringstream ss;
ss << "UDP port rva = " << totalBytesRead << ", errno is " << errno;
Logger::Debug(ss.str());
}
return UDP_ERROR_NO_ERROR;
}
void Receiver::ProcessingLoopThread()
{
while ( GetState() == STATE_RUN )
{
const TelemetryBase * telemetry = DequeTelemetry();
while (telemetry != NULL)
{
std::stringstream ss;
ss << "Receiver sending telemetry with ID: " << telemetry->GetTelemetryID();
Logger::Debug(ss.str());
SendTelemetry(telemetry);
delete telemetry;
telemetry = DequeTelemetry();
}
Receive();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
答案 0 :(得分:1)
几件事:
这可能不是问题,但是我鼓励您在任何其他代码有机会运行并清除errno之前捕获errno。代替这个:
std::stringstream ss;
ss << "UDP port rva = " << totalBytesRead << ", errno is " << errno;
更好:
totalBytesRead = recvfrom(rx_socket_fd,...
int lasterror = errno; // catch errno before anything else can change it
. . .
ss << "UDP port rva = " << totalBytesRead << ", errno is " << lasterror;
回到原始问题。
我猜想在使用非阻塞MSG_DONTWAIT标志时,您需要多次轮询套接字。
看起来您的主循环在每次套接字轮询之间睡眠10毫秒。如果那是您的设计,那么只需执行以下操作即可:
创建套接字时,在其上设置10毫秒的超时时间:
timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 10 * 1000; // 10 milliseconds
setsockopt(rx_socket_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
然后只需从recvfrom调用中删除MSG_DONTWAIT
标志即可。
此外,在主循环中删除sleep语句:
std::this_thread::sleep_for(std::chrono::milliseconds(10));
然后将超时错误作为可能发生的良性事件进行处理
totalBytesRead = recvfrom(rx_socket_fd, (char *) inputBuffer, UDP_BUFFER_BYTES,
0, (struct sockaddr *) &sender, &len );
if (totalBytesRead >= 0 )
{
// data available - handle it
}
else
{
if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
{
// socket timed out after waiting 10 milliseconds
}
else
{
// actual socket error
}
}
答案 1 :(得分:1)
<?php
require 'db.php';
$mysqli2 = new mysqli('localhost','x','x','x');
session_start();
$_SESSION['message'] ='';
if (isset($_POST['username'])){
/* User login process, checks if user exists and password is correct */
// Escape email to protect against SQL injections
$username= htmlentities($_POST['username']);
$username = $mysqli->real_escape_string($username);
$result = $mysqli->query("SELECT * FROM accounts WHERE username ='$username'");
if ($result->num_rows == 0){ // User doesn't exist
$_SESSION['message'] = "You should register !";
header("location: error.php");
}
else { // User exists
$accounts = $result->fetch_assoc();
$password= htmlentities($_POST['password']);
$password = $mysqli->real_escape_string($password);
if ( password_verify($password, $accounts['password']) ) {
$_SESSION['username'] = $accounts['username'];
$_SESSION['usercode'] = $accounts['usercode'];
$usercode= $_SESSION['usercode'];
$_SESSION['email'] = $accounts['email'];
$_SESSION['active'] = $accounts['active'];
$result2 = $mysqli2->query("SELECT * FROM account_info WHERE usercode ='$usercode'");
$account_info = $result2->fetch_assoc();
$_SESSION['name']= $account_info['name'];
$_SESSION['lastname']= $account_info['lastname'];
$_SESSION['location']= $account_info['location'];
$_SESSION['phone'] = $account_info['phone'];
$_SESSION['balance'] = $account_info['balance'];
// This is how we'll know the user is logged in
$_SESSION['logged_in'] = true;
header("location: index.php");
}
else {
$_SESSION['message'] = "You have entered wrong password, try again!";
header("location: error.php");
}
}
}
?>
您没有为struct sockaddr_in sender;
socklen_t len;
totalBytesRead = recvfrom(rx_socket_fd, (char *) inputBuffer, UDP_BUFFER_BYTES,
MSG_DONTWAIT, (struct sockaddr *) &sender, &len );
分配一个明智的值。如果不将len
初始化为套接字地址的大小,则调用可能会失败。
此外,您捕获len
的时间也太晚了。您必须在收到错误的调用后立即捕获它。否则,其他中介操作可以更改其值。因此,您不能依赖于获得明智的价值。
您使用单独的套接字进行发送和接收非常奇怪。如果要发送到同一端点,则应该只使用一个套接字。
答案 2 :(得分:-1)
MSG_DONTWAIT(从 Linux 2.2 开始) 启用非阻塞操作;如果操作会阻塞,则调用失败并显示 错误 EAGAIN 或 EWOULDBLOCK(这也可以使用 O_NONBLOCK 标志启用 F_SETFL fcntl(2)).