C- Windows线程以接受插座上的连接



我熟悉pthreads,但对于Windows线程来说是新的。在Linux中,可以以:

开始新线程
pthread_t tid;
int rc = pthread_create(&tid, NULL, Threadfn, &newsocket);
assert (rc == 0);
//<snip>//

Threadfn可以轻松重建插座:

void *Threadfn(void *vargp){
    pthread_detach(pthread_self());
    int *Socket = (int *) vargp;
    print("Socket is %dn", *Socket);
    // recv/read/send etc.. 
    pthread_exit(NULL);
}

我们如何在Windows线程中执行此操作?

我创建线程:

HANDLE thread = CreateThread(NULL, 0, Somethready, &ClientSocket, 0, NULL);

,但我似乎在Somethready上遇到麻烦:

DWORD WINAPI Somethready(void *vargp) {
    printf("Thread got evokedn");
    SOCKET *clientSocket = (SOCKET *)vargp;
    SOCKET ClientSocket = *clientSocket;
    printf("In thread, ClientSocket: %dn", ClientSocket);
    /*
    char RecvBuf[bufsize];
    memset(RecvBuf, 0, bufsize);
    int n = recv(ClientSocket, RecvBuf, bufsize,0);
    print("We got %d bytes, we got %sn", n, RecvBuf);
    */
    return 0;
}

我似乎无法正确正确:

ClientSocket: 184
Thread got evoked
In thread, ClientSocket: -1 // <<-- this 

我在做什么错?IOW,如何将Clienterocket传递到Windows线程右路?

谢谢!

编辑1

以下是ClientSocket的形状:

while (1) {
    SOCKET ClientSocket = INVALID_SOCKET;
    ClientSocket = accept(ListenSocket, NULL, NULL);
    if (ClientSocket == INVALID_SOCKET) {
        printf("accept failed: %dn", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    printf("ClientSocket: %dn", ClientSocket);
    HANDLE thread = CreateThread(NULL, 0, Somethready, &ClientSocket, 0, NULL);
}

编辑2

感谢您的答复。我有点摇摇欲坠,因为我以前从未在Linux中遇到过这种情况 - 至少每当我尝试过时,变量并不会很快消失。但是,这是我第一次尝试Windows线程时渗透。这是一个无价的教训。

问题:我注意到,如果我在调用线程后立即添加一个轻微的延迟(如下所示(稍后有一个堆积的杂物来清理,这使其更具吸引力。我很好奇这是可以接受的,还是等待这是一场灾难。谢谢!

while (1) {
    SOCKET ClientSocket = INVALID_SOCKET;
    ClientSocket = accept(ListenSocket, NULL, NULL);
    if (ClientSocket == INVALID_SOCKET) {
        printf("accept failed: %dn", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    printf("ClientSocket: %dn", ClientSocket);
    HANDLE thread = CreateThread(NULL, 0, Somethready, &ClientSocket, 0, NULL);
    Sleep(30); // < -- this 
}

已发布的Windows代码和已发布的Linux代码中的一个问题是,您传递给新产生的线程的参数是指向父母线程堆栈上局部变量的指针,并且在孩子线程有机会开始运行并查看它之前,该局部变量很可能已经从堆栈中弹出(因此可能被其他数据覆盖(。

因此,解决问题的解决方案是确保当孩子线程查看它时数据仍然有效。您可以通过多种方法来执行此操作:

1(简单(通常是最好的(方法:而不是分配堆栈上的套接字(作为本地变量(,而是从堆中分配:

// main thread
while (1) {
    SOCKET * pClientSocket = (SOCKET *) (malloc(sizeof(SOCKET)));  // allocate a SOCKET on the heap
    if (pClientSocket == NULL) {printf("malloc() failed!?n"); break;}
    *pClientSocket = accept(ListenSocket, NULL, NULL);
    if (*pClientSocket == INVALID_SOCKET) {
        free(pClientSocket);  // avoid memory leak!
        printf("accept failed: %dn", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    printf("ClientSocket: %dn", *pClientSocket);
    HANDLE thread = CreateThread(NULL, 0, Somethready, pClientSocket, 0, NULL);
    if (thread == NULL)
    {
       closesocket(*pClientSocket);  // avoid socket leak!
       free(pClientSocket);  // avoid memory leak!
       printf("CreateThread failed!?n");
    }
}
// child thread
DWORD WINAPI Somethready(void *vargp) {
    printf("Thread got evokedn");
    SOCKET *pClientSocket = (SOCKET *)vargp;
    SOCKET ClientSocket   = *pClientSocket;  // make a copy of the heap-allocated SOCKET object into a local variable
    free(pClientSocket);                     // then free the heap-allocated SOCKET to avoid a memory leak
    printf("In thread, ClientSocket: %dn", ClientSocket);
    [...]
    closesocket(ClientSocket);  // don't forget to close the socket when we're done
    return 0;
}

它会很好地工作,因为堆已分配的 SOCKET对象(由 pClientSocket指向(保证在有人在其上调用 free(),并且此代码将其留在子线程中,然后将其列为此之后。已将其内容复制到局部变量ClientSocket中。

唯一的潜在陷阱是,如果您忘记在堆积的插座上拨打free()(例如,在错误处理的早期回收案例中(,很容易意外创建记忆。那。

2(便宜的方法。这涉及一些潜在的不安全/不确定行为的铸造,但在实践中起作用,因此很多人都这样做。在这种方法中,我们只需将插座的值直接塞入Void-Pointer中。我不建议这样做,但要完整:

// main thread
while (1) {
    SOCKET ClientSocket = accept(ListenSocket, NULL, NULL);
    if (ClientSocket == INVALID_SOCKET) {
        printf("accept failed: %dn", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    printf("ClientSocket: %dn", *ClientSocket);
    HANDLE thread = CreateThread(NULL, 0, Somethready, (void *) ClientSocket, 0, NULL);
    if (thread == NULL)
    {
       closesocket(ClientSocket);  // avoid socket leak
       printf("CreateThread failed!?n");
    }
}
// child thread
DWORD WINAPI Somethready(void *vargp) {
    printf("Thread got evokedn");
    SOCKET ClientSocket = (SOCKET)vargp;  
    printf("In thread, ClientSocket: %dn", ClientSocket);
    [...]
    closesocket(ClientSocket);  // don't forget to close the socket itself when we're done
    return 0;
}

3(我非常贴上我的程序员,以我自己的好方法:在这种方法中,在产生子线程后,我们使用条件变量来阻止主线程的执行,直到Child Thread表示它已经开始运行,并且不再使用指针到Main-the-thread的堆栈变量。我将在伪代码中写这本书,因为我不在Windows机器上测试它,但这应该给您一个总体想法:

// global variables (or if you don't like global variables, you 
// could put these into a struct, along with the SOCKET object, 
// and pass a pointer-to-the-struct to the child thread instead)
CONDITION_VARIABLE wait_for_child_thread;
CRITICAL_SECTION   critical_section;
InitializeCriticalSection(&critical_section);
InitializeConditionVariable(&wait_for_child_thread);
// main thread
while (1) {
    SOCKET ClientSocket = accept(ListenSocket, NULL, NULL);
    if (ClientSocket == INVALID_SOCKET) {
        printf("accept failed: %dn", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    printf("ClientSocket: %dn", *ClientSocket);
    HANDLE thread = CreateThread(NULL, 0, Somethready, &ClientSocket, 0, NULL);
    if (thread != NULL)
    {  
       // Gotta wait here until the child thread wakes us up,
       // otherwise we risk invalidating (&ClientSocket) before he has used it!
       EnterCriticalSection(&critical_section);
       SleepConditionVariableCS(&wait_for_child_thread, &critical_section, INFINITE);
       LeaveCriticalSection(&critical_section);
    }
    else
    {  
       printf("CreateThread failed!?n");
    }
}
// child thread
DWORD WINAPI Somethready(void *vargp) {
    printf("Thread got evokedn");
    SOCKET * pClientSocket = (SOCKET *)vargp;
    SOCKET ClientSocket    = *pClientSocket;  // copy from main-thread's stack to our own stack
    // Now that we've made the copy, tell the main thread he can continue execution
    WakeConditionVariable(&wait_for_child_thread);
    printf("In thread, ClientSocket: %dn", ClientSocket);
    [...]
    closesocket(ClientSocket);  // don't forget to close the socket itself when we're done
    return 0;
}

如何确保ClientSocket在堆上堆之前停留一段时间

常见模式是:

while (1) {
  SOCKET ClientSocket = INVALID_SOCKET;
  ClientSocket = accept(ListenSocket, NULL, NULL);
  ...
  printf("ClientSocket: %dn", ClientSocket);
  {
    SOCKET * psd = malloc(sizeof *psd); /* allocate in the parent */
    *psd = ClientSocket;
    HANDLE thread = CreateThread(NULL, 0, Somethready, psd, 0, NULL);
  }

和线程内部执行:

DWORD WINAPI Somethready(void *vargp) {
  printf("Thread got evokedn");
  SOCKET *pClientSocket = (SOCKET *)vargp;
  SOCKET ClientSocket = *pClientSocket;
  printf("In thread, ClientSocket: %dn", ClientSocket);
  ...
  free(pClientSocket);  /* deallocate in the child */
  return 0;
}

顺便说一句,进入您的陷阱不是特定于Windows线程的特定,而是Posix线程完全相同的。

最新更新