C Windows winapi +多线程(HANDLE)_beginthreadex失败

时间:2013-01-28 06:58:09

标签: c multithreading winapi winsock

这段代码毁了​​我一整天。基本上我有一个我管理的50个web服务器的列表,我想检查它们是否为up / alive(isAlive()函数),我用50 ips / hostnames解析我的webservers.txt文件,并且为了坚牢,我尝试使用线程(10 ,20或30没关系)然后我的编译代码似乎只是退出而没有做任何事情......任何想法/帮助?

#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;
}

建议后的新代码:

#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;
}

现在我的代码运行了,但它占用了我的文本中的最后一行并创建了多个线程,我迷路了:(

hosts.txt(其中有11行)

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

结果: C:\ Documents and Settings \ Xtmtrx \ Desktop \ Code&gt; 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

如果我这样做:

#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++;
  }
}

会不会?

最后编辑:

这是我完成的代码,似乎工作,我也添加了超时但似乎没有考虑因为它可以挂在一些主机上:

#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;
}

任何想法/想法?

2 个答案:

答案 0 :(得分:3)

您的代码存在很多问题,尤其是:

  1. WaitForMultipleObjects的第一个参数需要NUMBER个元素,而不是缓冲区的SIZE。改为使用line_number。

  2. 您的循环从不设置数组的第一个元素(元素0),因为在通过hThreadArray [line_number]设置之前将line_number增加为1。

  3. 您正在将指向堆栈分配缓冲区(line_buffer)的指针作为线程的参数传递。然后在线程启动之前继续更改此堆栈缓冲区。作为线程起始参数发送的参数需要在堆上分配(每个线程一个,线程负责以后释放它)。

  4. 您实际上从未检查过_beginThreadEx的响应代码,因此您无法确定该线程是否已启动!

  5. 您的主题开头没有任何printf语句。把它放在那里可以帮助你调试你的线程是否一直在启动。

  6. 服务器的HTTP参数可能会失败。在Wireshark中查看真实的GET请求是什么样的并复制它。您至少应该发送带有主机头的HTTP / 1.1。

  7. 尽管先调用WSAStartup(),但新的线程代码中仍有一些路径不会调用WSACleanup()。

答案 1 :(得分:1)

考虑当主线程继续使用循环之前会发生什么,它刚刚启动的线程会有用。

它将覆盖line_buffer处的内存,这就是它将要执行的操作。这将在线程有机会查看之前更改内存。换句话说,请考虑此时间表:

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

你可以看到缓冲区在线程查看之前已经发生了变化。

如果要在这样的线程之间共享数据,则需要确保它们正确地序列化访问。有几种方法可以做到这一点,其中一些方法如下:

  • 使线缓冲一个数组,就像线程对象一样。这意味着main不会覆盖先前发送的。{/ li>
  • 在线程与main之间使用某种形式的通信,以便main等待线程制作本地副本,然后再继续。

此外,我不确定您的WaitForMultipleObjects应该使用sizeof(line_buffer)作为对象计数。当然line_number将是一个更好的选择,因为这是你创建的对象的实际数量。