2015-09-25 3 views
1

Я совершенно новичок в Winsock и стараюсь писать небольшой HTTP-сервер, который в основном слушает локальный хост для образовательных целей. В настоящее время сервер просто возвращает веб-страницу тому, кто подключается к нему, без разбора какого-либо запроса.C++ Winsock SO_REUSEADDR: accept() не возвращает сокет при подключении

Логично, что я всегда должен слушать новые подключения к порту прослушивания (я выбрал 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(); 
} 
+0

Слуховой сокет должен быть создан за пределами цикла while. –

+0

Я тоже пробовал это, и программа, похоже, входит в бесконечный цикл, забирающий всю память и процессор, и компьютер падает ... –

+0

Так исправить ошибку, которая вызывает это. Но вышеприведенный код не может работать. Кроме того, 'strlen (response.c_str()) * sizeof (char)' - серьезно ?! –

ответ

1

Вот как ваш код должен работать:

Основная функция:

  1. Открыть сокет прослушивания.
  2. Связать.
  3. Звоните слушать.
  4. Call accept.
  5. Отправляйте поток для обработки разъема, который мы только что приняли.
  6. Перейти к шагу 4.

Обратите внимание, что поток никогда не прикасается сокет.

+0

Я попытался поместить вызов bind, listen() и установить параметры сокета за пределами цикла while, а также не пытаться закрыть сокет каким-либо образом, как вы предлагаете здесь, но я все равно получаю WSAEINVAL во втором accept() петля. Кроме того, как я не прикасаюсь к гнезду прослушивания с новым потоком? Если я правильно понял, есть ли 2 гнезда, когда соединение установлено? –

+0

Правильно, есть два разъема. Один из них - это прослушивающий сокет, который 'main' использует для приема новых соединений. Другой - подключенный сокет, используемый потоком для общения с клиентом. Вы избавились от 'origSock = tempSock;'?Это явно чепуха. –

+0

Сделайте секцию для прослушивания закрытой для 'main', чтобы не было шансов на ее работу. Пропустите подключенный сокет к потоку и дайте потоку закрыть его, когда он будет сделан с ним. –

Смежные вопросы