표시된 코드는 두 가지 모두에서 작동하지 않습니다. 클라이언트 및 서버 측면에서 귀하의 코드에 여러 가지 버그가 있습니다.
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;
당신이 어떤 의견을 삽입 할 수 있을까요? 특히 공통 부분에? – Acron
코멘트를 추가했습니다. –
LoginReply는 실제로 레코드입니다. 내가 그 기록에 선포한다면 그 메시지 : 문자열; 해당 문자열을 파일 (10MB)의 데이터로 채우고 보내려고하면 서버에서 액세스 위반이 발생합니다. – Acron