我写了一个很简单的阻塞模式客户端1. 启动SOCKET 和 创建阻塞模式的SOCKET
  procedure Startup;
  var
    rWSAData: TWSADATA;
    wSockVer: Word;
    pIniFile: TIniFile;
  begin
    wSockVer := MAKEWORD(2,0);
    if Winsock.WSAStartup( wSockVer, rWSAData ) <> 0 then
    begin
      Exit;
    end;
    m_dwSocket := Winsock.socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
  end;2. 连接服务器
SevrConnect(dwSocket: DWORD; const pchAddr: PAnsiChar; wPort: WORD): Boolean;
var
  uAddr: Integer;
  pHost: PHostEnt;
  rRemote: TSockAddrIn;
begin
  Result := False;
  // 没有创建要连接的SOCKET
  if dwSocket = DWORD(INVALID_SOCKET) then Exit;                                
  // 先假定szAddr是IP地址
  uAddr := Winsock.inet_addr( pchAddr );  
  // 不是IP地址, 就认为这是主机的名称                                      
  if uAddr = INADDR_NONE then                                                   
  begin
    // 尝试用主机名称获得主机IP
    pHost := Winsock.gethostbyname( pchAddr );
    // 也不是主机名称就退出                                  
    if pHost = nil then                                                         
      Exit;
      // 得到以网络字节顺序排列的IP地址
      uAddr := PInAddr(pHost^.h_addr_list^)^.S_addr;                              
    end;
    // 填写服务器地址信息
    rRemote.sin_family := AF_INET;           
    // 端口                                     
    rRemote.sin_port := Winsock.htons( wPort );  
     // IP地址                                 
    rRemote.sin_addr.S_addr := uAddr;     
    // 连接到远程机                                       
    Result := (Winsock.connect( dwSocket, rRemote, Sizeof(rRemote) ) = 0 );   if not Result then
  begin
    // 判断是否已经连接中, 连接中也是已连接的状态
    if WinSock.WSAGetLastError = WinSock.WSAEISCONN then
      Result := True;
  end;
end;3. 发送字符串信息
procedure ProcMsg(lpcszMsg: PChar);
begin
  if SevrConnect( m_dwSocket, '127.0.0.1', 8888 ) then
    WinSock.send( m_dwSocket, lpcszMsg[0], StrLen(lpcszMsg), 0 );
end;以上是客户端的处理过程, 创建时会启动并创建SOCKET, 发送时均会检测是否有连接, 然后发送字符串以下是非阻塞服务器的处理过程
// 1. 窗体创建时启动, 创建SOCKET 并监听
procedure TForm1.FormCreate(Sender: TObject);
var
  rSin: sockaddr_in;
  rWSAData: TWSADATA;
  wSockVer: Word;
begin
  m_nConnCnt := 0;
  wSockVer := MAKEWORD(2,0);
  if Winsock.WSAStartup( wSockVer, rWSAData ) <> 0 then
  begin
    Memo.Lines.Add( 'WSAStartUp Failed!' );
    Exit;
  end;
  // 要监听首先必须创建SOCKET
  m_dwSocket := Winsock.socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );            
  if m_dwSocket = INVALID_SOCKET then
  begin
    Memo.Lines.Add( 'project can''t create socket!' );
    Exit;
  end;  rSin.sin_family := AF_INET;
  rSin.sin_port := htons( 8888 );                                               
  rSin.sin_addr.S_addr := INADDR_ANY;
  if WinSock.bind( m_dwSocket, rSin, sizeof(rSin) ) = SOCKET_ERROR  then
  begin
    Memo.Lines.Add( 'project can''t bind Port: 8765' );
    WinSock.closesocket( m_dwSocket );
    m_dwSocket := INVALID_SOCKET;
    Exit;
  end;  if WinSock.WSAAsyncSelect( m_dwSocket, Handle, WM_SOCKET,
        FD_ACCEPT or FD_CLOSE or FD_READ ) <> 0 then
  begin
    Memo.Lines.Add( 'project can''t set WM_SOCKET message!' );
    WinSock.closesocket( m_dwSocket );
    m_dwSocket := INVALID_SOCKET;
    Exit;
  end;  if WinSock.listen( m_dwSocket, 5 ) = SOCKET_ERROR then
  begin
    Memo.Lines.Add('project listen fialed,' +
      'please release the limition of firewall or AntiVirus soft');
    WinSock.closesocket( m_dwSocket );
    m_dwSocket := WinSock.INVALID_SOCKET;
    Exit;
  end;
  Memo.Lines.Add( 'project listening' );
end;2. 在对应的消息处理过程
procedure TForm1.WMSOCKET(var Message: TMessage);
var
  dwSocket, dwConnectSock: TSocket;
  byBuf: array [0..4095] of Char;
  sTmp, sIP: String;
  nIndex: Integer;
  wPort: Word;
  SockAddr: sockaddr_in;
  pHost: PHostEnt;
begin
  dwSocket := Message.wParam;
  Message.Result := 0;
  if Winsock.WSAGETSELECTERROR(Message.lParam) <> 0 then                               
  begin
    Winsock.closesocket( dwSocket );
    Memo.Lines.Add( 'TCP Asy connect error' );
    Exit;
  end;  case Winsock.WSAGETSELECTEVENT(Message.lParam) of
    FD_ACCEPT:
      begin
        Memo.Lines.Add( 'connect to Main' );
        Inc(m_nConnCnt);
        dwConnectSock := Winsock.accept( dwSocket, nil, nil );
        case Winsock.WSAAsyncSelect( dwConnectSock, Handle,
                WM_D7SOCKET, FD_READ or FD_WRITE or FD_CLOSE ) of
          0:                                                                  
            begin
              nIndex := Sizeof(SockAddr);
              ZeroMemory( @SockAddr, nIndex );
              WinSock.getpeername( dwConnectSock, SockAddr, nIndex );
              wPort := WinSock.ntohs( SockAddr.sin_port );
              sIP := WinSock.inet_ntoa( SockAddr.sin_addr );
              nIndex := WinSock.inet_addr( PChar(sIP) );
          pHost := WinSock.gethostbyaddr( @nIndex, 4, AF_INET );
              if pHost = nil then
                sTmp := '未知计算机'
              else
                sTmp := pHost^.h_name;
              Memo.Lines.Add( sTmp + ' ' + sIP + ':' + IntToStr(wPort) + ' connected' );
              CheckListBoxClientList.Checked[CheckListBoxClientList.Items.AddObject(
                sTmp + ' ' + sIP + ':' + IntToStr(wPort), TObject(dwConnectSock) )] := True;
            end;
          else
            begin
              Memo.Lines.Add( 'Accept filed.' );
              Winsock.closesocket( dwConnectSock );
              Exit;
            end;
        end;
      end;
    FD_CLOSE:
      begin
        Winsock.closesocket( dwSocket );
        nIndex := CheckListBoxClientList.Items.IndexOfObject( TObject(dwSocket) );
        if nIndex <> -1 then
          CheckListBoxClientList.Items.Delete( nIndex );
        Dec(m_nConnCnt);
      end;
    FD_READ:
      begin
        Winsock.closesocket( dwSocket );
        nIndex := CheckListBoxClientList.Items.IndexOfObject( TObject(dwSocket) );
        if nIndex <> -1 then
          CheckListBoxClientList.Items.Delete( nIndex );
        Dec(m_nConnCnt);
  end;
  FD_READ:
  begin
        ZeroMemory( @byBuf[0], 4096 );
        WinSock.recv( dwSocket, byBuf[0], 4096, 0 );
        ShowMessage(String(@byBuf[0]));
      end;
  end;
end;窗体关闭的过程
procedure TForm1.FormClose(Sender: TObject;
  var Action: TCloseAction);
var
  i: Integer;
begin
  Action := caFree;
  for i := 0 to CheckListBoxClientList.Items.Count - 1 do
    Winsock.closesocket( TSOCKET(CheckListBoxClientList.Items.Objects[i]) );
  Winsock.closesocket( m_dwSocket );
end;出现的现象如下: 
1. 很明显, 我在服务器是用ShowMessage, 按照阻塞来说, 客户端应该的send是不能返回的, 但实际上是直接返回了..
2. 在客户端进行连接, 关闭的时候, 服务器是很正确能够释放客户端的Socket的, 但是要是客户端不关闭, 直接关闭服务器, 出现的情况是: 客户端的连接并不会断开, SevrConnect 函数会一直返回True, 也就是要么是连接得上, 要么就是还在连接中. 
3. 直接关闭服务器后, 再重开监听, 客户端只要不关闭, 就一直不会重新连接重启的服务器...这个是什么问题?
虽然看很多示例程序, 都是客户端和服务器均为阻塞, 要么均为非阻塞, 是不是由于我一边用阻塞, 一边用非阻塞, 就会有这种结果? 求高手解答....

解决方案 »

  1.   

    问题算是解决了...
    在send的后面加个recv...只要传送成功, 服务器均返回随便的一个东西让客户端recv, recv不到东西的时候, 就代表连接失效了, 然后就关闭socket再创建, 下次发送信息再连接..这样就正确了只是...为何明明服务器close客户端的连接socket, 怎么客户端的connect方法还是会返回正确的呢...有高手可以解答一下么...
      

  2.   


    你Close之后,再Connect,不是相当于再次连接服务器吗,当然会成功,服务器本身又没有关!(我没仔细看代码,只是猜测理解啊,呵呵)
      

  3.   

    connect会执行连接操作,建议直接用connected看下是否断开与服务器的连接。