为什么套接字在多线程时不工作

Why is socket not working when multithreaded?

本文关键字:工作 多线程 套接字 为什么      更新时间:2023-10-16

我有一个非常简单的recvfrom()命令,只要不在"另一个"线程中调用,它就可以正常工作。

我会发布更多的代码,但其中有很多,所以希望我能过滤掉相关的部分:首先我们有一个全局变量:SOCKET Socket=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);

只要不涉及线程,这就可以很好地工作:

char message[_max_message_];
struct sockaddr_in* from;
int r;
    int SenderAddrSize = sizeof (struct sockaddr);
    r=recvfrom(Socket,message,_max_message_,0,(struct sockaddr *)&from,&SenderAddrSize);
    printf("Bytes recieved: %inError Code: %in",r,WSAGetLastError);

现在,我在线程后面调用了相同的代码,如下所示:pthread_create(&listener, NULL, listenloop, &Socket);

(代码基本忽略&socket。)

来自被调用线程的第一个执行的recvfrom()返回-1,但来自"原始"线程(其中设置了网络)的recvfrom()成功地用来自服务器的消息填充了message

好心地告诉我我做错了什么?

编辑:我不喜欢对好心帮助我的陌生人说十几句话,但如果我不这样做,我想我不会得到答案。所以,这是经过轻微编辑的工具包和kaboodle:

#include <iostream>
//#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <conio.h>
using namespace std;
#include <string>
//One thread shall listen continually for responses from the server.
/*The other thread shall listen continually for user input, and fire off user input at the local
 client to the server...*/
//#ifdef _WINDOWS
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
SOCKET Socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
inline int randport()
{
  return (50000 % rand() + 1000);
}
#define _serverip_ "***.***.***.***"
#define _welcome_ "Welcome,Wagon!"
#define _randomport_ 64000%rand()+100
#define _max_message_ 100
void *listenloop(void *arg)
{
  //SOCKET* listener = (SOCKET)arg;
  WSADATA WsaDat;
  WSAStartup(MAKEWORD(2, 0), &WsaDat);
  char message[_max_message_];
  //SOCKET listener=(SOCKET)arg;
  int r;
  //sockaddr_in SenderAddr;
  struct sockaddr_in from;
  //while (1){
  int SenderAddrSize = sizeof(struct sockaddr);
  r = recvfrom(Socket, message, _max_message_, 0, (struct sockaddr *) &from,
      &SenderAddrSize);
  printf("Thread Bytes recieved: %inThread Error Code: %in", r,
      WSAGetLastError);
  return NULL ;
  //}
  return NULL ;
}
int main()
{
  string user, pass, login;
  WSADATA WsaDat;
  WSAStartup(MAKEWORD(2, 0), &WsaDat);
  int port;
  cout << "Welcome!"
  SOCKET Socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  fflush(stdin); //As long as we compile with GCC Behavoir should be consistant
  //TRY NOT TO SEND PLAINTEXT PASSWORDS LIKE THIS!  IT MAY MAKE YOUR USERS VULNERABLE!  DONE FOR SAKE OF SIMPLICITY HERE!
  cout << "nnPlease enter the username you registered with:";
  getline(cin, user);
  cout << "nPlease enter your password, my good sir: ";
  getline(cin, pass);
  struct hostent *host;
  host = gethostbyaddr(_serverip_, strlen(_serverip_), AF_INET);
  if (host == NULL )
  {
    cout << "nn UNABLE TO CONNECT TO SERVER.  QUITTING. ";
    return -1;
  }
  short errorcount = 3;
  int socketfeedback;
  ///Put the address for the server on the "evelope"
  SOCKADDR_IN SockAddr;
  SockAddr.sin_port = htons(port);
  SockAddr.sin_family = AF_INET;
  SockAddr.sin_addr.s_addr = inet_addr(_serverip_);
  ///Sign the letter...
  int myport = _randomport_;
  int code;
  SOCKADDR_IN service;
  service.sin_family = AF_INET;
  service.sin_addr.s_addr = inet_addr("localhost");
  service.sin_port = htons(myport);
  //bind(Socket, (SOCKADDR *) &service, sizeof(service));
  //Start a thread, listening for that server
  while ((errorcount))
  {
    code = bind(Socket, (SOCKADDR *) &service, sizeof(service));
    if (code)
      break;
    else
      return -5;
    errorcount--;
    myport = _randomport_;
    service.sin_port = htons(myport);
  }
  login = user + ',' + pass;
  if (!errorcount)
  {
    cout << "nnMiserable failure.  Last Known Error Code: " << code;
    return -1;
  }
  ///Begin the listen loop!!
  pthread_t listener;
  pthread_create(&listener, NULL, listenloop, &Socket);
  struct sockaddr result;
  sendto(Socket, login.c_str(), strlen(login.c_str()), 0,
      (struct sockaddr *) &SockAddr, sizeof(SockAddr));
  char message[_max_message_];
  //SOCKET listener=(SOCKET)arg;
  //sockaddr_in SenderAddr;
  struct sockaddr_in from;
  int r;
  int SenderAddrSize = sizeof(struct sockaddr);
  r = recvfrom(Socket, message, _max_message_, 0, (struct sockaddr *) &from,
      &SenderAddrSize);
  printf("Bytes recieved: %inError Code: %in", r, WSAGetLastError);
  //SOCKET listener=(SOCKET)arg;
  WSACleanup();
  return 0;
}

为什么使用全局Socket?为什么你主要声明另一个Socket?您应该更好地使用pthread_create中传递的套接字(只需将listenloop中的args强制转换为SOCKET *)。多线程中的全局变量是一个非常糟糕的主意(您需要同步机制)。并用零初始化struct sockaddr_in from(例如用memset,或者按照alk所说的操作:struct sockaddr_in from = {0})。

而且,您正在从两个不同线程中的一个套接字读取,而没有任何同步。这势必会导致许多错误。

另外,我在其他线程中看到了WSACleanuprecvfrom的问题。你不知道这两个会以什么顺序运行(所以你也可以在其他线程中使用recvfrom之前获得WSACleanup)。你可以使用pthread_join等待其他线程完成,然后再执行WSACleanup

这对于注释来说太长了。

发布的代码根本不起作用,因为声明:

struct sockaddr_in* from;

然后像这样使用from

r=recvfrom(Socket,message,_max_message_,0,(struct sockaddr *)&from,&SenderAddrSize);

您正在使用struct sockaddr_in地址的地址,而不是仅使用其地址。

Is应为:

r=recvfrom(Socket,message,_max_message_,0,(struct sockaddr *)from,&SenderAddrSize);

但是,如果这样做,您将错过为from分配内存的机会。

struct sockaddr_in* from;

是一个拼写错误,应该阅读:

struct sockaddr_in from = {0};