现在我开始研究插座。但是我的双手工作不正常。我有很酷的登录方法:
int LogIn(const string &name, const string &password)
{
char Buffer[1024];
string data = "login=" + name + "&password=" + password;
sprintf(Buffer, "POST /Test/login_cl.php HTTP/1.1\r
"\nContent-Length: %d\r"
"\nConnection: Keep-Alive\r"
"\nAccept-Encoding: gzip\r"
"\nAccept-Language: ru-RU,en,*\r"
"\nUser-Agent: Mozilla/5.0\r"
"\nHost: 127.0.0.1\r"
"\nContent-Type: application/x-www-form-urlencoded\r\n\r\n"
"login=%s&"
"password=%s&", data.length(), name.c_str(), password.c_str());
int BytesSent = send(MySocket, Buffer, sizeof(Buffer), 0);
string test;
char ans;
while (recv(MySocket, &ans, 1, 0))
test.push_back(ans);
cout << test << endl;
return 0;
}
问题是我只能调用一次此方法。其他调用或调用注销方法无效,例如:
int result = LogIn(logn, paswd);
int result = LogIn(logn, paswd);
无法正常工作,我只收到一个答案(第二个是空的,而recv正在返回-1)
请帮助,谢谢。
答案 0 :(得分:2)
int BytesSent = send(MySocket, Buffer, sizeof(Buffer), 0);
这会发送太多字节的数据,在请求结束时另一方留下一大堆垃圾,它认为这是下一个请求的一部分。
string data = "login=" + name + "&password=" + password;
这个查询最后没有&
。
"password=%s&", data.length(), name.c_str(), password.c_str());
这会在查询结尾处放置&
。嗯,这是什么?
你需要解决两件事:
对查询末尾的&
保持一致。 (为什么要将查询字符串组合两次?)
使用strlen(Buffer)
代替sizeof(Buffer)
。
但实际上,您应该使用实际协议规范的实现而不是像这样的代码。例如,如果密码中有&
会发生什么?如果服务器决定在回复中使用分块编码会发生什么?你实际上没有实现HTTP 1.1,但你声称你这样做。这迟早会导致很多痛苦。
答案 1 :(得分:1)
您发送的字节太多,您没有正确格式化POST消息的正文,而且您根本没有正确读取响应。
如果您要使用C ++进行某些字符串处理,那么您应该使用C ++进行所有字符串处理,如果可以,请避免混合C字符串处理。
尝试更像这样的东西:
const string SafeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789*-._";
string Urlencode(const string &str)
{
if (str.length() == 0)
return string();
ostringstream buffer;
for (int I = 0; I < ASrc.length(); ++I)
{
char ch = ASrc[I];
if (ch == ' ')
buffer << '+';
else if (SafeChars.find(ch) != string::npos)
buffer << ch;
else
buffer << '%' << setw(2) << fillchar('0') << hex << ch;
}
return buffer.str();
}
int LogIn(const string &name, const string &password)
{
string data = "login=" + Urlencode(name) + "&password=" + Urlencode(password);
ostringstream buffer;
buffer << "POST /Test/login_cl.php HTTP/1.1\r\n"
<< "Content-Length: " << data.length() << "\r\n"
<< "Connection: Keep-Alive\r\n"
<< "Accept-Encoding: gzip\r\n"
<< "Accept-Language: ru-RU,en,*\r\n"
<< "User-Agent: Mozilla/5.0\r\n"
<< "Host: 127.0.0.1\r\n"
<< "Content-Type: application/x-www-form-urlencoded\r\n"
<< "\r\n"
<< data;
string request = buffer.str();
const char *req = request.c_str();
int reqlen = request.length();
do
{
int BytesSent = send(MySocket, request.c_str(), request.length(), 0);
if (BytesSent <= 0)
return -1;
req += BytesSent;
reqlen -= BytesSent;
}
while (reqlen > 0);
// you REALLY need to flesh out this reading logic!
// See RFC 2616 Section 4.4 for details
string response;
char ch;
while (recv(MySocket, &ch, 1, 0) > 0)
response += ch;
cout << response << endl;
return 0;
}
我将把它作为练习让您学习正确的方法来阅读HTTP响应(提示:it is a LOT harder then you think - 特别是因为您要包含Accept-Encoding: gzip
和Connection: Keep-Alive
标题,对响应处理有很大影响。请阅读RFC 2616 Section 4.4以获取有关如何确定响应长度和格式的详细信息。
话虽如此,HTTP并不是一个简单的手工实现协议,所以你真的应该使用预制的HTTP库,比如libcurl,或者使用微软自己的WinInet或WinHTTP API。