C ++ Winsock SO_REUSEADDR:accept()没有在连接

时间:2015-09-25 19:02:44

标签: 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 个答案:

答案 0 :(得分:1)

以下是您的代码应如何运作:

主要功能:

  1. 打开侦听套接字。
  2. 绑定它。
  3. 致电听。
  4. 致电接受。
  5. 发送一个线程来处理我们刚刚接受的套接字。
  6. 转到第4步。
  7. 请注意,线程永远不会触及侦听套接字。