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

标签 c++ sockets winsock

我是 Winsock 的新手,一直在尝试编写一个小型 HTTP 服务器,主要用于教育目的,监听本地主机。目前,服务器只是将网页返回给任何连接到它的人,而不解析任何请求。

从逻辑上讲,在我完成客户端并关闭连接后,我必须始终在监听端口(我在这里选择 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. 绑定(bind)它。
  3. 打电话听。
  4. 调用接受。
  5. 分派(dispatch)一个线程来处理我们刚刚接受的套接字。
  6. 转到第 4 步。

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

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

相关文章:

multithreading - 如何最小化 tcp 服务器应用程序中使用的线程数?

c++ - 在 C++ 中处理 Winsock 错误的推荐方法是什么?

c++ - UDP winsock 服务器 c++ 与阻塞

c++ - 如何运行实时服务器win winsock?

C++ 从模板中获取函数名称

c++ - 为数组中的所有元素分配相同值的方法

c++ - C/C++ - 如何通过 UDP 套接字发送 "packets"中的视频文件?

Ruby TCPSocket write 不起作用,但是 puts 可以吗?

c++ - 如何在 C++ 中返回一个整数数组

c++ - Q进程: Reset working directory during runtime