簡體   English   中英

C++ Winsock SO_REUSEADDR:accept() 在連接時不返回套接字

[英]C++ Winsock SO_REUSEADDR: accept() not returning socket on connection

我對 Winsock 完全陌生,並且一直在嘗試編寫一個小型 HTTP 服務器,主要用於教育目的在 localhost 上偵聽。 目前,服務器只是向連接到它的任何人返回一個網頁,而不解析任何請求。

從邏輯上講,在完成客戶端並關閉連接后,我必須始終在偵聽端口上偵聽新連接(我在這里選擇了 81),所以我搜索了很多,發現我可能應該為此使用 SO_REUSEADDR目的,但也許我弄錯了。 我使用 Firefox 作為客戶端。

第一個連接總是順利進行。 但是,客戶端第二次嘗試連接時,accept 函數似乎不接受連接。 另一方面,我可以看到當時使用監視本地端口 (CurrPorts) 的實用程序建立了連接。 我已經尋找了幾個小時的解決方案,並試圖使套接字非阻塞,但沒有運氣。 我做錯了什么?

#pragma comment(lib,"Ws2_32.lib")

#include <WinSock2.h>
#include <iostream>
#include <thread>
#include <string>
#include <array>
#include <ctime>
#include <winerror.h>

inline std::string getAddress(sockaddr_in* sin)
{
    std::string res = std::to_string(sin->sin_addr.S_un.S_un_b.s_b1) + '.' + std::to_string(sin->sin_addr.S_un.S_un_b.s_b2) + '.' + std::to_string(sin->sin_addr.S_un.S_un_b.s_b3) + '.' + std::to_string(sin->sin_addr.S_un.S_un_b.s_b4);
    return res;
}

void acceptTCP(SOCKET& origSock)
{
    SOCKET tempSock = SOCKET_ERROR;
    struct sockaddr* sa = new sockaddr();
    int size = sizeof(*sa);
    while (tempSock == SOCKET_ERROR)
    {
        tempSock = accept(origSock, sa, &size);
        int err = WSAGetLastError();
        if (err != 0 && err != WSAEWOULDBLOCK) std::cout << "\r\n" << err;
    }
    struct sockaddr_in* sin = (struct sockaddr_in*)sa;
    std::cout << "\r\nConnected to " << getAddress(sin) << ":" << htons(sin->sin_port);
    origSock = tempSock;
}

int closeSocket(SOCKET socket)
{
    shutdown(socket, 2); //I've tried using 0
    std::clock_t start = std::clock();
    char buf[1];
    while ((std::clock() - start) / (double)CLOCKS_PER_SEC < 5)
    {
        int res = recv(socket, buf, strlen(buf), IPPROTO_TCP);
        //std::cout << "\r\n" << res;
        bool br = false;
        switch (res)
        {
        case 0: br = true; break; //client closed connection
        case -1:
        {
            int err = WSAGetLastError();
        if (err != WSAEWOULDBLOCK && err != WSAEINTR)  //client closed connection
        {
            br = true;
            break;
        }
        else std::cout << "\r\nError on close socket: " << err;
        }
        default: exit(1); //data is being sent after shutdown request
        };
        if (br) break;
        //if (res == -1) std::cout << ": " << WSAGetLastError();
        //else std::cout << ": " << buf;
        //Sleep(1000);
    }
    return closesocket(socket);
}

int main()
{
    WSADATA WsaDat;
    if (WSAStartup(MAKEWORD(1, 1), &WsaDat) != 0) std::cout << "???";
    while (true)
    {
        SOCKET socket0 = socket(AF_INET, SOCK_STREAM, 0);
        if (socket0 == INVALID_SOCKET) std::cout << "Invalid socket!";
        struct sockaddr_in saServer;
        saServer.sin_family = AF_INET;
        saServer.sin_port = htons(81);
        saServer.sin_addr.S_un.S_un_b.s_b1 = 127;
        saServer.sin_addr.S_un.S_un_b.s_b2 = 0;
        saServer.sin_addr.S_un.S_un_b.s_b3 = 0;
        saServer.sin_addr.S_un.S_un_b.s_b4 = 1;
        int enable = 1;
        if (setsockopt(socket0, SOL_SOCKET, SO_REUSEADDR, (const char*)&enable, sizeof(int)) < 0)
            std::cout << "setsockopt(SO_REUSEADDR) failed";
        u_long iMode = 1;
        ioctlsocket(socket0, FIONBIO, &iMode);
        if (bind(socket0, (SOCKADDR*)&saServer, sizeof(saServer)) == SOCKET_ERROR) std::cout << "\r\nSocket Error " << WSAGetLastError();
        else std::cout << "Socket bound!";
        listen(socket0, 1);

        std::thread threadConnection(&acceptTCP, std::ref(socket0)); //I use a thread in case I will want to handle more than one connection at a time in the future, but it serves no purpose here
        threadConnection.join();
        std::string content = "<!DOCTYPE html><html><head><title>test</title></head><body><p>test</p></body></html>";
        std::string response = "HTTP/1.1 200 OK\r\nServer: myServer\r\nContent-Type: text/html\r\nConnection: close\r\nContent-Length: " + std::to_string(content.length()) + "\r\n\r\n" + content;
        std::cout << "\r\n" << send(socket0, response.c_str(), strlen(response.c_str())*sizeof(char), 0);
        Sleep(1000);
        std::cout << "\r\n" << closeSocket(socket0);
    }
    WSACleanup();
}

以下是您的代碼應該如何工作:

主功能:

  1. 打開監聽套接字。
  2. 綁定它。
  3. 打電話聽。
  4. 打電話接受。
  5. 調度一個線程來處理我們剛剛接受的套接字。
  6. 轉到步驟 4。

請注意,線程永遠不會接觸偵聽套接字。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM