2
votes

I am completely new to Winsock and have been trying to write a small HTTP server listening on localhost for educational purposes mainly. Currently the server simply returns a web page to whoever connects to it, without parsing any request.

Logically, I must always be listening for new connections on the listening port (I chose 81 here) after I finish with a client and close the connection, so I've googled quite a bit and found that I should probably be using SO_REUSEADDR for this purpose, but maybe I got it wrong. I am using Firefox as the client.

The first connection always goes without a hitch. However, the second time a client attempts to connect, the accept function doesn't seem to accept the connection. On the other hand, I can see that a connection IS established at that time using a utility that watches local ports (CurrPorts). I've looked for hours for a solution and have tried to make the socket non-blocking, but no luck. What did I do wrong?

#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
The listening socket should be created outside of the while loop.David Schwartz
I have tried that also, and the program seems to go into an infinite loop taking all my memory and CPU and the computer crashes...Æðelstan
So fix the bug that's causing that. But the code above can't possibly work. Also, strlen(response.c_str())*sizeof(char) -- seriously?!David Schwartz
Ok, I'll look into that; I wasn't sure it was a bug or something I did wrong.Æðelstan
Well... it doesn't really matter anyway heheÆðelstan

1 Answers

1
votes

Here's how your code should work:

Main function:

  1. Open listening socket.
  2. Bind it.
  3. Call listen.
  4. Call accept.
  5. Dispatch a thread to handle the socket we just accepted.
  6. Go to step 4.

Note that the thread never touches the listening socket.