![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Передача данных при помощи сокетов. [D6, Win95/98, WinME, NT4, Win2k, WinXP]
V_Pavel © (2002-11-01 12:27:00)Мне нужно организовать обмен данными между клиентом и сервером при помощи сокетов. Как и с помощью чего это лучше сделать чтоб работало надежно? Предполагаемый размер пакетов колеблется от 10 байт до 500 килобайт. Данные нужно передавать как с клиента на сервер так и обратно. Если у кого есть примеры или ссылки на примеры поделитесь буду рад. Я побывал это делать при помощи стандартных компанент работает но не устойчива, а именно если после большого пакета размером примерно 50 кб. передаю пакет размером 10 байт он кудато теряется.
V_Pavel (2002-11-05 11:48:00) [41]Нужно передать от сервера клиенту абстрактный поток данных (файл, текст, картинка неважно). В начале потока в первых 10 байтах передавать его длину. Клиент считывает длину и все последующие блоки склеивает в один пока длина получившегося блока не равна длине передаваемых данных. Когда клиент получил этот поток он решает что с ним делать.
Digitman (2002-11-05 08:25:00) [42]Приведи спецификацию своего собственного протокола инф.обмена, тогда и разговор будет
V_Pavel (2002-11-05 06:13:00) [43]Передатчик вроде работает нормально, но теперь придется переписать приемник. Digitman ©, не подскажешь как это правильнее сделать.
V_Pavel (2002-11-04 14:11:00) [44]Спасибо за разъяснения. Теперь все понятно. Вечером попробую запихнуть это все в свой проект. А там посмотрим что получиться.
Надеюсь будет работать как надо. Еще раз спасибо.
Digitman (2002-11-04 13:48:00) [45]
> Тут мы создаем поток а где мы его убиваем?
Хэлп-то читать будем когда-нибудь ?
Читай внимательно описание SendStream() :
Note: The Stream passed as a parameter to SendStream becomes “owned” by the windows socket object. The Windows socket object frees the stream when it is finished with it. Do not attempt to free the stream after it has been passed as a parameter.
Digitman (2002-11-04 13:46:00) [46]Вот так корректней будет.
//в момент необходимости передачи данных клиенту
var Stream: TMemoryStream; //лок.переменная
var Target: TCustomWinSocket; //лок.переменная
var CurStreamReadPos: Integer; //лок.переменная
...
Target := Socket.Connections[index]; // целевое гнездо
Stream := Target.Data;
if not Assigned(Stream) then
Stream := TMemoryStream.Create;
CurStreamReadPos := Stream.Position; //тек.позиция считывания из потока
Stream.Position := Stream.Size; //в конец потока
Stream.Write(...то что нужно передать...); //в хвост потока - новые данные
Stream.Position := CurStreamReadPos; //восстановление тек.позиции
if not Assigned(Target.Data) then // если очередь передачи пуста
begin
Socket.Data := Stream; // ставим поток в очередь
Процедура_обработчика_OnClientWrite(ServerSocket, Target); // инициируем передачу очереди
end
Теперь понятно ?
V_Pavel (2002-11-04 13:30:00) [47]>if not Assigned(Stream) then
> Stream := TMemoryStream.Create;
Тут мы создаем поток а где мы его убиваем?
V_Pavel (2002-11-04 13:20:00) [48]Может я чтото не понимаю но объясни мне это место
if not Assigned(Socket.Data) and not Socket.Connections[index].SendStream(Stream) then
Digitman (2002-11-04 12:32:00) [49]
> а если тут сделать так
> Stream := Socket.Connections[index].Data;
> я думаю будет правильнее или я ощибаюсь.
Да, так будет правильно.
> И мне не совсем понятен код
Что тебе непонятно ? Конкретно ?
V_Pavel (2002-11-04 12:23:00) [50]Спасибо за пример!
У меня по нему 2 вопроса.
1)
//в момент необходимости передачи данных клиенту
var Stream: TMemoryStream; //лок.переменная
var CurStreamReadPos: Integer; //лок.переменная
...
Stream := Socket.Data;
а если тут сделать так
Stream := Socket.Connections[index].Data;
я думаю будет правильнее или я ощибаюсь.
2)И мне не совсем понятен код
if not Assigned(Socket.Data) and not Socket.Connections[index].SendStream(Stream) then
begin
Socket.Data := Stream;
Процедура_обработчика_OnClientWrite(ServerSocket, Socket);
end
Digitman (2002-11-04 10:43:00) [51]и вот еще поправка :
//тело обработчика OnClientDisconnect()
if Assigned(Socket.Data) then
try
TMemoryStream(Socket.Data).Free;
except
end
Digitman (2002-11-04 10:36:00) [52]вернее - вот так :
//в момент необходимости передачи данных клиенту
var Stream: TMemoryStream; //лок.переменная
var CurStreamReadPos: Integer; //лок.переменная
...
Stream := Socket.Data;
if not Assigned(Stream) then
Stream := TMemoryStream.Create;
CurStreamReadPos := Stream.Position; //тек.позиция считывания из потока
Stream.Position := Stream. Size; //в конец потока
Stream.Write(...то что нужно передать...); //в хвост потока - новые данные
Stream.Position := CurStreamReadPos; //восстановление тек.позиции
if not Assigned(Socket.Data) and not Socket.Connections[index].SendStream(Stream) then
begin
Socket.Data := Stream;
Процедура_обработчика_OnClientWrite(ServerSocket, Socket);
end
Digitman (2002-11-04 10:29:00) [53]Я посоветую для начала внимательно почитать хэлп на метод TCustomWinSocket.SendText() и на событие TCustomWinSocket.OnWrite(). Результат вызова метода SendText() (как и прочих иных send-методов) говорит о многом, в зависимости от результата дальнейшие действия могут быть тесно связаны с необходимостью выполнения повторной передачи тех же данных в момент возникновения события OnWrite().
Imho, самый удобный, наглядный и наименее проблематичный способ передачи данных в асинхр.неблок.режиме - это передача с использованием поточного класса TStream.
Примерный вариант :
//тело обработчика OnClientConnect()
Socket.Data := nil;
//тело обработчика OnClientDisconnect()
if Assigned(Socket.Data) then
TMemoryStream(Socket.Data).Free;
//в момент необходимости передачи данных клиенту
var Stream: TMemoryStream; //лок.переменная
var CurStreamReadPos: Integer; //лок.переменная
...
Stream := Socket.Data;
if not Assigned(Stream) then
Stream := TMemoryStream.Create;
CurStreamReadPos := Stream.Position;
Stream.Write(...то что нужно передать...);
Stream.Position := CurStreamReadPos;
if not Assigned(Socket.Data) and not Socket.Connections[index].SendStream(Stream) then
begin
Socket.Data := Stream;
Процедура_обработчика_OnClientWrite(ServerSocket, Socket);
end
//тело обработчика OnClientWrite()
var Stream: TMemoryStream; //лок.переменная
...
Stream := TMemoryStream(Socket.Data);
if Assigned(Stream) and Socket.SendStream(Stream) then
Socket.Data := nil;
V_Pavel (2002-11-04 09:16:00) [54]Digitman © , что ты посоветуешь сделать. Я с сетями работаю не так давно и еще не сильно разбираюсь. Подскажи хоябы идею по обработке результата, возвращаемого ф-цией socket.Connections[ind].SendText(head+str) и что нужно написать в обработчике события OnClientWrite.
Digitman (2002-11-04 08:26:00) [55]>V_Pavel
Где анализ результата, возвращаемого ф-цией
socket.Connections[ind].SendText(head+str) ?
Где обработка события OnClientWrite ?
V_Pavel (2002-11-03 17:07:00) [56]Вот передатчик.
procedure sendpacket(str:string;sh:integer;Socket: TServerWinSocket);
var i,ind:integer;
head:string;
begin
if sh=0 then ind:=0 else
for i:=0 to socket.ActiveConnections-1 do
if socket.Connections[i].Handle=sh then ind:=i;
head:=padl(inttostr(length(str)+10),10,"0");
socket.Connections[ind].SendText(head+str);
end;
Если слать одинаковые пакеты то они не теряются а вот если слать сперва большой пакет а потом маленький то маленький не доходит. Я пробывал ставить задержку между посылками хотябы на 10 ms. тогда вроде рабтает.
Digitman (2002-11-01 13:48:00) [57]ПРиведи еще код передатчика
V_Pavel (2002-11-01 13:31:00) [58]да я согласен что мой код некорректен.
Вот посмотрите и скажите что не так. Данный код при приеме склеивает пакет из нескольких кусков в один и при наличии в начале первого куска информации о длине пакета и проверяет куски на наличие склеенных пакетов.
Если после передачи большого пакета сервером проходит немного времени перед передачей маленького пакета то все нормально а вот если сразу передается маленький пакет по он теряется в 3-5 случаях из 10.
procedure TFmain.ClientSocket1Read(Sender: TObject;
Socket: TCustomWinSocket);
var
count :Integer;
buffer: Array [0..MAX_BUF_SIZE] of Char;
p:tstringstream;
i,ln:integer;
str:string;
begin
i:=0;
p:=tstringstream.Create("");
Socket.Lock;
repeat
count:= Socket.ReceiveBuf(buffer,SizeOf(buffer));
if count > 0 then
begin
p.WriteBuffer(buffer,count);
if i=0 then ln:=strtoint(copy(p.DataString,1,10)));
if p.Size>=ln then
repeat
str:=p.DataString;
ln:=strtoint(copy(str,1,10));
decodePacket(copy(str,1,ln+10)); {обработчик пакета}
p.free;
delete(str,1,ln+10);
p:=tstringstream.Create(str);
i:=0;
until (p.Size<10)
else inc(i);
end;
Socket.Unlock;
until (count <= 0);
dePacket(p.DataString);
p.Free;
end;
Song (2002-11-01 13:03:00) [59]http://delphi.mastak.ru/cgi-bin/forum.pl?look=1&id=1036137108&n=4
Digitman (2002-11-01 12:55:00) [60]
> побывал это делать при помощи стандартных компанент работает
> но не устойчива
Некорректен именно твой код, а не внутренняя работа этих компонентов