Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2003.06.26;
Скачать: CL | DM;

Вниз

WinSocket. Как узнать что послано текст или данные ?   Найти похожие ветки 

 
SergeyI ©   (2003-04-23 11:50) [0]

Как узнать что послано текст или данные ?
те какой функцией получать данные Socket.ReceiveBuf или
socket.ReceiveText


 
Digitman ©   (2003-04-23 12:05) [1]


> Как узнать что послано текст или данные


а протокол информационного обмена для какой цели, по твоему, разрабатывается для каждого конкретного отдельно взятого сервиса ? будь то HTTP, FTP, SMTP, POP3, ICQ или что-то еще ?

именно в соответствии с конкретным протоколом информационного обмена каждая из обменивающихся сторон имеет возможность знать, приема какого типа данных следует ожидать от партнера в каждый конкретный момент времени при конкреных условиях


> какой функцией получать данные Socket.ReceiveBuf или
> socket.ReceiveText


Любой. ReceiveText внутри себя вызывает ReceiveBuf. Разницы, по сути - никакой. Так что лучше всего вообще не пользовать ReceiveText, а вызывать сразу ReceiveBuf. Единственное отличие - ReceiveText самостоятельно организует локальный буфер приема в виде строки, размер которой устанавливается по фактически прочитанным данным. При этом фактически записанные в буфер строки данные никак не проверяются на тот признак, текст это или что-то еще.


 
SergeyI ©   (2003-04-23 12:22) [2]

Спасибо за ответ а можно получить совет на тему "как лучше сделать" задача такая клиент может посылать запросы-команды (я думал сделать их строками) и данные, на сервере надо отличать первое от второго


 
Digitman ©   (2003-04-23 12:35) [3]

клиент, например, может послать запрос-команду фикс.размера (скажем, dword), следом - размер сопровождающих команду данных (скажем, тоже dword), следом - собственно данные

сервер в этом случае знает, что, приняв первый dword он получает код команды, следующий dword - размер возможно сопровождающих команду данных, далее (имея уже размер данных, если он - ненулевой) считывает из гнезда ровно столько данных, сколько заявил клиент во 2-м (по очереди считывания) dword"е.


 
SergeyI ©   (2003-04-23 12:53) [4]

идея понятна только зачем передавать размер ? если имхо его можно получить Socket.ReceiveLength - sizeof(dword) или нельзя ?


 
Digitman ©   (2003-04-23 13:11) [5]


> если имхо его можно получить Socket.ReceiveLength


а что по твоему означает ReceiveLength ? каков его фактический смысл в твоем понимании ?


 
Akni   (2003-04-23 13:34) [6]

> клиент, например, может послать запрос-команду фикс.размера (скажем, dword), следом - размер сопровождающих команду данных (скажем, тоже dword), следом - собственно данные

именно так я и пытаюсь сделать:

type ISRecDat=record
...
end;


на сервере

var GlobalDat: ISRecDat;
procedure TISServerThread.SendData(var Msg: TMessage);
var iCode: integer;
begin
if fServerSocket.Socket.ActiveConnections>0 then
begin
iCode:=cdRec;
fServerSocket.Socket.Connections[0].SendBuf(iCode, sizeOf(iCode));
fServerSocket.Socket.Connections[0].SendBuf(GlobalDat, SizeOF(ISRecDat));
end;
end;


на клиенте

var GlobalDat1: ISRecDat;
iRecvdCode: integer;

procedure TISClientThread.SocketRead(Sender: TObject;
Socket: TCustomWinSocket);
var iRecvdLen: Integer;
iRecvdSize: integer;
iCode: integer;
begin
iRecvdLen := Socket.ReceiveLength;
if iRecvdLen>0 then
if iRecvdCode=cdNONE then //получить код
begin
Socket.ReceiveBuf(iRecvdCode, SizeOf(iRecvdCode));
end;

case iRecvdCode of
cdREC:
begin
Socket.ReceiveBuf(GlobalDat1, SizeOf(ISRecDat)); //получить данные
iRecvdCode:=cdNONE; //занулить код
end;
end;
end;


Но данные не приходят одним блоком того же размера, как были посланы.
Является ли хорошим решением в данном случае использовать поток (TMemoryStream) и писать в него данные, пока этих данных не наберется опред. кол-во (напр. SizeOf(ISRecDat) или SizeOf(Integer)) а потом уже читать из потока в соответствующие переменные?


 
Digitman ©   (2003-04-23 13:48) [7]


> Akni



> Но данные не приходят одним блоком того же размера, как
> были посланы


разумеется ! протокол-то - поточный)


> Является ли хорошим решением в данном случае использовать
> поток (TMemoryStream) и писать в него данные, пока этих
> данных не наберется опред. кол-во (напр. SizeOf(ISRecDat)
> или SizeOf(Integer)) а потом уже читать из потока в соответствующие
> переменные?


как тебе будет удобней и проще реализовать прием и анализ принятого на соответствие протоколу инф.обмена (с пом. стрима или иным способом) - в конечном счете решать тебе : никаких ограничений нет
но, в принципе, использование мемстрима для этой цели есть не самое худшее решение.
здесь гораздо важней другое - реализовать нечто вроде "машины логического состояния транспорта" : что в данный момент ожидается/считывается из буфера (статус = 1.команда/2.размер/3.данные), сколько еще осталось принять данных для переключения в следующий по логике инф.обмена статус и т.д.


 
Akni   (2003-04-23 14:16) [8]

>(с пом. стрима или иным способом)

а какие еще есть другие способы? у меня, честно говоря мемстрим - единственная идея пока

> реализовать нечто вроде "машины логического состояния транспорта"
это понятно

еще один вопрос - можно ли анализировать мемстрим прямо в OnRead? Не может ли произойти так, что пока я разбираюсь со стримом, пришли новые данные, а сокет не готов их принять, т.к. он все еще находится в обработчике OnRead и занят предыдущей порцией данных? Если да, то как с этим бороться?



 
Polevi ©   (2003-04-23 14:27) [9]

OnRead должен работать быстро


 
Digitman ©   (2003-04-23 14:32) [10]

совершенно верно Polevi говорит : обработчик OnRead() должен как можно быстрей быть выполненным.

Да и не только OnRead() ! Любой обработчик события должен выполняться насколько это возможно быстро. При этом совершенно неважно, к каком код.потоке он вызывается


 
Akni   (2003-04-23 16:17) [11]

А можно поконкретнее, что значит "быстро"? Я сама понимаю, что считать в обработчике простые числа, наверное, не стоит :)

Но хотелось бы узнать ваше мнение, является ли приемлимым след. код.

procedure TISClientThread.SocketRead(Sender: TObject;
Socket: TCustomWinSocket);
var iRecvdLen: Integer;
iRecvdSize: integer;
iAllSize: integer;
Buffer: Pointer;
Dat: ISRecDat;
begin
iAllSize:=0;
iRecvdLen := Socket.ReceiveLength;
if iRecvdLen>0 then
repeat
if iRecvdCode=cdNONE then
begin
GetMem(Buffer, SizeOf(iRecvdCode));
try
iRecvdSize:=Socket.ReceiveBuf(Buffer^, SizeOf(iRecvdCode));
iAllSize:=iAllSize+iRecvdSize;
fMemStream.Write(Buffer^,iRecvdSize);

if fMemStream.Size=SizeOf(iRecvdCode) then
begin
fMemStream.Seek(0,soFromBeginning);
fMemStream.ReadBuffer(iRecvdCode, SizeOf(iRecvdCode));
fMemStream.Clear;
end;
finally
FreeMem(Buffer);
end;
end;

case iRecvdCode of
cdREC:
begin
GetMem(Buffer, SizeOf(ISRecDat));
try
iRecvdSize:=Socket.ReceiveBuf(Buffer^, SizeOf(ISRecDat));
iAllSize:=iAllSize+iRecvdSize;
fMemStream.Write(Buffer^,iRecvdSize);

if fMemStream.Size=SizeOf(ISRecDat) then
begin
fMemStream.Seek(0,soFromBeginning);
fMemStream.ReadBuffer(Dat, SizeOf(ISRecDat));
PostMessage(fFrm.Handle,wm_RefreshClients,cdREC,0);
iRecvdCode:=cdNONE;
fMemStream.Clear;
end;
finally
FreeMem(Buffer);
end;
...
end;
end;
until (iAllSize=iRecvdLen) or (iRecvdSize<=0);

end;


На двух машинах в одной подсети работает; не хотелось бы, чтобы возникли проблемы при переносе в большую систему.


 
Digitman ©   (2003-04-23 16:41) [12]

вот такая транспортная логика работает у меня, и - вполне успешно в уловиях глоб.сети :


procedure TSocketTransport.ReadEventHandler(Sender: TObject; Socket: TCustomWinSocket);
var
FRecvBuf: PByteArray;
FRcvBufSize: Integer;
NumOfBytesRead: Integer;
begin
FRcvBufSize := Socket.ReceiveLength;
if FRcvBufSize = 0 then Exit;
GetMem(FRecvBuf, FRcvBufSize);
try
try
NumOfBytesRead := Socket.ReceiveBuf(FRecvBuf, Min(FExpectedBytesToRead, FRcvBufSize));
while NumOfBytesRead > 0 do
begin
Dec(FExpectedBytesToRead, NumOfBytesRead);
case FRecvState of
rsSignature: //статус - прием сигнатуры
if FExpectedBytesToRead = 0 then
begin
if (PInteger(FRecvBuf)^ and CallSig <> CallSig) and
(PInteger(FRecvBuf)^ and ResultSig <> ResultSig) then
raise Exception.CreateRes(@SInvalidDataPacket);
FIncomingDataBlock := TDataBlock.Create as IDataBlock;
FIncomingDataBlock.Signature := PInteger(FRecvBuf)^;
FExpectedBytesToRead := SizeOf(DWord);
FRecvState := rsContext;
end;
rsContext: //статус - прием идент-ра тек.контекста
if FExpectedBytesToRead = 0 then
begin
FIncomingDataBlock.Context := PInteger(FRecvBuf)^;
FExpectedBytesToRead := SizeOf(DWord);
FRecvState := rsSize;
end;
rsSize: //


 
Polevi ©   (2003-04-23 17:12) [13]

2Digitman © (23.04.03 16:41)
FRcvBufSize := Socket.ReceiveLength;
я тоже так делаю, однако Э.Джонс в "Программирование в сетях Microsoft Windows" этого не рекомендует - мотивирует тем что лишний раз приходится пробегать по очереди пакетов в буфере winsock, с его точки зрения правильнее запросить столько данных, сколько требуется и анализировать их прочитанное колво


 
Digitman ©   (2003-04-24 08:35) [14]


> Polevi


трассировкой мне удалось локализовать вызов в недрах ReceiveLength() ф-ции ядра ZwDeviceIoControlFile() (та же самая ф-ция вызывается и в любом из recv-методов, но с иними параметрами)... собственно эта ф-ция в дан.случае и возвращает размер потенциально доступных данных... ф-ция входит в шлюз Int2E и далее ее логика выполняется в 0-м кольце привелегий, так что пока сказать трудно по поводу сканирования...



Страницы: 1 вся ветка

Текущий архив: 2003.06.26;
Скачать: CL | DM;

Наверх




Память: 0.51 MB
Время: 0.019 c
14-84671
Merfi
2003-06-08 15:11
2003.06.26
Глюк WinXp


1-84200
AlexSt
2003-06-12 15:23
2003.06.26
Перемешать элементы массива


14-84724
ДиМАньяк
2003-06-05 16:19
2003.06.26
Win API


14-84708
Merfi
2003-06-05 06:23
2003.06.26
Выключение компа(программно)


9-83835
ko@ndrew
2002-05-16 16:18
2003.06.26
DirectPlay8