2009-07-22 12 views
1

クライアント:10057 WSA例外際SendBufソケット経由

//is called when the client tries to log in 
    procedure TLogin_Form.btnLoginClick(Sender: TObject); 
    var LoginQuery: TQuery; 
    begin 
     //If socket not open, open it 
     if not LoginSocket.Active then 
     begin 
      LoginSocket.Open; 
     end; 

     //create package 
     LoginQuery.Login := ledtName.Text; 
     LoginQuery.Passwort := ledtPasswort.Text; 
     LoginQuery.IP := LoginSocket.Socket.LocalAddress; 
     //send package 
     LoginSocket.Socket.SendBuf(LoginQuery, SizeOf(LoginQuery)); 
    end; 

サーバー:

//This procedure is executed when I click on start server button 
    procedure TServer_Form.btnStartStopClick(Sender: TObject); 
    begin 
     //If not open, open it 
     if not ServerSocket.Active then 
     begin 
      btnStartStop.Caption := 'stop server'; 
      //Open ServerSocket 
      ServerSocket.Open; 
     end 
     else 
     begin 
     //If Socket open, close it, but watch for active connctions. 
     if ServerSocket.Socket.ActiveConnections > 0 then 
      begin 
      ShowMessage('Clients still logged in'); 
      end 
     else 
      begin 
      //If no clients connected, close socket 
      ServerSocket.Close; 
      end; 
     end; 
    end; 

    //This procedure is called to verify weather the user is logged in and to send the verification back 
    procedure UserCheckExist(Login, Passwort: string); 
    var LoginReply: TReply; 
    begin 
     begin 
      //Connect to DB 
      DBConnect(true); 
      DM.AQ_LOGIN.Close; 
      DM.AQ_LOGIN.SQL.Clear; 
      //Count of BLOGINs 
      DM.AQ_LOGIN.SQL.Add('select count(BLOGIN) from BENU where BLOGIN = ''' + Login + ''' AND BPW = ''' + Passwort + ''''); 
      DM.AQ_LOGIN.Open; 
      //LoginReply.Action tells the client then what to do with the LoginReply.Value 
      LoginReply.Action := 0; 
      //if user unique 
      if DM.AQ_LOGIN.Fields[0].AsInteger = 1 then 
      begin 
       //LoginReply.Value = 1 means the client is allowed to log in 
       LoginReply.Value := 1; 
       //THIS RETURNS THE WSA 10057 EXCEPTION of user is unique 
       Server_Form.ServerSocket.Socket.SendBuf(LoginReply, SizeOf(LoginReply)); 
      end 
      else 
      begin 
       //LoginReply.Value = 0 means the client is NOT allowed to log in 
       LoginReply.Value := 0; 

       //THIS RETURNS THE WSA 10057 EXCEPTION if user is NOT unique 
       Server_Form.ServerSocket.Socket.SendBuf(LoginReply, SizeOf(LoginReply)); 
      end; 
      //Close ADOQuery 
      DM.AQ_LOGIN.Close; 
      //Close DB Connection 
      DBConnect(false); 
     end; 
    end; 

    //Is called when something is in the socket connection 
    procedure TServer_Form.ServerSocketClientRead(Sender: TObject; 
     Socket: TCustomWinSocket); 
    var Query: TQuery; 
    begin 
     //Reads from the Socket (cant use ServerSocket.Socket.ReceiveBuf whysoever, but this is another thread) 
     Socket.ReceiveBuf(Query, SizeOf(Query)); 
     case Query.Action of 
     //If Query.Action = 0, which means the client tries to login call UserCheckExist 
     0: UserCheckExist(Query.Login, Query.Passwort); 
     //Otherwise, getfuckedup 
     else ShowMessage('Query Action not defined'); 
     end; 
    end; 

一つ奇妙なことは、私は、クライアントからの2回のログイン+ PWを送信しなければならないことです。

最初にsend(Client)を送信すると、サーバーでonClientConnectとonAcceptを取得します。 2回目に(クライアント)を送信すると、サーバはマークされた行までコードを実行します。私は10057 WSAの例外を取得します。

なぜこのエラーが発生しますか?しかし、奇妙なことに、もし私が「ソケットが開いていない」という例外を受け取った行の直前にサーバー上のソケットを開くと、私はそれをとにかく取得します。

答えて

5

あなたが示したコードは、クライアント側とサーバー側には、コード内のいくつかのバグがあります。

TClientSocketがctNonBlockingモード(使用していると仮定している)に設定されている場合、btnLoginClick()が終了してフローがメッセージキューに戻されるまで、Open()はOnConnectイベントをトリガーしません。 OnConnectイベントが発生するまで、ソケットからデータを読み書きすることは有効ではありません。したがって、送信コードをOnConnectイベント自体に移動する必要があります。また、SendBuf()がすべてのデータを単一のパケットで送信できないことも考慮する必要があります。 SendBuf()が-1を返し、WSAGetLastError()が後でWSAEWOULDBLOCKを返した場合(OnErrorイベントがトリガーされていない場合は常にtrue)、データは完全には送信されませんでした。送信されていないバイトをどこかにバッファリングしてから、OnWriteイベントが発生するのを待ってから、バッファされたバイトをもう一度書き込むか、それ以外のものをソケットに書き込もうとします。

サーバーコードでは、間違ったオブジェクトに送信データを書き込もうとしています。 OnReadイベントが提供するTCustomWinSocketオブジェクトを使用してデータを読み書きする必要があります。代わりに、サーバーのTServerWinSocketオブジェクトにデータを書き込もうとしています。これは、接続されたクライアントの有効なソケットエンドポイントを表していません。また、部分送信も処理するためにReceiveBuf()の戻り値を調べる必要があります。

は、より多くの次のようなものを試してみてください:

共通:

type 
    // helper class that holds buffered input/output data 
    SocketBuffers = class 
    public 
    constructor Create; 
    destructor Destroy; 
    Inbound: TMemoryStream; 
    Outbound: TMemoryStream; 
    end; 

constructor SocketBuffers.Create; 
begin 
    inherited; 
    Inbound := TMemoryStream.Create; 
    Outbound := TMemoryStream.Create; 
end; 

destructor SocketBuffers.Destroy; 
begin 
    Inbound.Free; 
    Outbound.Free; 
    inherited; 
end; 

// removes processed bytes from a buffer 
procedure CompactBuffer(Buffer: TMemoryStream); 
begin 
    if Buffer.Position > 0 then 
    begin 
    // bytes have been processed, remove them from the buffer... 
    if Buffer.Position < Buffer.Size then 
    begin 
     // move unprocessed bytes to the front of the buffer... 
     Move(Pointer(Longint(Buffer.Memory)+Buffer.Position)^, Buffer.Memory^, Buffer.Size - Buffer.Position); 
     // reduce the buffer size just the remaining bytes... 
     Buffer.Size := Buffer.Size - Buffer.Position; 
    end else 
    begin 
     // all bytes have been processed, clear the buffer... 
     Buffer.Clear; 
    end; 
    end; 
end; 

// sends raw bytes to the specified socket, buffering any unsent bytes 
function SendDataToSocket(Socket: TCustomWinSocket; Data: Pointer; DataSize: Integer; Buffer: TMemoryStream): Integer; 
var 
    DataPtr: PByte; 
    NumSent: Integer; 
begin 
    Result := 0; 
    DataPtr := PByte(Data); 
    if DataSize > 0 then 
    begin 
    if Buffer.Size = 0 then 
    begin 
     // the buffer is empty, send as many bytes as possible... 
     repeat 
     NumSent := Socket.SendBuf(DataPtr^, DataSize); 
     if NumSent <= 0 then Break; // error or disconnected 
     Inc(DataPtr, NumSent); 
     Dec(DataSize, NumSent); 
     Inc(Result, NumSent); 
     until DataSize = 0; 
     if DataSize = 0 then Exit; // nothing left to send or buffer 
    end; 
    // add unsent bytes to the end of the buffer... 
    Buffer.Seek(0, soFromEnd); 
    Buffer.WriteBuf(DataPtr^, DataSize); 
    Inc(Result, DataSize); 
    end; 
end; 

// sends buffered bytes to the specified socket 
procedure SendBufferToSocket(Socket: TCustomWinSocket; Buffer: TMemoryStream); 
var 
    DataPtr: PByte; 
    NumSent: Integer; 
begin 
    // start at the beginning of the buffer 
    Buffer.Position := 0; 
    DataPtr := PByte(Buffer.Memory); 
    while Buffer.Position < Buffer.Size do 
    begin 
    NumSent := Socket.SendBuf(DataPtr^, Buffer.Size - Buffer.Position); 
    if NumSent <= 0 then Break; // error or disconnected 
    Inc(DataPtr, NumSent); 
    Buffer.Seek(NumSent, soFromCurrent); 
    end; 
    // remove bytes that were sent... 
    CompactBuffer(Buffer); 
end; 

// reads raw bytes from the specified socket ands buffers them 
procedure ReadBufferFromSocket(Socket: TCustomWinSocket; Buffer: TMemoryStream); 
var 
    NumRecv: Integer; 
    OldSize: Integer; 
begin 
    repeat 
    NumRecv := Socket.ReceiveLength; 
    if NumRecv <= 0 then Exit; // error or no data available 

    // increase the size of the buffer 
    OldSize := Buffer.Size; 
    Buffer.Size := Buffer.Size + NumRecv; 

    // read bytes into the new memory space 
    NumRecv := Socket.ReceiveBuf(Pointer(Longint(Buffer.Memory)+OldSize)^, NumRecv); 
    if NumRecv <= 0 then 
    begin 
     // nothing read, free the unused memory 
     Buffer.Size := OldSize; 
     Exit; 
    end; 
    until False; 
end; 

クライアント:

var 
    Buffers: SocketBuffers = nil; 

procedure TLogin_Form.FormCreate(Sender: TObject); 
begin 
    Buffers := SocketBuffers.Create; 
end; 

procedure TLogin_Form.FormDestroy(Sender: TObject); 
begin 
    LoginSocket.Close; 
    Buffers.Free; 
end; 

procedure TLogin_Form.btnLoginClick(Sender: TObject); 
begin 
    if not LoginSocket.Active then 
    begin 
    Buffers.Inbound.Clear; 
    Buffers.Outbound.Clear; 
    LoginSocket.Open; 
    end; 
end; 

procedure TLogin_Form.LoginSocketConnect(Sender: TObject; Socket: TCustomWinSocket); 
var 
    LoginQuery: TQuery; 
begin 
    LoginQuery.Login := ledtName.Text; 
    LoginQuery.Passwort := ledtPasswort.Text; 
    LoginQuery.IP := LoginSocket.Socket.LocalAddress; 

    // send query, buffering unsent bytes if needed... 
    SendDataToSocket(Socket, @LoginQuery, SizeOf(LoginQuery), Buffers.Outbound); 
end; 

procedure TLogin_Form.LoginSocketRead(Sender: TObject; Socket: TCustomWinSocket); 
var 
    Buffer: TmemoryStream; 
    Available: Integer; 
    Query: TQuery; 
begin 
    Buffer := Buffers.Inbound; 

    // read available bytes into the buffer... 
    ReadBufferFromSocket(Socket, Buffer); 

    // process complete queries, ignore unfinished queries until later... 
    Buffer.Position := 0; 
    repeat 
    Available := Buffer.Size - Buffer.Position; 
    if Available < SizeOf(Query) then Break; 
    Buffer.ReadBuf(Query, SizeOf(Query)); 
    // process query as needed ... 
    until False; 

    // remove processed bytes from the buffer... 
    CompactBuffer(Buffer); 
end; 

procedure TLogin_Form.LoginSocketWrite(Sender: TObject; Socket: TCustomWinSocket); 
begin 
    // can send any buffered bytes now... 
    SendBufferToSocket(Socket, Buffers.Outbound); 
end; 

サーバー:

procedure TServer_Form.btnStartStopClick(Sender: TObject); 
begin 
    if not ServerSocket.Active then 
    begin 
    btnStartStop.Caption := 'stop server'; 
    ServerSocket.Open; 
    end 
    else if ServerSocket.Socket.ActiveConnections > 0 then 
    begin 
    ShowMessage('Clients still logged in'); 
    end 
    else 
    begin 
    ServerSocket.Close; 
    end; 
end; 

procedure UserCheckExist(Socket: TCustomWinSocket; Login, Password: string); 
var 
    LoginReply: TReply; 
begin 
    ... 
    LoginReply.Value := ...; 

    // send query, buffering unsent bytes if needed... 
    SendDataToSocket(Socket, @LoginReply, Sizeof(LoginReply), SocketBuffers(Socket.Data).Outbound); 
    ... 
end; 


procedure TServer_Form.ServerSocketClientConnect(Sender: TObject; Socket: TCustomWinSocket); 
begin 
    Socket.Data := SocketBuffers.Create; 
end; 

procedure TServer_Form.ServerSocketClientDisconnect(Sender: TObject; Socket: TCustomWinSocket); 
begin 
    SocketBuffers(Socket.Data).Free; 
    Socket.Data := nil; 
end; 

procedure TServer_Form.ServerSocketClientRead(Sender: TObject; Socket: TCustomWinSocket); 
var 
    Buffer: TmemoryStream; 
    Available: Integer; 
    Query: TQuery; 
begin 
    Buffer := SocketBuffers(Socket.Data).Inbound; 

    // read available bytes into the buffer... 
    ReadBufferFromSocket(Socket, Buffer); 

    // process complete queries, ignore unfinished queries until later... 
    Buffer.Position := 0; 
    repeat 
    Available := Buffer.Size - Buffer.Position; 
    if Available < SizeOf(Query) then Break; 
    Buffer.ReadBuf(Query, SizeOf(Query)); 
    // process query as needed ... 
    case Query.Action of 
     0: UserCheckExist(Socket, Query.Login, Query.Password); 
     ... 
    end; 
    until False; 

    // remove processed bytes from the buffer... 
    CompactBuffer(Buffer); 
end; 

procedure TServer_Form.ServerSocketClientWrite(Sender: TObject; Socket: TCustomWinSocket); 
begin 
    // can send any buffered bytes now... 
    SendBufferToSocket(Socket, SocketBuffers(Socket.Data).Outbound); 
end; 
+0

あなたには、いくつかのコメントを挿入できますか?特に共通部分は? – Acron

+0

コメントを追加しました。 –

+0

LoginReplyは実際にはレコードです。私がレコードで宣言すれば、そのメッセージ:文字列;その文字列をファイル(10MB)のデータで埋めて送信しようとすると、サーバー上でアクセス違反が発生します。 – Acron

関連する問題