【问题标题】:Winsock error 10038 starting from third connectionWinsock 错误 10038 从第三个连接开始
【发布时间】:2014-06-09 08:44:02
【问题描述】:

我需要从 TCP 服务器发送简单数据到环回连接的多个客户端。 前两个客户端的发送命令成功,但当我尝试连接第三个客户端时它失败并出现错误 10038。

代码很简单,在第一个连接上创建一个线程,然后对于其他连接,打开的套接字被添加到一个全局数组中。 该线程将相同的数据(字符串“hello”)发送到所有连接的客户端(通过 localhost)。

我犯了一个小错误吗?有什么我不知道的吗?为什么它只适用于两个连接?

编辑:

我想强调一下,我发布的代码只是一个示例,它并不是写成一个真正的服务器,它只是想显示一个我不明白的奇怪症状,我的意思是,为什么当第三个客户端连接到服务器发送失败,错误 10038 (WSAENOTSOCK)?

我的测试(在同一台机器上)发生的情况是:

  1. 我运行服务器
  2. 我在端口 5000 上打开一个 telnet 连接,它 显示带有“Hello”的无限循环,这是我所期望的。
  3. 我在端口 5000 上打开第二个 telnet 连接,它显示无限 用“Hello”循环,这正是我所期望的。
  4. 我打开第三个 在端口 5000 上 telnet 连接,连接失败 立即,服务器在第三个套接字上显示“错误 10038”。

为什么第三次连接失败?我究竟做错了什么?

我的代码:

#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/timeb.h>

int nclients = 0;
SOCKET  lClient[10];
CRITICAL_SECTION m_cs;

DWORD WINAPI ClientThread(LPVOID lpParam)
{
    int          i;

    char buffer[20] = "Hello\n";

    while(1) {
        EnterCriticalSection(&m_cs);
        for (i=0;i<nclients;i++) {
            if (lClient[i] != INVALID_SOCKET) {
                if( send(lClient[i], buffer , strlen(buffer) , 0) < 0) {
                    printf("socket() failed: %d\n", WSAGetLastError());
                    nclients--;
                }
            }
        }
        LeaveCriticalSection(&m_cs);
        Sleep(200); 
    }
    return 0;
}


int main(int argc, char **argv) {
    WSADATA             wsd;
    SOCKET              sListen, sClient;
    int                 iAddrSize;
    HANDLE              hThread;
    DWORD               dwThreadId;
    struct sockaddr_in  local, client;
    int i,iPort;

    InitializeCriticalSection(&m_cs);
    for (i=0;i<10;i++) lClient[i] = INVALID_SOCKET;

    iPort = 5000;

    if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)
    {
        printf("Failed to load Winsock!\n");
        return 1;
    }
    // Create our listening socket
    //
    sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    if (sListen == SOCKET_ERROR)
    {
        printf("socket() failed: %d\n", WSAGetLastError());
        return 1;
    }
    // Select the local interface and bind to it
    //
    local.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    local.sin_family = AF_INET;
    local.sin_port = htons(iPort);

    if (bind(sListen, (struct sockaddr *)&local,
        sizeof(local)) == SOCKET_ERROR)
    {
        printf("bind() failed: %d\n", WSAGetLastError());
        return 1;
    }
    listen(sListen, 8);
    //
    // In a continous loop, wait for incoming clients. Once one
    // is detected, create a thread and pass the handle off to it.
    //
    while (1)
    {
        iAddrSize = sizeof(client);
        sClient = accept(sListen, (struct sockaddr *)&client,&iAddrSize);
        if (sClient == INVALID_SOCKET)
        {
            printf("accept() failed: %d\n", WSAGetLastError());
            break;
        }
        printf("Accepted client: %s:%d\n",inet_ntoa(client.sin_addr), ntohs(client.sin_port));
        if (nclients == 0) {
            hThread = CreateThread(NULL, 0, ClientThread, 0, 0, &dwThreadId);
            EnterCriticalSection(&m_cs);
            lClient[nclients] = sClient;
            nclients++;
            LeaveCriticalSection(&m_cs);
        }
        else {
            EnterCriticalSection(&m_cs);
            lClient[nclients] = sClient;
            nclients++;
            LeaveCriticalSection(&m_cs);
        }
        if (hThread == NULL)
        {
            printf("CreateThread() failed: %d\n", GetLastError());
            break;
        }
        CloseHandle(hThread);
    }
    closesocket(sListen);

    WSACleanup();
    return 0;
}

【问题讨论】:

标签: c windows sockets localhost


【解决方案1】:

您的线程循环在遇到错误时未正确更新数组。而且您的主代码没有处理所有客户端断开连接然后新客户端连接的可能性,因此它可能会创建多个线程。

试试这个:

bool keepRunning = true;

DWORD WINAPI ClientThread(LPVOID lpParam)
{
    const char buffer[] = "Hello\n";
    const int buflen = strlen(buffer) ;

    while (keepRunning)
    {
        EnterCriticalSection(&m_cs);

        int i = 0;
        while ((i < nclients) && (keepRunning))
        {
            if (send(lClient[i], buffer, buflen, 0) == SOCKET_ERROR)
            {
                int err = WSAGetLastError();
                closesocket(lClient[i]);

                if (err != WSAENOTSOCK)
                    printf("Failed to send data to a client! Error: %d\n", err);

                for (int j = i+1; j < nclients; ++j)
                    lClient[j-1] = lClient[j];

                --nclients;
            }
            else
            {
                ++i;
            }
        }

        LeaveCriticalSection(&m_cs);

        Sleep(200); 
    }

    return 0;
}

int main(int argc, char **argv)
{
    WSADATA             wsd;
    SOCKET              sListen, sClient;
    int                 iAddrSize;
    HANDLE              hThread = NULL;
    DWORD               dwThreadId = 0;
    struct sockaddr_in  local, client;
    int iPort;

    InitializeCriticalSection(&m_cs);
    for (int i = 0; i < 10; ++i) lClient[i] = INVALID_SOCKET;

    iPort = 5000;

    int ret = WSAStartup(MAKEWORD(2,2), &wsd);
    if (ret != 0)
    {
        printf("Failed to load Winsock! Error: %d\n", ret);
        return 1;
    }

    // Create our listening socket
    //
    sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    if (sListen == SOCKET_ERROR)
    {
        printf("Failed to create listening socket! Error: %d\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }

    // Select the local interface and bind to it
    //
    local.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    local.sin_family = AF_INET;
    local.sin_port = htons(iPort);

    if (bind(sListen, (struct sockaddr *)&local, sizeof(local)) == SOCKET_ERROR)
    {
       printf("Failed to bind listening socket! Error: %d\n", WSAGetLastError());
       closesocket(sListen);
       WSACleanup();
       return 1;
    }

    // Start listening for clients
    //
    if (listen(sListen, 8) == SOCKET_ERROR)
    {
       printf("Failed to listen for clients! Error: %d\n", WSAGetLastError());
       closesocket(sListen);
       WSACleanup();
       return 1;
    }

    //
    // In a continuous loop, wait for incoming clients. Once
    // one is detected, create a thread to process clients.
    //
    while (keepRunning)
    {
        iAddrSize = sizeof(client);
        sClient = accept(sListen, (struct sockaddr *)&client, &iAddrSize);
        if (sClient == INVALID_SOCKET)
        {
            printf("Unable to accept a client! Error: %d\n", WSAGetLastError());
            break;
        }

        EnterCriticalSection(&m_cs);

        if (nclients == 10)
        {
            LeaveCriticalSection(&m_cs);
            closesocket(sClient);

            printf("Ignoring client: %s:%d, too many clients already connected\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));

            continue;
        }

        lClient[nclients] = sClient;
        ++nclients;

        LeaveCriticalSection(&m_cs);

        printf("Accepted client: %s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));

        if (!hThread)
        {
            hThread = CreateThread(NULL, 0, &ClientThread, 0, 0, &dwThreadId);
            if (hThread == NULL)
            {
                printf("Unable to create thread! Error: %u\n", GetLastError());
                break;
            }
        }
    }

    keepRunning = false;

    if (hThread != NULL)
    {
        WaitForSingleObject(hThread, INFINITE);
        CloseHandle(hThread);
    }

    for (int i = 0; i < nclients; ++i)
        closesocket(lClient[i]);
    closesocket(sListen);

    WSACleanup();

    return 0;
}

【讨论】:

  • 或者,使用他现有的代码,只需将元素设置为INVALID_SOCKET 并且不要减少计数。
  • 但是死客户端永远不会离开数组,而新客户端最终会溢出数组,因为原始代码没有进行任何边界检查。当客户端断开连接时,应将其从阵列中移除。原始代码没有正确地做到这一点(没有关闭死套接字,在错误地将数组计数器用作循环计数器时递减数组计数器等)。所以是的,原始代码没有正确管理数组,所以它很可能有时会遇到无效的套接字并因错误而失败。
  • 如果有的话,你可以将一个新客户放入INVALID_SOCKET 条目中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-15
  • 2014-12-21
相关资源
最近更新 更多