我对Winsock完全陌生,并且一直试图编写一个小型HTTP服务器,主要出于教育目的而在localhost上监听。当前,服务器仅将网页返回给与其连接的任何人,而不会解析任何请求。

从逻辑上讲,在我完成与客户端的连接并关闭连接之后,我必须始终在监听端口上监听新的连接(我在此处选择81),因此我在Google上进行了很多搜索,发现我可能应该为此使用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();
}

最佳答案

这是您的代码应如何工作:

主功能:

  • 打开监听套接字。
  • 绑定(bind)它。
  • 调用监听。
  • 调用接受。
  • 分派(dispatch)一个线程来处理我们刚刚接受的套接字。
  • 转到步骤4。

  • 请注意,该线程永远不会接触监听套接字。

    关于c++ - C++ Winsock SO_REUSEADDR : accept() not returning socket on connection,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/32788986/

    10-10 23:16