使用相同的套接字进行第二次发送和recv没有结果?

时间:2013-07-30 17:58:13

标签: c++ sockets http send recv

当我想第二次使用连接到主机的相同套接字send()和recv()时,recv()将返回0而缓冲区中没有任何内容

基本上我在做:

  1. 连接到网站
  2. 发送数据包
  3. 接收数据包
  4. 再次发送数据包(我认为这个工作正常,因为它没有给我SOCKET_ERROR)
  5. 再次接收数据包(这一个返回0以便“连接关闭”)
  6. 源代码:http://pastebin.com/sm5k5GAe

    正如你所看到的,我有sock2,当我用它来进行第二次发送/接收它工作正常时,但是我会做更多的沟通,并且为所有这些进行套接字连接它们会有点愚蠢我想

1 个答案:

答案 0 :(得分:1)

导致recv()返回0的唯一两个条件是:

  1. 如果你提供了一个0长度的缓冲区。

  2. 如果对方已正常关闭其结尾处的连接。您可以使用数据包嗅探器(如Wireshark)来验证这一点。

  3. 您正在发送包含Connection: close标头的HTTP 1.1请求。这告诉HTTP服务器在发送响应后关闭其连接的结束。这是完全正常的行为。只需阅读响应,关闭连接结束,然后重新连接,然后再发送下一个请求。

    如果要保持连接打开以便通过单个连接发送多个请求,请改为发送Connection: keep-alive标头,或者只是省略Connection标头,因为您发送的是HTTP 1.1请求并且keep-alive是HTTP 1.1的默认行为。

    无论哪种方式,您都必须查看服务器的实际响应Connection标头,以了解它是否将关闭其连接的结束。对于HTTP 0.9或1.0响应,如果没有Connection: keep-alive标头,则必须假定连接将被关闭。对于HTTP 1.1响应,如果没有Connection: close标头,则必须假设连接处于打开状态。但是你必须准备好处理连接可能仍然关闭的可能性,例如通过中间路由器/防火墙,所以如果下一个请求因连接错误而失败,则只需重新连接。

    话虽如此,你最好不要手动实现HTTP,而是使用预先制作的库来处理这些细节,例如libcurl

    <强>更新

    尝试更像这样的东西:

    #include <winsock2.h>
    #include <windows.h>
    #include <iostream>
    #include <string>
    #include <vector>
    #include <sstream>
    #include <fstream>
    
    using namespace std;
    
    SOCKET sock;
    string currentHost;
    
    bool checkConnection(string Hostname);
    int readFromSock(char *data, int datalen, bool disconnectOK = false);
    int readData(string &workBuffer, char *data, int datalen, bool disconnectOK = false);
    bool readLine(string &workBuffer, string &line);
    bool doRequest(string Hostname, string Request, string &Reply);
    
    bool GetReply(string Host, string &Reply);
    bool GetLogin(string Reply, string MyStr, string &Login);
    bool GetSession(string Reply, string MyStr, string &Session);
    bool GetLoginReply(string Host, string Username, string Password, string Login, string Session, string &Reply);
    
    bool Login(string Host, string Resource, string Username, string Password);
    bool IsConnected(string Reply, string MyStr);
    
    #pragma comment (lib, "ws2_32.lib")
    #pragma warning(disable:4996)
    
    int main()
    {
        string sLoginSite, sUsername, sPassword;
        char buffer[32];
    
        //Initialize Winsock
        WSADATA WsaData;
        if(WSAStartup(MAKEWORD(2,2), &WsaData) != 0)
        {
            cout << "WinSock Startup Failed" << endl;
            system("pause");
            return 1;
        }
    
        sock = INVALID_SOCKET;
    
        //Get Login Site
        cout << "Travian login site e.g. ts1.travian.com: ";
        cin >> buffer;
        sLoginSite = buffer;
        cout << endl;
    
        //Get Username
        cout << "Username: ";
        cin >> buffer;
        sUsername = buffer;
    
        //Get Password
        cout << "Password: ";
        cin >> buffer;
        sPassword = buffer;
        cout << endl;
    
        // Perform Login
        if (!Login(sLoginSite, sUsername, sPassword))
        {
            cout << "Error while Logging in" << endl;
            system("pause");
            return 1;
        }
    
        cout << "Successfully connected to the account \"" << sUsername << "\" at " << sLoginSite << endl;
        system("pause");
    
        if (sock != INVALID_SOCKET)
            closesocket(sock);
    
        WSACleanup();
    
        return 0;
    }
    
    // ensure connection to Hostname
    bool checkConnection(string Hostname)
    {
        // Switching to a different hostname?  If so, disconnect...
        if (currentHost != Hostname)
        {
            if (sock != INVALID_SOCKET)
            {
                closesocket(sock);
                sock = INVALID_SOCKET;
            }
    
            currentHost = Host;
        }
    
        // TODO: make sure the socket is actually still connected. If not, disconnect...
        if (sock != INVALID_SOCKET)
        {
            /*
            if (no longer connected)
            {
                closesocket(sock);
                sock = INVALID_SOCKET;
            }
            */
        }
    
        // Create a new connection?
        if (sock == INVALID_SOCKET)
        {
            // resolve the Hostname...
    
            struct hostent *host = gethostbyname(Hostname.c_str());
            if(!host)
            {
                cout << "Can't Resolve Hostname" << endl;
                return false;
            }
    
            // Connect to the Hostname...
    
            SOCKET newSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
            if (newSock == INVALID_SOCKET)
            {
                cout << "Error creating Socket" << endl;
                return false;
            }
    
            SOCKADDR_IN SockAddr;
            ZeroMemory(&SockAddr, sizeof(SockAddr)));
            SockAddr.sin_port = htons(80);
            SockAddr.sin_family = AF_INET;
            SockAddr.sin_addr.s_addr = *((unsigned long*)host->h_addr);
    
            cout << "Connecting" << endl;
            if(connect(newSock, (SOCKADDR*) &SockAddr, sizeof(SockAddr)) != 0)
            {
                closesocket(newSock);
                cout << "Can't Connect to Hostname" << endl;
                return false;
            }
    
            sock = newSock;
            cout << "Successfully connected to " << Hostname << "!" << endl;
        }
    
        // Ready
        return true;
    }
    
    // read raw data from the socket directly
    // returns how many bytes were actually read, -1 on error, or 0 on disconnect
    int readFromSock(char *data, int datalen, bool disconnectOK)
    {
        int read = 0;
    
        while (datalen > 0)
        {
            // more data is expected...
    
            int ret = recv(sock, data, datalen, 0);
            if (ret == SOCKET_ERROR)
            {
                cout << "recv failed: " << WSAGetLastError() << endl;
                closesocket(sock);
                sock = INVALID_SOCKET;
                return -1;
            }
    
            if (ret == 0)
            {
                cout << "server disconnected" << endl;
                closesocket(sock);
                sock = INVALID_SOCKET;
    
                // if the caller is OK with a disconnect occurring, exit without error...
                if (disconnectOK)
                    break;
    
                return -1;
            }
    
            // move forward in the output buffer
            data += ret;
            datalen -= ret;
    
            // increment the result value
            read += ret;
        }
    
        // done
        return read;
    }
    
    // read raw data from an in-memory buffer, reading from the socket directly only when the buffer is empty.
    // returns how many bytes were actually read, -1 on error, or 0 on disconnect
    int readData(string &workBuffer, char *data, int datalen, bool disconnectOK)
    {
        int read = 0;
    
        int len;
        char buffer[512];
    
        while (datalen > 0)
        {
            // more data is expected...
    
            len = workBuffer.length();
            if (len > 0)
            {
                // the work buffer has cached data, move to the output buffer...
    
                if (len > datalen) len = datalen;
                workBuffer.copy(data, len);
                workBuffer.erase(len);
    
                // move forward in the output buffer
                data += len;
                datalen -= len;
    
                // increment the return value
                read += len;
            }
            else
            {
                // the work buffer is empty, read from the socket and cache it...
    
                len = readFromSock(buffer, sizeof(buffer), disconnectOK);
                if (ret == -1)
                    return -1;
    
                // disconnected?
                if (ret == 0)
                    break;
    
                // append new data to the work buffer...
                workBuffer += string(buffer, ret);
            }
        }
    
        // done
        return read;
    }
    
    // reads a LF-delimited line of text from an in-memory buffer, reading from the socket directly only when the buffer is empty.
    // returns whether a full line was actually read.
    bool readLine(string &workBuffer, string &line)
    {
        // clear the output...
        line = "";
    
        int found, len, start = 0;
        char buffer[512];
    
        do
        {
            // check if a LF is already cached. Ignore previously searched text..
            found = workBuffer.find("\n", start);
            if (found != string::npos)
            {
                len = found;
    
                // is the LF preceded by a CR? If so, do include it in the output...
                if (len > 0)
                {
                    if (workBuffer[len-1] == '\r')
                        --len;
                }
    
                // output the line, up to but not including the CR/LF...
                line = workBuffer.substr(0, len);
                workBuffer.erase(found);
    
                break;
            }
    
            // the work buffer does not contain a LF, read from the socket and cache it...
    
            len = readFromSock(buffer, sizeof(buffer));
            if (len <= 0)
            {
                closesocket(sock);
                sock = INVALID_SOCKET;
                return false;
            }
    
            // append new data to the work buffer and search again...
            start = workBuffer.length();
            workBuffer += string(buffer, len);
        }
        while (true);
    
        // done
        return true;
    }
    
    // perform an HTTP request and read the reply
    // returns whether the reply was actually read
    bool doRequest(string Hostname, string Request, string &Reply)
    {
        // clear the output
        Reply = "";
    
        char buffer[512];
        string str, workBuffer;
        string sContentLength, sTransferEncoding, sConnection;
        bool doClose;
    
        // make sure there is a connection, reconnecting if needed...
        if (!checkConnection(Hostname))
            return false;
    
        // send the request...
    
        char *data = Request.c_str();
        int len = Request.length();
        int ret;
    
        do
        {
            ret = send(sock, data, len, 0);
            if (ret == SOCKET_ERROR)
            {
                cout << "Send Error" << endl;
                closesocket(sock);
                sock = INVALID_SOCKET;
                return false;
            }
    
            // move forward in the input buffer...
            data += ret;
            len -= ret;
        }
        while (len > 0);
    
        // read the response's status line...
        if (!readLine(workBuffer, str))
            return false;
    
        // TODO: parse out the line's values, ie: "200 OK HTTP/1.1"
        int ResponseNum = ...;
        int VersionMajor = ...;
        int VersionMinor = ...;
    
        // only HTTP 1.0 responses have headers...
        if (VersionMajor >= 1)
        {    
            // read the headers until a blank line is reached...
    
            do
            {
                // read a header
                if (!readLine(workBuffer, str))
                    return false;
    
                // headers finished?
                if (str.length() == 0)
                    break;
    
                // TODO: do case-insensitive comparisons
                if (str.compare(0, 15, "Content-Length:") == 0)
                    sContentLength = str.substr(15);
                else if (str.compare(0, 18, "Transfer-Encoding:") == 0)
                    sTransferEncoding = str.substr(18);
                else if (str.compare(0, 18, "Connection:") == 0)
                    sConnection = str.substr(11);
            }
            while (true);
    
            // If HTTP 1.0, the connection must closed if the "Connection" header is not "keep-alive"
            // If HTTP 1.1+, the connection must be left open the "Connection" header is not "close"
    
            // TODO: do case-insensitive comparisons
            if ((VersionMajor == 1) && (VersionMinor == 0))
                doClose = (sConnection.compare"keep-alive") != 0);
            else
                doClose = (sConnection.compare("close") == 0);
        }
        else
        {
            // HTTP 0.9 or earlier, no support for headers or keep-alives
            doClose = true;
        }
    
        // TODO: do case-insensitive comparisons
        if (sTransferEncoding.compare(sTransferEncoding.length()-7, 7, "chunked") == 0)
        {
            // the response is chunked, read the chunks until a blank chunk is reached...
    
            do
            {
                // read the chunk header...
                if (!readLine(workBuffer, str))
                    return false;
    
                // ignore any extensions for now...
                int found = str.find(";");
                if (found != string::npos)
                    str.resize(found);
    
                // convert the chunk size from hex to decimal...
                size = strtol(str.c_str(), NULL, 16);
    
                // chunks finished?
                if (size == 0)
                    break;
    
                // read the chunk's data...
                do
                {
                    len = size;
                    if (len > sizeof(buffer)) len = sizeof(buffer);
    
                    if (!readData(workBuffer, buffer, len))
                        return false;
    
                    // copy the data to the output
                    Reply += string(buffer, len);
                    size -= len;
                }
                while (size > 0);
    
                // the data is followed by a CRLF, skip it...
                if (!readLine(workBuffer, str))
                    return false;
            }
            while (true);
    
            // read trailing headers...
    
            do
            {
                // read a header...
                if (!readLine(workBuffer, str))
                    return false;
    
                // headers finished?
                if (str.length() == 0)
                    break;
    
                // process header as needed, overwriting HTTP header if needed...
            }
            while (true);
        }
        else if (sContentLength.length() != 0)
        {
            // the response has a length, read only as many bytes as are specified...
    
            // convert the length to decimal...
            len = strtol(sContentLength.c_str(), NULL, 10);
    
            if (len > 0)
            {
                // read the data...
    
                do
                {
                    ret = len;
                    if (ret > sizeof(buffer)) ret = sizeof(buffer);
    
                    ret = readData(workBuffer, buffer, ret);
                    if (ret <= 0)
                        return false;
    
                    // copy the data to the output
                    Reply += string(buffer, ret);
                    len -= ret;
                }
                while (len > 0);
            }
        }
        else
        {
            // response is terminated by a disconnect...
    
            do
            {
                len = readData(workBuffer, buffer, sizeof(buffer), true);
                if (len == -1)
                    return false;
    
                // disconnected?
                if (len == 0)
                    break;
    
                // copy the data to the output
                Reply += string(buffer, len);
            }
            while (true);
    
            doClose = true;
        }
    
        // close the socket now?
        if (doClose)
        {
            closesocket(sock);
            sock = INVALID_SOCKET;
        }
    
        // TODO: handle other responses, like 3xx redirects...
    
        return ((ResponseNum / 100) == 2);
    }
    
    // Login to Hostname with Username and Password
    // returns whether login was successful or not
    bool Login(string Hostname, string Username, string Password)
    {
        string sLoginForm, sReply, sSessionID, sLoginID, sLoginReply;
    
        //Get Login Form HTML
        if (!GetReply(Hostname, sReply))
        {
            cout << "Reply Error" << endl;
            return false;
        }
    
        //Save Reply
        ofstream Data;
        Data.open("Reply.txt");
        Data << sReply;
        Data.close();
    
        //Get Session ID from HTML
        if (!GetSession(sReply, "sess_id", sSessionID))
        {
            cout << "Session ID Error" << endl;
            return false;
        }
    
        //Get Login ID from HTML
        if (!GetLogin(sReply, "<input type=\"hidden\" name=\"login\" value=", sLoginID))
        {
            cout << "Login ID Error" << endl;
            return false;
        }
    
        // perform final Login
        if (!GetLoginReply(Hostname, Username, Password, sLoginID, sSessionID, sLoginReply))
        {
            cout << "Login Reply Error" << endl;
            return false;
        }
    
        /*
        if(!IsConnected(sLoginReply, "HTTP/1.1 303 See Other"))
        {
            cout << "Invalid Username or Password" << endl;
            return false;
        }
        */
    
        //Save Reply
        Data.open("login.txt");
        Data << sLoginReply;
        Data.close();
    
        // done
        return true;
    }
    
    bool GetReply(string Hostname, string &Reply)
    {
        string str;
    
        str = "GET / HTTP/1.1\r\n";
        str += "Host: " + Hostname + "\r\n"
        str += "Connection: keep-alive\r\n"
        str += "\r\n";
    
        return doRequest(Hostname, str, Reply);
    }
    
    bool GetSession(string Reply, string MyStr, string &Session)
    {
        int found = Reply.find(MyStr);
        if(found == string::npos)
            return false;
    
        Session = Reply.substr(found+MyStr.Length(), 32);
        return true;
    }
    
    bool GetLogin(string Reply, string MyStr, string &Login)
    {
        int found = Reply.find(MyStr);
        if(found == string::npos)
            return false;
    
        Login = Reply.substr(found+MyStr.length(), 10)
        return true;
    }
    
    bool GetLoginReply(string Hostname, string Username, string Password, string Login, string Session, string &Reply)
    {
        string str, special;
    
        special = "name=" + Username + "&password=" + Password + "&s1=Login&w=1600%3A900&login=" + Login;
    
        string temp;
        stringstream ss;
        ss << special.length();
        ss >> temp;
    
        str = "POST /dorf1.php HTTP/1.1\r\n";
        str += "Host: " + Hostname + "\r\n";
        str += "Cookie: sess_id=" + Session + "; highlightsToggle=false; WMBlueprints=%5B%5D\r\n";
        str += "Content-Type: application/x-www-form-urlencoded\r\n";
        str += "Content-Length: " + temp + "\r\n"
        str += "\r\n";
        str += special;
    
        return doRequest(Hostname, str, Reply);
    }
    
    bool IsConnected(string Reply, string MyStr)
    {
        return (Reply.find(MyStr) != string::npos);
    }