简体   繁体   中英

C Windows winapi + multi-threading (HANDLE)_beginthreadex fail

This code ruined all my day. Basically I have a list of 50 webservers I administrate, I want to check them if up/alive (isAlive() function), I parse my webservers.txt file with the 50 ips/hostnames and for fastness I try to use threads (10, 20 or 30 doesn't matter) then my compiled code seems only to exit without doing anything...any idea/help?

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <winsock.h>
#include <process.h>
#include <string.h>

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

unsigned int _stdcall isAlive(void *ptr)
{
  struct sockaddr_in blah;
  struct hostent *he;
  WSADATA wsaData;
  int i;
  WORD wVersionRequested;
  SOCKET sock;
  char* addr = (char*)ptr;

  char buff[1024];
  char *ex;
  ex="GET /alive.php HTTP/1.0\n\n";
  char *fmsg="ALIVE";

  wVersionRequested = MAKEWORD(1, 1);
  if (WSAStartup(wVersionRequested , &wsaData)){
    printf("Winsock Initialization failed.\n");
    return(1);
  }

  if ((sock=socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET){
    printf("Can not create socket.\n");
    return(1);
  }
  sock = socket(AF_INET,SOCK_STREAM,0);
  blah.sin_family = AF_INET;
  blah.sin_port = htons(80);
  blah.sin_addr.s_addr = inet_addr(addr);

  if ((he=gethostbyname(addr))!=NULL){
    memcpy((char *)&blah.sin_addr.s_addr,he->h_addr,he->h_length);
  }
  else{
    if((blah.sin_addr.s_addr=inet_addr(addr))==-1){
      WSACleanup();
      return(1);
    }
  }

  if (connect(sock,(struct sockaddr*)&blah,sizeof(blah))==0){
    send(sock,ex,strlen(ex),0);
    recv(sock,buff,sizeof(buff),0);
    if(strstr(buff,fmsg)!=NULL){
      printf("ALIVE: %s", addr);
    }
  }

  closesocket(sock);
  WSACleanup();
  _endthreadex(0);
  return(1);
}

int main(int argc,char *argv[])
{
  if(argc!=2){
    printf("Usage: %s <webservers list>\n", argv[0]);
    return(1);
  }

  char *inname = argv[1];
  FILE *infile;
  char line_buffer[BUFSIZ];
  char line_number;

  infile = fopen(inname, "r");
  if (!infile) {
    printf("Couldn't open file %s for reading.\n", inname);
    return 0;
  }

  line_number = 0;
  HANDLE hThreadArray[200];
  while (fgets(line_buffer, sizeof(line_buffer), infile)) {
    ++line_number;
    unsigned threadID;
    hThreadArray[line_number] = (HANDLE)_beginthreadex(0, 0, isAlive, line_buffer, 0, &threadID);
  }
  WaitForMultipleObjects(sizeof(line_buffer), hThreadArray, TRUE, INFINITE);
  return 0;
}

New code after your advices:

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <winsock.h>
#include <process.h>
#include <string.h>

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

unsigned int _stdcall isAlive(void *ptr)
{
  struct sockaddr_in blah;
  struct hostent *he;
  WSADATA wsaData;
  int i;
  WORD wVersionRequested;
  SOCKET sock;
  char* addr = (char*)ptr;

  char buff[1024];
  char *ex;
  ex="GET /alive.php HTTP/1.1\n\n";
  char *fmsg="ALIVE";

  wVersionRequested = MAKEWORD(1, 1);
  if (WSAStartup(wVersionRequested , &wsaData)){
    printf("Winsock Initialization failed.\n");
    return(1);
  }

  if ((sock=socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET){
    printf("Can not create socket.\n");
    return(1);
  }
  sock = socket(AF_INET,SOCK_STREAM,0);
  blah.sin_family = AF_INET;
  blah.sin_port = htons(80);
  blah.sin_addr.s_addr = inet_addr(addr);

  if ((he=gethostbyname(addr))!=NULL){
    memcpy((char *)&blah.sin_addr.s_addr,he->h_addr,he->h_length);
  }
  else{
    if((blah.sin_addr.s_addr=inet_addr(addr))==-1){
      WSACleanup();
      return(1);
    }
  }

  if (connect(sock,(struct sockaddr*)&blah,sizeof(blah))==0){
    send(sock,ex,strlen(ex),0);
    recv(sock,buff,sizeof(buff),0);
    if(strstr(buff,fmsg)!=NULL){
      printf("ALIVE: %s\n", addr);
    }
  }

  closesocket(sock);
  WSACleanup();
  _endthreadex(0);
  return(1);
}

int main(int argc,char *argv[])
{
  if(argc!=2){
    printf("Usage: %s <webservers list>\n", argv[0]);
    return(1);
  }

  char *inname = argv[1];
  FILE *infile;
  char line_buffer[10000];
  char line_number;

  infile = fopen(inname, "r");
  if (!infile) {
    printf("Couldn't open file %s for reading.\n", inname);
    return 0;
  }

  line_number = 0;
  HANDLE hThreadArray[200];
  while (fgets(line_buffer, sizeof(line_buffer), infile)) {
    unsigned threadID;
    hThreadArray[line_number] = (HANDLE)_beginthreadex(0, 0, isAlive, line_buffer, 0, &threadID);
    ++line_number;
  }
  WaitForMultipleObjects(line_number, hThreadArray, TRUE, INFINITE);
  fclose(infile);
  return 0;
}

Now my code runs but it takes the last line in my text and makes multiple threads with that, I'm lost :(

hosts.txt (11 lines in it)

myhost.com
mysecondhost.com
...
mylasthost.com

result: C:\\Documents and Settings\\Xtmtrx\\Desktop\\Code>checkalive.exe hosts.txt

ALIVE: mylasthost.com
ALIVE: mylasthost.com
ALIVE: mylasthost.com
ALIVE: mylasthost.com
ALIVE: mylasthost.com
ALIVE: mylasthost.com
ALIVE: mylasthost.com
ALIVE: mylasthost.com
ALIVE: mylasthost.com
ALIVE: mylasthost.com
ALIVE: mylasthost.com

If I do this:

#define MAX 10000

/* snip */

HANDLE hThreadArray[200];
char str[MAX];
char *x[MAX];
int i =0;

while(!feof(infile)) {
  while(fgets(str, sizeof str, infile)) {
    unsigned threadID;
    x[i] = strdup(str);
    printf("%s", *(x+i));
    hThreadArray[i] = (HANDLE)_beginthreadex(0, 0, isAlive, *(x+i), 0, &threadID);
    i++;
  }
}

would be right?

LAST EDIT:

This is my finished code, seems to work, I added timeout also but doesn't seem to be taken in consideration so it can hang on some hosts:

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <winsock.h>
#include <process.h>
#include <string.h>

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

#define MAX 10000

unsigned int _stdcall isAlive(void *ptr)
{
  struct sockaddr_in blah;
  struct hostent *he;
  WSADATA wsaData;
  int i;
  WORD wVersionRequested;
  SOCKET sock;
  char* addr = (char*)ptr;

  char buff[1024];
  char *request;
  request="GET /alive.php HTTP/1.0\n\n";
  char *fmsg="ALIVE";

  wVersionRequested = MAKEWORD(1, 1);
  if (WSAStartup(wVersionRequested , &wsaData)){
    printf("Winsock Initialization failed.\n");
    return(1);
  }

  if ((sock=socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET){
    printf("Can not create socket.\n");
    return(1);
  }
  sock = socket(AF_INET,SOCK_STREAM,0);
  blah.sin_family = AF_INET;
  blah.sin_port = htons(80);
  blah.sin_addr.s_addr = inet_addr(addr);

  if ((he=gethostbyname(addr))!=NULL){
    memcpy((char *)&blah.sin_addr.s_addr,he->h_addr,he->h_length);
  }
  else{
    if((blah.sin_addr.s_addr=inet_addr(addr))==-1){
      WSACleanup();
      return(1);
    }
  }

  if (connect(sock,(struct sockaddr*)&blah,sizeof(blah))==0){
    send(sock,request,strlen(request),0);
    recv(sock,buff,sizeof(buff),0);
    if(strstr(buff,fmsg)!=NULL){
      printf("ALIVE: %s", addr);
    }
  }

  closesocket(sock);
  WSACleanup();
  _endthreadex(0);
  return(1);
}

int main(int argc,char *argv[])
{
  if(argc!=2){
    printf("Usage: %s <webservers list>\n", argv[0]);
    return(1);
  }

  char *inname = argv[1];
  FILE *infile;
  char line_buffer[BUFSIZ];
  char line_number;

  infile = fopen(inname, "r");
  if (!infile) {
    printf("Couldn't open file %s for reading.\n", inname);
    return 0;
  }

  HANDLE hThreadArray[200];
  char str[MAX];
  char *x[MAX];
  int i = 0;
  while(!feof(infile)) {
    while(fgets(str, sizeof str, infile)) {
      unsigned threadID;
      x[i] = strdup(str);
      //printf("%s", *(x+i)); // DEBUG
      hThreadArray[i] = (HANDLE)_beginthreadex(0, 0, isAlive, *(x+i), 0, &threadID);
      i++;
      }
  }

  WaitForMultipleObjects(i, hThreadArray, TRUE, INFINITE);
  fclose(infile);
  return 0;
}

Any thoughts/ideas?

There are quite a few problems with your code, not least:

  1. The first parameter to WaitForMultipleObjects takes a NUMBER of elements, not a SIZE of a buffer. use line_number instead.

  2. Your loop never sets the first element (element 0) of the array because you increment line_number to 1 before you set it via hThreadArray[line_number].

  3. You are passing a pointer to a stack allocated buffer (line_buffer) as the parameter to the thread. You then continue to alter this stack buffer before the thread has started. Parameters sent as thread start arguments need to be allocated on the heap (one per thread, and the thread is responsible for freeing it later).

  4. You never actually check the response code of _beginThreadEx, so you can't know for sure if the thread started at all!

  5. You don't have any printf statements at the beginning of your thread. Putting one there would help you debug whether your threads are ever starting.

  6. The HTTP parameter to the server is likely to fail. Take a look in Wireshark at what a real GET request looks like and copy it. You should at the very least sent a HTTP/1.1 with a Host header.

  7. There are paths through your new thread code that don't call WSACleanup() despite calling WSAStartup() first.

Think about what will happen when the main thread carries on with the loop before the thread it's just started does anything useful.

It will overwrite the memory at line_buffer , that's what it will do. This will change the memory before the thread gets a chance to look at it. In other words, consider this timeline:

main populates linebuffer
main starts thread 1
                               thread1 starts
main populates linebuffer
                               thread1 reads linebuffer

You can see there that the buffer has changed before the thread has looked at it.

If you're going to share data between threads like that, you'll need to ensure that they properly serialise access. There are several ways to do this, some of which are below:

  • Make the line buffers an array, just like the thread objects. This means main won't overwrite a previously sent one.
  • Use some form of communication between the thread and main so that main waits until the thread has made a local copy before continuing.

In addition, I'm not sure that your WaitForMultipleObjects should be using sizeof(line_buffer) as the object count. Surely line_number would be a better choice since that's the actual count of objects you created.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM