0

这段代码毁了​​我一整天。基本上我有一个我管理的 50 个网络服务器的列表,我想检查它们是否启动/活着(isAlive() 函数),我用 50 个 ips/主机名解析我的 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>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;
}

有什么想法/想法吗?

4

2 回答 2

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 请求是什么样子并复制它。您至少应该发送一个带有 Host 标头的 HTTP/1.1。

  7. 尽管首先调用了 WSAStartup(),但通过新线程代码的路径不会调用 WSACleanup()。

于 2013-01-28T07:07:28.443 回答
1

想想当主线程在它刚刚启动的线程做任何有用的事情之前继续循环时会发生什么。

它将覆盖 处的内存line_buffer,这就是它会做的事情。这将在线程有机会查看之前更改内存。换句话说,考虑这个时间线:

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

您可以在那里看到缓冲区在线程查看之前已更改。

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

  • 使行缓冲区成为一个数组,就像线程对象一样。这意味着main不会覆盖以前发送的。
  • 在线程之间使用某种形式的通信,main以便main等到线程在继续之前制作本地副本。

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

于 2013-01-28T07:06:00.567 回答