Select() 在程序中实施错误,从服务器获取超时
Select() implemented wrong in program, getting timeouts from server
传奇中的第三个问题:How to correctly implement select to correctly get data from stdin and recv()。我建议阅读这个和它链接的另一个问题以了解情况。
基本上,我自己尝试实现了 select()
。我的代码:
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <stdlib.h>
using namespace std;
int main (int argc, char** argv) {
if (argv[1] == NULL) {
cout << "3[31mTARGET NOT SPECIFIED - TERMINATING...3[0m\n";
return -1;
}
if (argv[2] == NULL) {
cout << "3[31mPORT NOT SPECIFIED - TERMINATING...3[0m\n";
return -2;
}
string target = argv[1];
int port = atoi(argv[2]);
cout << "GENERATING SOCKET...\n";
int chatter = socket(AF_INET, SOCK_STREAM, 0);
if (chatter == -1) {
cout << "3[31mSOCKET GENERATION FAILURE - TERMINATING...3[0m\n";
return -3;
}
cout << "3[32mSUCCESSFULLY GENERATED SOCKET3[0m\n";
struct sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(port);
inet_pton(AF_INET, target.c_str(), &hint.sin_addr);
struct timeval tv;
tv.tv_usec = 0.0;
tv.tv_sec = 5;
int recval;
cout << "CONNECTING TO " << target << " AT PORT " << port << "...\n";
int connection_status = connect(chatter, (sockaddr*)&hint, sizeof(hint));
if (connection_status == -1) {
cout << "3[31mCONNECTION FAILURE - TERMINATING...3[0m\n";
return -4;
}
cout << "3[32mCONNECTED TO HOST3[0m\n";
char buf[4096] = {0};
string msg;
while (true) {
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(chatter, &rfds);
getline(cin, msg);
msg+"\r\n";
int sendmsg = send(chatter, msg.c_str(), msg.size()+1, 0);
if (sendmsg == -1) {
cout << "3[31mMESSAGE SENDING FAILURE - TERMINATING...3[0m\n";
return -5;
}
recval = select(chatter + 1, &rfds, NULL, NULL, &tv);
switch(recval) {
case(0):
cout << "3[31mTIMEOUT3[0m\n";
break;
case(-1):
cout << "3[31mERROR3[0m\n";
break;
default:
if (recv(chatter, buf, 4096, 0) < 0) {
cout << "3[31mFAILURE TO RECEIVE MESSAGE - TERMINATING...3[0m\n";
return -6;
} else {
cout << recv(chatter, buf, 4096, 0) << "\n";
cout << buf << "\n";
}
break;
}
}
close(chatter);
return 0;
}
在 scanme.nmap.org
和我的 HTTP 服务器上尝试该程序时,我一直收到 TIMEOUT
。我做错了什么?
此时,在解决了用户在第一个问题中指出的问题后,我知道我发送数据的方式没有问题。程序处理从 getline()
/recv()
.
获取数据的方式存在问题
编辑:新的、改进的、工作代码感谢回答者
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <stdlib.h>
using namespace std;
int main (int argc, char** argv) {
if (argv[1] == NULL) {
cout << "3[31mTARGET NOT SPECIFIED - TERMINATING...3[0m\n";
return -1;
}
if (argv[2] == NULL) {
cout << "3[31mPORT NOT SPECIFIED - TERMINATING...3[0m\n";
return -2;
}
string target = argv[1];
int port = atoi(argv[2]);
cout << "GENERATING SOCKET...\n";
int chatter = socket(AF_INET, SOCK_STREAM, 0);
if (chatter == -1) {
cout << "3[31mSOCKET GENERATION FAILURE - TERMINATING...3[0m\n";
return -3;
}
cout << "3[32mSUCCESSFULLY GENERATED SOCKET3[0m\n";
struct sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(port);
inet_pton(AF_INET, target.c_str(), &hint.sin_addr);
int recval;
cout << "CONNECTING TO " << target << " AT PORT " << port << "...\n";
int connection_status = connect(chatter, (sockaddr*)&hint, sizeof(hint));
if (connection_status == -1) {
cout << "3[31mCONNECTION FAILURE - TERMINATING...3[0m\n";
return -4;
}
cout << "3[32mCONNECTED TO HOST3[0m\n";
char buf[4096] = {0};
string msg;
while (true) {
struct timeval tv;
tv.tv_usec = 0.0;
tv.tv_sec = 5;
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(chatter, &rfds);
getline(cin, msg);
msg += "\r\n";
const char *pMsg = msg.c_str();
size_t msgSize = msg.size();
do {
int numSent = send(chatter, pMsg, msgSize, 0);
if (numSent == -1) {
cout << "3[31mMESSAGE SENDING FAILURE - TERMINATING...3[0m\n";
close(chatter);
return -5;
}
pMsg += numSent;
msgSize -= numSent;
} while (msgSize > 0);
recval = select(chatter + 1, &rfds, NULL, NULL, &tv);
switch(recval) {
case(0):
cout << "3[31mTIMEOUT3[0m\n";
break;
case(-1):
cout << "3[31mERROR3[0m\n";
break;
default:
int numRead = recv(chatter, buf, 4096, 0);
if (numRead < 0) {
cout << "3[31mFAILURE TO RECEIVE MESSAGE - TERMINATING...3[0m\n";
close(chatter);
return -6;
}
else if (numRead == 0) {
cout << "3[31mDISCONNECTED - TERMINATING...3[0m\n";
close(chatter);
break;
} else {
cout << numRead << "\n";
cout.write(buf, numRead);
cout << "\n";
}
break;
}
}
close(chatter);
return 0;
}
在某些平台上,select()
会更改传递的 timeval
以指示剩余时间。所以这很可能是你超时错误的原因,因为你只设置了一次 timeval
并且它最终会下降到 0。你需要在每次调用 [=13= 时重置你的 tv
变量],所以将它移到 while
循环中。
此外,您有 2 次调用 recv()
,而您应该只使用 1 次调用。您忽略了第一个 recv()
接收到的字节,如果服务器恰好发送少于 4096 个字节,那么 next 调用将不会留下任何数据select()
检测,除非连接断开。
改变这个:
if (recv(chatter, buf, 4096, 0) < 0) {
cout << "3[31mFAILURE TO RECEIVE MESSAGE - TERMINATING...3[0m\n";
return -6;
} else {
cout << recv(chatter, buf, 4096, 0) << "\n";
cout << buf << "\n";
}
为此:
int numRead = recv(chatter, buf, 4096, 0);
if (numRead < 0) {
cout << "3[31mFAILURE TO RECEIVE MESSAGE - TERMINATING...3[0m\n";
return -6;
}
else if (numRead == 0) {
cout << "3[32mHOST DISCONNECTED3[0m\n";
break;
} else {
cout << numRead << "\n";
cout.write(buf, numRead);
cout << "\n";
}
此外,msg+"\r\n";
是空操作,您可能打算改用 msg += "\r\n";
。
并且,调用 send()
时不应包含 msg
的空终止符。而且您没有考虑 send()
可能无法 一次性发送全部数据的可能性。您需要在循环中调用 send()
,例如:
const char *pMsg = msg.c_str();
size_t msgSize = msg.size();
do {
int numSent = send(chatter, pMsg, msgSize, 0);
if (numSent == -1) {
cout << "3[31mMESSAGE SENDING FAILURE - TERMINATING...3[0m\n";
return -5;
}
pMsg += numSent;
msgSize -= numSent;
}
while (msgSize > 0);
传奇中的第三个问题:How to correctly implement select to correctly get data from stdin and recv()。我建议阅读这个和它链接的另一个问题以了解情况。
基本上,我自己尝试实现了 select()
。我的代码:
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <stdlib.h>
using namespace std;
int main (int argc, char** argv) {
if (argv[1] == NULL) {
cout << "3[31mTARGET NOT SPECIFIED - TERMINATING...3[0m\n";
return -1;
}
if (argv[2] == NULL) {
cout << "3[31mPORT NOT SPECIFIED - TERMINATING...3[0m\n";
return -2;
}
string target = argv[1];
int port = atoi(argv[2]);
cout << "GENERATING SOCKET...\n";
int chatter = socket(AF_INET, SOCK_STREAM, 0);
if (chatter == -1) {
cout << "3[31mSOCKET GENERATION FAILURE - TERMINATING...3[0m\n";
return -3;
}
cout << "3[32mSUCCESSFULLY GENERATED SOCKET3[0m\n";
struct sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(port);
inet_pton(AF_INET, target.c_str(), &hint.sin_addr);
struct timeval tv;
tv.tv_usec = 0.0;
tv.tv_sec = 5;
int recval;
cout << "CONNECTING TO " << target << " AT PORT " << port << "...\n";
int connection_status = connect(chatter, (sockaddr*)&hint, sizeof(hint));
if (connection_status == -1) {
cout << "3[31mCONNECTION FAILURE - TERMINATING...3[0m\n";
return -4;
}
cout << "3[32mCONNECTED TO HOST3[0m\n";
char buf[4096] = {0};
string msg;
while (true) {
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(chatter, &rfds);
getline(cin, msg);
msg+"\r\n";
int sendmsg = send(chatter, msg.c_str(), msg.size()+1, 0);
if (sendmsg == -1) {
cout << "3[31mMESSAGE SENDING FAILURE - TERMINATING...3[0m\n";
return -5;
}
recval = select(chatter + 1, &rfds, NULL, NULL, &tv);
switch(recval) {
case(0):
cout << "3[31mTIMEOUT3[0m\n";
break;
case(-1):
cout << "3[31mERROR3[0m\n";
break;
default:
if (recv(chatter, buf, 4096, 0) < 0) {
cout << "3[31mFAILURE TO RECEIVE MESSAGE - TERMINATING...3[0m\n";
return -6;
} else {
cout << recv(chatter, buf, 4096, 0) << "\n";
cout << buf << "\n";
}
break;
}
}
close(chatter);
return 0;
}
在 scanme.nmap.org
和我的 HTTP 服务器上尝试该程序时,我一直收到 TIMEOUT
。我做错了什么?
此时,在解决了用户在第一个问题中指出的问题后,我知道我发送数据的方式没有问题。程序处理从 getline()
/recv()
.
编辑:新的、改进的、工作代码感谢回答者
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <stdlib.h>
using namespace std;
int main (int argc, char** argv) {
if (argv[1] == NULL) {
cout << "3[31mTARGET NOT SPECIFIED - TERMINATING...3[0m\n";
return -1;
}
if (argv[2] == NULL) {
cout << "3[31mPORT NOT SPECIFIED - TERMINATING...3[0m\n";
return -2;
}
string target = argv[1];
int port = atoi(argv[2]);
cout << "GENERATING SOCKET...\n";
int chatter = socket(AF_INET, SOCK_STREAM, 0);
if (chatter == -1) {
cout << "3[31mSOCKET GENERATION FAILURE - TERMINATING...3[0m\n";
return -3;
}
cout << "3[32mSUCCESSFULLY GENERATED SOCKET3[0m\n";
struct sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(port);
inet_pton(AF_INET, target.c_str(), &hint.sin_addr);
int recval;
cout << "CONNECTING TO " << target << " AT PORT " << port << "...\n";
int connection_status = connect(chatter, (sockaddr*)&hint, sizeof(hint));
if (connection_status == -1) {
cout << "3[31mCONNECTION FAILURE - TERMINATING...3[0m\n";
return -4;
}
cout << "3[32mCONNECTED TO HOST3[0m\n";
char buf[4096] = {0};
string msg;
while (true) {
struct timeval tv;
tv.tv_usec = 0.0;
tv.tv_sec = 5;
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(chatter, &rfds);
getline(cin, msg);
msg += "\r\n";
const char *pMsg = msg.c_str();
size_t msgSize = msg.size();
do {
int numSent = send(chatter, pMsg, msgSize, 0);
if (numSent == -1) {
cout << "3[31mMESSAGE SENDING FAILURE - TERMINATING...3[0m\n";
close(chatter);
return -5;
}
pMsg += numSent;
msgSize -= numSent;
} while (msgSize > 0);
recval = select(chatter + 1, &rfds, NULL, NULL, &tv);
switch(recval) {
case(0):
cout << "3[31mTIMEOUT3[0m\n";
break;
case(-1):
cout << "3[31mERROR3[0m\n";
break;
default:
int numRead = recv(chatter, buf, 4096, 0);
if (numRead < 0) {
cout << "3[31mFAILURE TO RECEIVE MESSAGE - TERMINATING...3[0m\n";
close(chatter);
return -6;
}
else if (numRead == 0) {
cout << "3[31mDISCONNECTED - TERMINATING...3[0m\n";
close(chatter);
break;
} else {
cout << numRead << "\n";
cout.write(buf, numRead);
cout << "\n";
}
break;
}
}
close(chatter);
return 0;
}
在某些平台上,select()
会更改传递的 timeval
以指示剩余时间。所以这很可能是你超时错误的原因,因为你只设置了一次 timeval
并且它最终会下降到 0。你需要在每次调用 [=13= 时重置你的 tv
变量],所以将它移到 while
循环中。
此外,您有 2 次调用 recv()
,而您应该只使用 1 次调用。您忽略了第一个 recv()
接收到的字节,如果服务器恰好发送少于 4096 个字节,那么 next 调用将不会留下任何数据select()
检测,除非连接断开。
改变这个:
if (recv(chatter, buf, 4096, 0) < 0) {
cout << "3[31mFAILURE TO RECEIVE MESSAGE - TERMINATING...3[0m\n";
return -6;
} else {
cout << recv(chatter, buf, 4096, 0) << "\n";
cout << buf << "\n";
}
为此:
int numRead = recv(chatter, buf, 4096, 0);
if (numRead < 0) {
cout << "3[31mFAILURE TO RECEIVE MESSAGE - TERMINATING...3[0m\n";
return -6;
}
else if (numRead == 0) {
cout << "3[32mHOST DISCONNECTED3[0m\n";
break;
} else {
cout << numRead << "\n";
cout.write(buf, numRead);
cout << "\n";
}
此外,msg+"\r\n";
是空操作,您可能打算改用 msg += "\r\n";
。
并且,调用 send()
时不应包含 msg
的空终止符。而且您没有考虑 send()
可能无法 一次性发送全部数据的可能性。您需要在循环中调用 send()
,例如:
const char *pMsg = msg.c_str();
size_t msgSize = msg.size();
do {
int numSent = send(chatter, pMsg, msgSize, 0);
if (numSent == -1) {
cout << "3[31mMESSAGE SENDING FAILURE - TERMINATING...3[0m\n";
return -5;
}
pMsg += numSent;
msgSize -= numSent;
}
while (msgSize > 0);