unit Unit1;interfaceuses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs,IdWinSock2, StdCtrls;
       //用了Indy9的IdWinSock2单元
type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    SocketArray:array[0..wsa_maximum_wait_events-1] of TSocket;
    EventArray:array[0..wsa_maximum_wait_events-1] of wsaevent;
    NewEvent:wsaevent;
    NetWorkEvents:TWSANetworkEvents;
    InternetAddr:TSockAddrIn;
    AcceptSkt,ListenSkt:TSocket;
    EventTotal:DWORD;
    index:DWORD;
    { Public declarations }
  end;var
  Form1: TForm1;implementation//书上原文没有这个CompressArrays函数,按照自己的理解写了这个函数实现的内容
procedure CompressArrays(var pEventArray:array of wsaevent;pSocketArray:array of TSocket;var nEventTotal:DWORD;const nIndex:Integer);
var
  i:integer;
begin
  for i:=nIndex to Length(pEventArray)-2 do
  begin
    pEventArray[i]:=pEventArray[i+1];
    pSocketArray[i]:=pSocketArray[i+1];
  end;
  pEventArray[Length(pEventArray)-1]:=0;
  pSocketArray[Length(pEventArray)-1]:=0;
  Dec(nEventTotal);
end;{$R *.dfm}procedure TForm1.FormCreate(Sender: TObject);
var
  WSAData:TWSADATA;
begin
  //1.初始化 WinSock
  if (WSAStartup(MakeWord(2,0),WSAData)<>0) then
  begin
    //初始化失败
    Memo1.Lines.Add('Winsock Init Failed');
    Exit;
  end
  else
    Memo1.Lines.Add('Socket Start');
end;procedure TForm1.FormDestroy(Sender: TObject);
begin
  WSACleanup;
end;procedure TForm1.Button1Click(Sender: TObject);
var
  i:integer;
  buffer:PChar;
begin
  EventTotal:=0;
  NewEvent:=0;
  GetMem(buffer,1024);//自己加的  ListenSkt:=socket(pf_inet,sock_stream,0);
  InternetAddr.sin_family:=af_inet;
  InternetAddr.sin_addr.S_addr:=htonl(inaddr_any);
  InternetAddr.sin_port:=htons(5150);  bind(ListenSkt,@InternetAddr,SizeOf(InternetAddr));  NewEvent:=WSACreateEvent();//这里出错,提示Access violation at address 00000000.Read of address 00000000.  WSAEventSelect(ListenSkt,NewEvent,fd_accept or fd_close);  listen(ListenSkt,5);
  SocketArray[EventTotal]:=ListenSkt;
  EventArray[EventTotal]:=NewEvent;  while True do
  begin
    //等候所有套接字上的网络事件
    Index:=WSAWaitForMultipleEvents(EventTotal,@EventArray,False,wsa_infinite,False);
    Index:=index-wsa_wait_event_0;       //遍历所有事件,查看被传信的事件是否多于一个
    for i:=index to EventTotal-1 do
    begin
      Index:=WSAWaitForMultipleEvents(1,@(EventArray[i]),True,1000,False);
      if (index=WSA_Wait_Failed) or (index=WSA_wait_timeout) then
        Continue
      else begin
        Index:=i;
        WSAEnumNetworkEvents(SocketArray[index],EventArray[index],@NetWorkEvents);        //检查FD_Accept消息
        if (NetWorkEvents.lNetworkEvents and fd_accept)<>0 then
        begin
          if NetWorkEvents.iErrorCode[FD_Accept_bit]<>0 then
          begin
            Memo1.Lines.Add('FD_Accept failed with error '+IntToStr(NetWorkEvents.iErrorCode[fd_accept_bit]));
            Break;
          end;          //接受一个新连接,并将它添加到套接字及事件列表中
          AcceptSkt:=accept(Socketarray[index],nil,nil);          //无法处理多于WSA_MAXIMUM_WAIT_EVENTS数量的套接字,故关闭接受套接字
          if EventTotal>=WSA_MAXIMUM_WAIT_EVENTS then
          begin
            Memo1.Lines.Add('Too ManyConnections');
            closesocket(AcceptSkt);
            Break;
          end;          NewEvent:=WSACreateEvent();          WSAEventSelect(AcceptSkt,NewEvent,fd_read or fd_write or fd_close);          Inc(EventTotal);
          EventArray[EventTotal]:=NewEvent;
          SocketArray[EventTotal]:=AcceptSkt;
          //原文在这里Inc(EventTotal),我觉得应该写到前两行去
          Memo1.Lines.Add('Socket '+IntToStr(AcceptSkt)+' Connected!');
        end;        //处理FD_Read通知
        if (NetWorkEvents.lNetworkEvents and FD_read)<>0 then
        begin
          if (NetWorkEvents.iErrorCode[FD_read_Bit]<>0) then
          begin
            Memo1.Lines.Add('FD_read failed with error '+IntToStr(NetWorkEvents.iErrorCode[fd_read_bit]));
            Break;
          end;
          //从套接字读入数据
          ZeroMemory(buffer,SizeOf(buffer));
          recv(socketarray[index-wsa_wait_event_0],buffer,SizeOf(buffer),0);
          Memo1.Lines.Add('Socket ['+IntToStr(socketarray[index-wsa_wait_event_0])+'] Recv:'+buffer);
        end;        //处理FD_WRITE通知
        if (NetWorkEvents.lNetworkEvents and FD_Write)<>0 then
        begin
          if NetWorkEvents.iErrorCode[fd_write_bit]<>0 then
          begin
            Memo1.Lines.Add('FD_Write failed with error '+IntToStr(NetWorkEvents.iErrorCode[fd_write_bit]));
            break;
          end;
          send(SocketArray[index-wsa_wait_event_0],buffer,SizeOf(buffer),0);
          Memo1.Lines.Add('Socket ['+IntToStr(Socketarray[index])+'] Send something...');
        end;        if (NetWorkEvents.lNetworkEvents and FD_Close)<>0 then
        begin
          if NetWorkEvents.iErrorCode[FD_close_bit]<>0 then
          begin
            Memo1.Lines.Add('FD_Close failed with error '+Inttostr(NetWorkEvents.iErrorCode[FD_close_bit]));
            Break;
          end;
          closesocket(Socketarray[index]);
          Memo1.Lines.Add('Socket ['+IntToStr(Socketarray[index])+'] Close !');          //从Socket和Event数组删除套接字与其关联的事件,并递减EventTotal
          CompressArrays(EventArray,SocketArray,EventTotal,index);
        end;
      end;
    end;
  end;
  FreeMem(buffer);
  Memo1.Lines.Add('Server Exit!')
end;end.------------------------------
窗体上只有一个Memo1组件和一个Button1启动按钮,大家可以直接拷贝代码调试。执行到NewEvent:=WSACreateEvent();时提示错误:Access violation at address 00000000.Read of address 00000000.看了一些帖子,提示可能是内存未分配导致的,不过自己不知道要怎么改。 
也有帖子写有好像需要手动加载WS2_32.dll,不过也试过,好像不行。
不知道有没有朋友遇到过类似问题,怎么解决的,指点一下。
环境:Delphi7+Indy9+XP,代码使用的I/O模型是WSAventSelect模型。

解决方案 »

  1.   

    唉~我翻译的代码真烂,错了好几个地方,照着书打字还打漏了一行,更正如下,供D友们看这本书时参考。
    书上是P113页那段,我适当增加了一些信息输出的地方,用于方便调试。
    ------------------------------------
    unit Unit1;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs,JwaWinsock2,StdCtrls;type
      TForm1 = class(TForm)
        Memo1: TMemo;
        Button1: TButton;
        Button2: TButton;
        procedure FormCreate(Sender: TObject);
        procedure FormDestroy(Sender: TObject);
        procedure Button1Click(Sender: TObject);
        procedure Button2Click(Sender: TObject);
        procedure FormClose(Sender: TObject; var Action: TCloseAction);
      private
        { Private declarations }
      public
        SocketArray:array[0..WSA_MAXIMUM_WAIT_EVENTS-1] of TSocket;
        EventArray:array[0..WSA_MAXIMUM_WAIT_EVENTS-1] of WSAEVENT;
        NewEvent:wsaevent;
        NetWorkEvents:TWSANetworkEvents;
        InternetAddr:TSockAddrIn;
        AcceptSkt,ListenSkt:TSocket;
        EventTotal:DWORD;
        index:DWORD;
        Working:Boolean;
        { Public declarations }
      end;var
      Form1: TForm1;implementationprocedure CompressArrays(var pEventArray:array of wsaevent;pSocketArray:array of TSocket;var nEventTotal:DWORD;const nIndex:Integer);
    var
      i:integer;
    begin
      for i:=nIndex to Length(pEventArray)-2 do
      begin
        pEventArray[i]:=pEventArray[i+1];
        pSocketArray[i]:=pSocketArray[i+1];
      end;
      pEventArray[Length(pEventArray)-1]:=0;
      pSocketArray[Length(pEventArray)-1]:=0;
      Dec(nEventTotal);
    end;{$R *.dfm}procedure TForm1.FormCreate(Sender: TObject);
    var
      WSAData:TWSADATA;
    begin
      //1.初始化 WinSock
      if (WSAStartup(MakeWord(2,0),WSAData)<>0) then
      begin
        //初始化失败
        Memo1.Lines.Add('Winsock Init Failed');
        Exit;
      end
      else
        Memo1.Lines.Add('Socket initialize OK!');
    end;procedure TForm1.FormDestroy(Sender: TObject);
    begin
      WSACleanup;
    end;procedure TForm1.Button1Click(Sender: TObject);
    var
      i:DWORD;
      buffer:PChar;
      Re:Integer;
    begin
      Working:=True;
      EventTotal:=0;
      NewEvent:=0;
      GetMem(buffer,1024);
      ZeroMemory(buffer,1024);  ListenSkt:=socket(PF_INET,SOCK_STREAM,0);
      InternetAddr.sin_family:=AF_INET;
      InternetAddr.sin_addr.S_addr:=htonl(INADDR_ANY);
      InternetAddr.sin_port:=htons(8888);  Re:=bind(ListenSkt,@InternetAddr,SizeOf(InternetAddr));
      if Re=SOCKET_ERROR then
      begin
        Memo1.Lines.Add('Socket Bind Error ');
      end else
        Memo1.Lines.Add('Socket Bind OK ');  NewEvent:=WSACreateEvent();  WSAEventSelect(ListenSkt,NewEvent,FD_ACCEPT or FD_CLOSE);  listen(ListenSkt,5);
      SocketArray[EventTotal]:=ListenSkt;
      EventArray[EventTotal]:=NewEvent;
      Inc(EventTotal);  while Working do
      begin
        Application.ProcessMessages;
        //等候所有套接字上的网络事件
        Index:=WSAWaitForMultipleEvents(EventTotal,@EventArray,False,1000,False);
        Index:=index-WSA_WAIT_EVENT_0;
        Memo1.Lines.Add('Index='+IntToStr(index)+';WSA_WAIT_EVENT_0='+IntToStr(WSA_WAIT_EVENT_0));    //遍历所有事件,查看被传信的事件是否多于一个
        for i:=index to EventTotal-1 do
        begin
          Index:=WSAWaitForMultipleEvents(1,@(EventArray[i]),True,1000,False);
          if (index=WSA_WAIT_FAILED) or (index=WSA_WAIT_TIMEOUT) then
            Continue
          else begin
            Index:=i;
            WSAEnumNetworkEvents(SocketArray[index],EventArray[index],@NetWorkEvents);        //检查FD_Accept消息
            if (NetWorkEvents.lNetworkEvents and FD_ACCEPT)=FD_ACCEPT then
            begin
              if NetWorkEvents.iErrorCode[FD_ACCEPT_BIT]<>0 then
              begin
                Memo1.Lines.Add('FD_ACCEPT failed with error '+IntToStr(NetWorkEvents.iErrorCode[FD_ACCEPT_BIT]));
                Break;
              end;          //接受一个新连接,并将它添加到套接字及事件列表中
              AcceptSkt:=accept(SocketArray[index],nil,nil);          //无法处理多于WSA_MAXIMUM_WAIT_EVENTS数量的套接字,故关闭接受套接字
              if EventTotal>=WSA_MAXIMUM_WAIT_EVENTS then
              begin
                Memo1.Lines.Add('Too ManyConnections');
                closesocket(AcceptSkt);
                Break;
              end;          NewEvent:=WSACreateEvent();          WSAEventSelect(AcceptSkt,NewEvent,FD_READ or FD_WRITE or FD_CLOSE);          EventArray[EventTotal]:=NewEvent;
              SocketArray[EventTotal]:=AcceptSkt;
              Inc(EventTotal);
              Memo1.Lines.Add('Socket '+IntToStr(AcceptSkt)+' Connected!');
            end;        //处理FD_Read通知
            if (NetWorkEvents.lNetworkEvents and FD_READ)=FD_READ then
            begin
              if (NetWorkEvents.iErrorCode[FD_READ_BIT]<>0) then
              begin
                Memo1.Lines.Add('FD_read failed with error '+IntToStr(NetWorkEvents.iErrorCode[FD_READ_BIT]));
                Break;
              end;
              Memo1.Lines.Add('Socket ['+IntToStr(SocketArray[index])+'] Read something...');
              //从套接字读入数据
              ZeroMemory(buffer,1024);
              recv(socketarray[index-WSA_WAIT_EVENT_0],buffer^,1024,0);
              Memo1.Lines.Add('Socket ['+IntToStr(SocketArray[index-WSA_WAIT_EVENT_0])+'] Recv:'+buffer);
            end;        //处理FD_WRITE通知
            if (NetWorkEvents.lNetworkEvents and FD_WRITE)=FD_WRITE then
            begin
              if NetWorkEvents.iErrorCode[FD_WRITE_BIT]<>0 then
              begin
                Memo1.Lines.Add('FD_Write failed with error '+IntToStr(NetWorkEvents.iErrorCode[FD_WRITE_BIT]));
                break;
              end;
              send(SocketArray[index-WSA_WAIT_EVENT_0],buffer^,1024,0);
              Memo1.Lines.Add('Socket ['+IntToStr(SocketArray[index])+'] Send something...');
            end;        //处理FD_CLOSE通知
            if (NetWorkEvents.lNetworkEvents and FD_CLOSE)=FD_CLOSE then
            begin
              if NetWorkEvents.iErrorCode[FD_CLOSE_BIT]<>0 then
              begin
                Memo1.Lines.Add('FD_Close failed with error '+Inttostr(NetWorkEvents.iErrorCode[FD_CLOSE_BIT]));
                closesocket(SocketArray[index]);
                Memo1.Lines.Add('Socket ['+IntToStr(SocketArray[index])+'] Close !');
                CompressArrays(EventArray,SocketArray,EventTotal,index);
                Break;
              end;
              closesocket(SocketArray[index]);
              Memo1.Lines.Add('Socket ['+IntToStr(SocketArray[index])+'] Close !');          //从Socket和Event数组删除套接字与其关联的事件,并递减EventTotal
              CompressArrays(EventArray,SocketArray,EventTotal,index);
            end;
          end;
        end;
      end;
      FreeMem(buffer);
      Memo1.Lines.Add('Server Exit!')
    end;procedure TForm1.Button2Click(Sender: TObject);
    begin
      Working:=False;
    end;procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
    begin
      Working:=False;
    end;end.