上次文了一个关于TClientWinSocket的问题。非常感谢"僵哥"的回答。经过几天的反反复复的修改测试。终于有点眉目了。
代码是这样的。
  FSocket := TClientWinSocket.Create(Integer(not(0)));
  //采用阻塞模式
  FSocket.ClientType := ctBlocking;
  try
    FSocket.Open(FHost, FHost, '', 8309); //连接服务端
    Timeout := 2000;                      //2秒超时
    HadSendConn := false;
    setsockopt(FSocket.SocketHandle, SOL_SOCKET, SO_RCVTIMEO, @Timeout, SizeOf(Timeout));
  except
  end;
  PeekMessage(msg, 0, 0, 0, PM_NOREMOVE);
  Event := WSACreateEvent;  //创建事件句柄
  try
    WSAEventSelect(FSocket.SocketHandle, Event, FD_READ or FD_CLOSE);
    while (not Terminated) and (FSocket.Connected=True) do
      case MsgWaitForMultipleObjects(1, Event, False, 500, QS_ALLINPUT) of
        //接收网络消息部分
        WAIT_OBJECT_0:
        begin
          if IsClose(FSocket.SocketHandle, Event) then
          begin
            { 'server close' ; }
            Break;
          end;
           //***********接收数据部分*********************
          RetLen := FSocket.ReceiveBuf(D.Content, D.Len);
          //**********如果接收到非法消息,程序continue
          wsaRecMsg := D.Content;
          PostMessage(frm_wyClientMain.Handle,WM_GETMES,WParam(wsaRecMsg),Length(wsaRecMsg));
          //***********接收数据结束*********
          //************重置事件句柄*********
          //关键是这句。如果接收到合法消息程序会到这里,事件对象状态清除为未置信号
           //也就是这样一句,可能使我的socket连接存在,但是永远不会进入到读状态。
           WSAResetEvent(Event);
         //********************************
        end;
        WAIT_OBJECT_0 + 1:
        begin
          //发送网络消息部分
          if PeekMessage(msg, 0, 0, 0, PM_REMOVE) then
            begin
              case msg.message of
                WM_USER:
                begin
                  //发送自定义消息 如 登陆,注销等
                end;
                WM_CLOSE:
                begin
                  //发送客户端正常关闭消息
                  //正常退出,服务端会对数据库做相应操作
                  //退出线程
                     break;
                end;
             end;
           end;
        end;
        WAIT_TIMEOUT:
        begin
          //线程启动时发送连接消息
           if HadSendConn = false then
          begin
            //置已连接标志为真
            HadSendConn := true;
          end;
        end;
       end;
  finally
    WSACloseEvent(Event);
  end;
上次僵哥说要我全部用API函数,他说可能TClientWinSocket会与我的代码有冲突。但是改了后还是一样。现在的问题是这个函数WSAResetEvent跟我的程序逻辑是不是冲突,请高手看一下。为什么会这样呢?是不是本身我就不应该使用这个函数在这里重置。。
请高手出马啊

解决方案 »

  1.   

    TClientWinSocket继续于TCustomWinSocket,其Open方法在TCustomWinSocket当中有实现。当采用block模式,在Open调用过程当中就会调用TCustomWinSocket.DoOpen,而DoOpen又会调用TCustomWinSocket.DoSetAsyncStyles,在DoSetAsyncStyles当中则有一个WSAAsyncSelect,而这之后,楼主再调用WSAEventSelect,你觉得系统会听谁的?
      

  2.   

    MSDN当中有这样一段描述,Issuing a WSAEventSelect for a socket cancels any previous WSAAsyncSelect or WSAEventSelect for the same socket and clears the internal network event record.意思也就是WSAEventSelect是可以覆盖掉原来的WSAAsyncSelect,但是如果服务器是一个主动服务(比如SMTP服务等有欢迎消息发送的,或者是完全主动推送的,那么在被覆盖掉之前会是什么情况?另外PeekMessage当中,除了上面的几个消息,以后的消息怎么办?就算是独立的线程,那如果里面还有别的如TTimer,那么这个WM_TIMER消息不就丢弃了?另外还是之前提到过的那个同步的问题。PostMessage不等待消息的处理。wsaRecMsg会是怎么样的一种情况?
      

  3.   

    谢谢。
    昨天终于发现了,原来问题不再这里段代码,而在我服务端。服务端可以正常发送心跳消息,客户端也可以正常接收、发送消息。只是服务端接收客户端消息有阻塞。导致socket连接正常,客户端可以正常收发情况下,服务端不能返回消息。就算是我知道了这种情况,释放掉原来连接重新建立的服务端线程还是不能启动。
    呵呵。害的搞了几天一直以为客户端同步或是阻塞的问题。
    谢谢僵哥。分继续给上。