如果在线程中执行,简单的 Winsock 服务器将无法工作

问题描述

我想在一个程序中有多个服务器,但是如果我尝试将服务器放在一个函数中并使用线程执行它,它会崩溃(没有编译错误)。抱歉,代码太长,但需要证明我的问题:

#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <Windows.h>
#include <thread>

#pragma comment(lib,"Ws2_32.lib")
#define _WINSOCK_DEPRECATED_NO_WARNINGS

int server()
{
    WSADATA data;
    WSAStartup(MAKEWORD(2,2),&data);

    sockaddr_in listen_address;
    listen_address.sin_family = AF_INET;
    listen_address.sin_port = htons(1000);
    listen_address.sin_addr.S_un.S_addr = INADDR_ANY;


    SOCKET listen_socket = socket(AF_INET,SOCK_STREAM,0);

    bind(listen_socket,(sockaddr*)&listen_address,sizeof(listen_address));
    listen(listen_socket,SOMAXCONN);

    sockaddr client_info = { 0 };
    int socklen;
    SOCKADDR_IN addr;
    int addrlen = sizeof(addr);
    listen_socket = accept(listen_socket,(struct sockaddr*)&client_info,&addrlen);

    if (listen_socket != INVALID_SOCKET)
    {
        std::cout << "New connection" << std::endl;
    }

    int iResult,iSendResult;
    char buffer[1024]{ 0 };

    do {
        iResult = recv(listen_socket,buffer,sizeof(buffer),0);
        if (iResult > 0) {
            std::cout << buffer << std::endl;
        }
        else if (iResult == 0)
            std::cout << "Closing connection" << std::endl;
        else {
            printf("recv Failed: %d\n",WSAGetLastError());
            closesocket(listen_socket);
            WSACleanup();
            return 1;
        }

    } while (iResult > 0);

    //clean up
    system("pause");
    closesocket(listen_socket);
    WSACleanup();
    return 0;
}

int main() {
    std::thread server_thread(server);
    return 0;
}

如果你只运行 server() 函数,它工作正常,但在一个线程中它崩溃了。

解决方法

崩溃是因为当 .Where(predicate).Count() 退出时您的 std::thread 超出范围,但您没有在线程上调用 main()join(),所以 {{ 1}} 析构函数正在调用 detach() 来终止您的进程。

试试这个:

std::thread

现在,每个服务器有 1 个客户端并不是特别有用。要使用 1 个服务器处理多个客户端,您可以在循环中调用 std::terminate(),然后为每个接受的客户端创建一个单独的线程(您可以使用更好的方法,但以下仅用于演示目的),例如:

#include <iostream>
#include <thread>
#include <cstdlib>

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <winsock2.h>
#include <Windows.h>
#include <ws2tcpip.h>

#pragma comment(lib,"Ws2_32.lib")

void server()
{
    int iResult;
    char buffer[1024];

    sockaddr_in listen_address;
    listen_address.sin_family = AF_INET;
    listen_address.sin_port = htons(1000);
    listen_address.sin_addr.S_un.S_addr = INADDR_ANY;

    SOCKET listen_socket = socket(AF_INET,SOCK_STREAM,0);
    if (listen_socket == INVALID_SOCKET) {
        iResult = WSAGetLastError();
        std::cerr << "socket failed: " << iResult << "\n";
        return;
    }

    if (bind(listen_socket,(sockaddr*)&listen_address,sizeof(listen_address)) == SOCKET_ERROR) {
        iResult = WSAGetLastError();
        std::cerr << "bind failed: " << iResult << "\n";
        closesocket(listen_socket);
        return;
    }

    if (listen(listen_socket,1) == SOCKET_ERROR) {
        iResult = WSAGetLastError();
        std::cerr << "listen failed: " << iResult << "\n";
        closesocket(listen_socket);
        return;
    }

    sockaddr client_info = { 0 };
    int socklen;
    int addrlen = sizeof(client_info);

    SOCKET client_socket = accept(listen_socket,(struct sockaddr*)&client_info,&addrlen);
    if (client_socket == INVALID_SOCKET) {
        iResult = WSAGetLastError();
        std::cerr << "accept failed: " << iResult << "\n";
        closesocket(listen_socket);
        return;
    }

    closesocket(listen_socket);

    std::cout << "New connection" << std::endl;

    do {
        iResult = recv(client_socket,buffer,sizeof(buffer),0);
        if (iResult <= 0) {
            if (iResult == 0) {
                std::cout << "Closing connection" << std::endl;
            }
            else {
                iResult = WSAGetLastError();
                std::cerr << "recv failed: " << iResult << "\n";
            }
            break;
        }
        std::cout.write(buffer,iResult);
        std::cout << std::endl;
    }
    while (true);

    //clean up
    closesocket(client_socket);
}

int main() {
    WSADATA data;
    int iResult = WSAStartup(MAKEWORD(2,2),&data);
    if (iResult != 0) {
        std::cerr << "WSAStartup failed: " << iResult << "\n";
        return 0;
    }

    std::thread server_thread(server);

    std::system("pause");
    server_thread.join();

    WSACleanup();
    return 0;
}

或者,所有套接字都处于非阻塞模式,您根本不需要在自己的线程中运行每个客户端,例如:

accept()