c++ - 绑定(bind)服务器套接字时出现 WSA 错误 10048

标签 c++ multithreading sockets winsock2 windows-socket-api

我从简单的服务器 tutorial 开始在 msdn 网站上,了解如何在客户端和服务器应用程序中使用套接字。

完成教程后,我开始将客户端和服务器代码改编为多线程程序,以便制作 tchat 客户端和服务器。一切都很顺利,直到我遇到了 WSA 错误 10048。我尝试为每个套接字使用不同的端口,但仍然没有解决该错误。

这是我的服务器代码:

#undef UNICODE

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <thread>
#include <vector>

// Need to link with Ws2_32.lib
#pragma comment (lib, "Ws2_32.lib")
// #pragma comment (lib, "Mswsock.lib")

//Global values
//I put them as global values in order to get the server up and running.
//I will try to pass them as params later on
int iResult;
struct addrinfo *result = NULL;
struct addrinfo hints;
int numClients = 0;
SOCKET ClientSocket[5];
std::thread** sendReceiveThread = new std::thread*[5];

//Prototypes
int listen(SOCKET ListenSocket);
int accept(SOCKET ListenSocket);
int sendReceive();
int shutdownFunction(SOCKET ClientSocket);

#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT1 "1016"
#define DEFAULT_PORT2 "1017"
#define DEFAULT_PORT3 "1018"
#define DEFAULT_PORT4 "1019"
#define DEFAULT_PORT5 "1020"

int main()
{
    std::cout << 1 << std::endl;
    WSADATA wsaData;

    SOCKET ListenSocket = INVALID_SOCKET;


    // Initialize Winsock
    std::cout << 2 << std::endl;
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        std::cout << 3 << std::endl;
        printf("WSAStartup failed with error: %d\n", iResult);
        return 1;
    }

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;

    std::thread ListenThread{ [ListenSocket](){listen(ListenSocket); } };
    ListenThread.join();

    return 0;
}

int listen(SOCKET ListenSocket)
{
    int numPort = 1;
    std::vector<std::thread*> thread_vec;
    while (true)
    {
        if (numPort == 1)
        {
            // Resolve the server address and port
            std::cout << 4 << std::endl;
            iResult = getaddrinfo(NULL, DEFAULT_PORT1, &hints, &result);
            numPort++;
            if (iResult != 0) {
                std::cout << 5 << std::endl;
                printf("getaddrinfo failed with error: %d\n", iResult);
                WSACleanup();
                break;
            }
        }

        else if (numPort == 2)
        {
            // Resolve the server address and port
            std::cout << 4 << std::endl;
            iResult = getaddrinfo(NULL, DEFAULT_PORT2, &hints, &result);
            numPort++;
            if (iResult != 0) {
                std::cout << 5 << std::endl;
                printf("getaddrinfo failed with error: %d\n", iResult);
                WSACleanup();
                break;
            }
        }

        else if (numPort == 3)
        {
            // Resolve the server address and port
            std::cout << 4 << std::endl;
            iResult = getaddrinfo(NULL, DEFAULT_PORT3, &hints, &result);
            numPort++;
            if (iResult != 0) {
                std::cout << 5 << std::endl;
                printf("getaddrinfo failed with error: %d\n", iResult);
                WSACleanup();
                break;
            }
        }

        else if (numPort == 4)
        {
            // Resolve the server address and port
            std::cout << 4 << std::endl;
            iResult = getaddrinfo(NULL, DEFAULT_PORT4, &hints, &result);
            numPort++;
            if (iResult != 0) {
                std::cout << 5 << std::endl;
                printf("getaddrinfo failed with error: %d\n", iResult);
                WSACleanup();
                break;
            }
        }

        else if (numPort == 5)
        {
            // Resolve the server address and port
            std::cout << 4 << std::endl;
            iResult = getaddrinfo(NULL, DEFAULT_PORT5, &hints, &result);
            numPort++;
            if (iResult != 0) {
                std::cout << 5 << std::endl;
                printf("getaddrinfo failed with error: %d\n", iResult);
                WSACleanup();
                break;
            }
        }

        // Create a SOCKET for connecting to server
        std::cout << 6 << std::endl;
        ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
        if (ListenSocket == INVALID_SOCKET) {
            std::cout << 7 << std::endl;
            printf("socket failed with error: %ld\n", WSAGetLastError());
            freeaddrinfo(result);
            WSACleanup();
            break;
        }

        // Setup the TCP listening socket
        std::cout << 8 << std::endl;
        iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
        if (iResult == SOCKET_ERROR) {
            std::cout << 9 << std::endl;
            printf("bind failed with error: %d\n", WSAGetLastError());
            freeaddrinfo(result);
            closesocket(ListenSocket);
            WSACleanup();
            break;
        }

        freeaddrinfo(result);

        std::cout << 10 << std::endl;
        iResult = listen(ListenSocket, SOMAXCONN);
        if (iResult == SOCKET_ERROR) {
            std::cout << 11 << std::endl;
            printf("listen failed with error: %d\n", WSAGetLastError());
            closesocket(ListenSocket);
            WSACleanup();
            break;
        }


        static std::thread AcceptThread{ [ListenSocket](){accept(ListenSocket); } };
        thread_vec.push_back(&AcceptThread);
    }
    for (auto it : thread_vec) it->join();
    return 0;
}

int accept(SOCKET ListenSocket)
{
    numClients++;
    const int currentNumClients = numClients;
    for (int i = 0; i <= 5; i++)
    {
        ClientSocket[i] = INVALID_SOCKET;
    }

    // Accept a client socket
    std::cout << 12 << std::endl;

    std::cout << 13 << std::endl;

    ClientSocket[currentNumClients] = accept(ListenSocket, NULL, NULL);
    if (ClientSocket[currentNumClients] == INVALID_SOCKET)
    {
        printf("accept failed with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    sendReceiveThread[currentNumClients] = new std::thread([](){sendReceive(); });
    (*sendReceiveThread[currentNumClients]).join();
    delete sendReceiveThread[currentNumClients];

    return 0;
}

int sendReceive()
{
    int currentNumClients = numClients;
    int iSendResult;
    char recvbuf[DEFAULT_BUFLEN];
    int recvbuflen = DEFAULT_BUFLEN;

    // Receive until the peer shuts down the connection
    while(true)
    {
        std::cout << 14 << std::endl;
        iResult = recv(ClientSocket[currentNumClients], recvbuf, recvbuflen, 0);
        std::cout << iResult << std::endl;
        if (iResult > 0) {
            std::cout << 15 << std::endl;
            printf("Bytes received: %d\n", iResult);

            // Echo the buffer back to the clients
            std::cout << 16 << std::endl;
            for (int i = 1; i <= numClients; i++)
            {
                iSendResult = send(ClientSocket[currentNumClients], recvbuf, iResult, 0);
                if (iSendResult == SOCKET_ERROR) {
                    std::cout << 17 << std::endl;
                    printf("send failed with error: %d\n", WSAGetLastError());
                    closesocket(ClientSocket[currentNumClients]);
                    WSACleanup();
                    return 1;
                }
                printf("Bytes sent: %d\n", iSendResult);
            }
        }
        else if (iResult == 0) {
            std::cout << 18 << std::endl;
            printf("Connection closing...\n");
            break;
        }
        else {
            std::cout << 19 << std::endl;
            printf("recv failed with error: %d\n", WSAGetLastError());
            std::cout << "On client #" << currentNumClients << std::endl;
            break;
        }

    }

    iResult = shutdownFunction(ClientSocket[currentNumClients]);

    std::cout << 22 << std::endl;
    // cleanup
    closesocket(ClientSocket[currentNumClients]);
    WSACleanup();

    return 0;
}

int shutdownFunction(SOCKET ClientSocket)
{
    std::cout << 20 << std::endl;
    iResult = shutdown(ClientSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) {
        std::cout << 21 << std::endl;
        printf("shutdown failed with error: %d\n", WSAGetLastError());
        closesocket(ClientSocket);
        WSACleanup();
        return 1;
    }

    return 0;
}

您可能会注意到不同的计算,这些只是为了了解程序的行为方式。

最佳答案

WSA 10048 是“地址正在使用”错误 ( https://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx )。

我没有 Windows,因此无法运行您的代码,但当服务器将服务器端口保留几分钟然后才能重新使用时,通常会发生此错误。

提供了一个套接字选项来允许更快的重用,setsockopt (SO_REUSEADDR)。

在您的情况下,您可以在创建并检查 ListenSocket 后立即添加以下行:

int optval = 1;
iResult = ::setsockopt(ListenSocket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval);

if (iResult == SOCKET_ERROR) {
        std::cout << 9 << std::endl;
        printf("setsockopt failed with error: %d\n", WSAGetLastError());
}

TCP 堆栈有充分的理由在释放旧端口号后不立即重新发布旧端口号,但对于服务器端口来说,这是不受欢迎的行为。只是,堆栈不知道服务器端口和非服务器端口之间的区别,因此我们必须使用setsockopt。

关于c++ - 绑定(bind)服务器套接字时出现 WSA 错误 10048,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55785119/

相关文章:

c# - C#UDP监听器线程—是否不使用while(true)刻录CPU?

c++ - 检查 2 个变量的动态类型的继承 - 但它们在范围内未声明

c++ - boost 重命名()功能不起作用

.net - 在多线程环境中访问.NET词典

sockets - Lua连接redis服务器困难

Java 加密聊天客户端无法通过关联

c++ - 模板类的类型定义?

c++ - 不正确的值被打印到文件中。串口通信程序

python - 线程可以在 Python 中创建子线程吗?

android - 如果里面没有消息,MessageQueue 怎么知道要阻塞多久呢?