POST Winsock麻烦

时间:2014-01-27 01:46:53

标签: c++ winsock2

现在我开始研究插座。但是我的双手工作不正常。我有很酷的登录方法:

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)

请帮助,谢谢。

2 个答案:

答案 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());

这会在查询结尾处放置&。嗯,这是什么?

你需要解决两件事:

  1. 对查询末尾的&保持一致。 (为什么要将查询字符串组合两次?)

  2. 使用strlen(Buffer)代替sizeof(Buffer)

  3. 但实际上,您应该使用实际协议规范的实现而不是像这样的代码。例如,如果密码中有&会发生什么?如果服务器决定在回复中使用分块编码会发生什么?你实际上没有实现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: gzipConnection: Keep-Alive标题,对响应处理有很大影响。请阅读RFC 2616 Section 4.4以获取有关如何确定响应长度和格式的详细信息。

话虽如此,HTTP并不是一个简单的手工实现协议,所以你真的应该使用预制的HTTP库,比如libcurl,或者使用微软自己的WinInet或WinHTTP API。