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

Вниз

Не понимаю - почему ошибка при копировании через ClientSocket?:(   Найти похожие ветки 

 
TankMan ©   (2005-03-23 12:15) [0]

Посмотрел в FAQ про передачу файлов через client-server Socket вот что получилось:

{Процедура на ClientSocket}
var sFile:TFileStream;
begin
sFile:=TFileStream.Create(eFileName.text,fmOpenReadWrite);
csClient.Socket.SendText("@-"+Edit2.text+"-@#-"+inttostr(sFile.Size)+"-#");
csClient.Socket.SendStream(sFile);
sleep(100);
sFile.Free;
csClient.Close;

{Прием на ServerSocket}

procedure TForm1.ssServerClientRead(Sender: TObject;
 Socket: TCustomWinSocket);
var command,param:string;
l: DWORD;
buf: PChar;
src: TFileStream;
begin
if ReceiveFile then
begin
 l := Socket.ReceiveLength;
 GetMem(buf,l+1);
 Socket.ReceiveBuf(buf,l);
 if not fileExists(ExtractFilePath(ParamStr(0))+"vot.upd") then
 src:=TFileStream.Create(ExtractFilePath(ParamStr(0))+"vot.upd",fmCreate)
 else src:=TFileStream.Create(ExtractFilePath(ParamStr(0))+"vot.upd",fmOpenReadWrite);
 src.Seek(0,soFromEnd);
 src.WriteBuffer(buf,l);
 FreeMem(buf);
 Application.ProcessMessages;
 src.Free;
 LenghtofFile:=LenghtofFile-l;
 if LenghtofFile=0 then begin ReceiveFile:=false;ShowMessage("File is received");end;
 exit;
end;  //   }
if ExtractParamCommand(Socket.ReceiveText,command,param) then
Memo1.Lines.Add(Socket.RemoteAddress+":"+Command+" - "+Param)
else Memo1.Lines.Add(Socket.RemoteAddress+":"+Param);
if command="update" then begin ReceiveFile:=true; LenghtofFile:=strtoint(Param);end; //}


Вобщем суть такова - клиент в начале посылает строку с коммандой и параметром а сервер приняв комманду, узнает не update-ли это, и если так то (как я задумал :) ) начинает прием файла...
Но тут то и проблема, почему-то выбрав файл в клиенте , я нажав отправить на строке
csClient.Socket.SendStream(sFile);
Выдается ошибка stack overflow, или так же выдавалась ошибка EPrivilege :(, я бы понял, если бы я был не под администратором, но почемуж он выдает ошибку :( не понимаю :(
Ах да забыл сказать, для удобства и клиент и сервер находится на одном компе на клиенте стоит Address:="127.0.0.1", соединяется нормально...
Может кто знает более надежный/удобный способ/алгоритм передачи через socket?


 
TankMan ©   (2005-03-23 12:17) [1]

И еще забыл сказать, что на строке
FreeMem(buf);
В процедуре приема данных почему-то выдается ошибка access violation, хотя GetMem выдает положительный результат :(


 
Digitman ©   (2005-03-23 12:59) [2]

куча грубых ошибок ... и в коде сервера и в коде клиента ..

по поводу клиента - начни с внимательного чтения замечаний в станд.справке к методу SendStream


 
TankMan ©   (2005-03-23 13:26) [3]

>>Digitman
Мдаа, вот потом пологайся на статьи на солидных порталах...
http://delphi.mastak.ru/articles/socksrv/index.html
И всетаки ошибки остались, которые я не могу исправить :(


 
Digitman ©   (2005-03-23 14:28) [4]


> TankMan ©   (23.03.05 13:26) [3]


бездумно сдирать код я тебе не советую.
ВНЕ зависимости откуда ты его содрал - хоть с "солидного портала" хоть с убогого ..

статейный код, где бы и каким бы он ни был, приводится не для бездумного сдирания, а для детального анализа и адаптации к КОНКРЕТНОЙ ситуации и требованиям.


> И всетаки ошибки остались, которые я не могу исправить


для начала приведи фрагмент, где ты исправил уже упомянутые мной "ошибки"


 
TankMan ©   (2005-03-24 07:30) [5]

ХА!! :)
Я тут почитал архивы :), я смотрю - я далеко не первый кто воспользовался этой статьей, и я далкео не первый кому ты пытался указать на ошибки конкретно не тыкая носом :),
но статью удалили совсем недавно, кажется :)...

А исправил я вот что - во первых в клиенте убрал sFile.Free;и csClient.Close;
А в сервере

 i:=Socket.ReceiveBuf(buf^,l);
 if not fileExists(ExtractFilePath(ParamStr(0))+"vot.upd") then
 src:=TFileStream.Create(ExtractFilePath(ParamStr(0))+"vot.upd",fmCreate)
 else src:=TFileStream.Create(ExtractFilePath(ParamStr(0))+"vot.upd",fmOpenReadWrite);
 src.Seek(0,soFromEnd);
 src.WriteBuffer(buf^,i);
 FreeMem(buf);
 src.Free;
 LenghtofFile:=LenghtofFile-i;
 if LenghtofFile=0 then begin ReceiveFile:=false;ShowMessage("File is received");end
 else Memo1.Lines.Add("Осталось :"+inttostr(LenghtofFile));

Но тем неменее у меня ошибка...
Файл - плейлист (для примера), который я пересылаю, занимает 65кб а пересылается почему-то 57-59кб, т.е. я так понимаю - последний пакет не доходит... а почему :(


 
Digitman ©   (2005-03-24 08:14) [6]


> Но тем неменее у меня ошибка


какая ? на какой строчке ?


 
TankMan ©   (2005-03-25 07:12) [7]

Тут получается вообще не конкретная ошибка, а общая...
Файл посылается - клиент не выдает никаких ошибок, сервер тоже ничего не выдает, но файл отосланный клиентом становиться короче на 7кб...а примерно столько я понимаю и "весит" один пакет, значит он не доходит, вот не могу понять почему?

З.Ы.
Но у меня вопрос, а почему нужно ставить "^"? Я так понимаю это передача не переменной, а указателя, так? но в описании ReceiveBuf ни слова про указатели я не встретил...может это само собой подразумевалось? А я и не знаю :(


 
Digitman ©   (2005-03-25 08:17) [8]


> не конкретная ошибка, а общая


приводи полный код клиента и сервера с учетом уже сделанных тобой коррекций


> почему нужно ставить "^"?


формальный параметр, задающий буфер, в ф-ции SendBuf объявлен как

var Buf

это означает, что фактический параметр м.б. нетипизированным и передается не по значению, а по ссылке

т.о., если у тебя факт.параметр объявлен как

buf: PChar

в ф-цию будет передан адрес переменной buf, а не ее содержимое (в ней хранится указатель на собственно буфер)

поэтому и требуется т.н. "разыменование переменной" - увидев галку компилятор передаст в ф-цию в качестве ссылки именно то, что лежит в указательной переменной, а не адрес самой этой переменной ... что собссно и требуется ...


 
TankMan ©   (2005-03-25 09:52) [9]

Вот клиента кусок :)

procedure TForm1.Button2Click(Sender: TObject);
var sFile:TFileStream;
begin
sFile:=TFileStream.Create(edit3.text,fmOpenReadWrite);
csClient.Socket.SendText("@-"+Edit2.text+"-@#-"+inttostr(sFile.Size)+"-#");
csClient.Socket.SendStream(sFile);
end;


А вот сервера кусок:

procedure TForm1.ssServerClientRead(Sender: TObject;
 Socket: TCustomWinSocket);
var command,param:string;
l: DWORD;
i:integer;
buf: PChar;
src: TFileStream;
begin
if ReceiveFile then
begin
 l := Socket.ReceiveLength;
 GetMem(buf,l+1);
 i:=Socket.ReceiveBuf(buf^,l);
 if not fileExists(ExtractFilePath(ParamStr(0))+"vot.upd") then
 src:=TFileStream.Create(ExtractFilePath(ParamStr(0))+"vot.upd",fmCreate)
 else src:=TFileStream.Create(ExtractFilePath(ParamStr(0))+"vot.upd",fmOpenReadWrite);
 src.Seek(0,soFromEnd);
 src.WriteBuffer(buf^,i);
 FreeMem(buf);
 src.Free;
 LenghtofFile:=LenghtofFile-i;
 if LenghtofFile=0 then begin ReceiveFile:=false;ShowMessage("File is received");end
 else Memo1.Lines.Add("Îñòàëîñü :"+inttostr(LenghtofFile));
 exit;
end;
if ExtractParamCommand(Socket.ReceiveText,command,param) then
Memo1.Lines.Add(Socket.RemoteAddress+":"+Command+" - "+Param);
if command="update" then begin ReceiveFile:=true; LenghtofFile:=strtoint(Param);exit;end; //}
end;


ХМ... сейчас еще раз попробовал запустить и проверить - выдал - что все нормально переписалось, я уж обрадовался, попробовал еще раз - и опять - не дописывает последние 4-7 кб в не зависимости от размера файла (качал от 65кб до 1100кб)... не понимаю...


 
Digitman ©   (2005-03-25 10:26) [10]

вот с чего ты взял, что строка, переданная вызовом SendText(), придет целиком, и ты получишь ее вызовом ReceiveText() именно в том виде, в каком отправлял ?

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

чтобы передать строку, нужно передать сначала ее длину (например, integer-значение, т.н. ПРЕФИКС), а затем собственно строковые данные .. принимающая же сторона читает сначала длину (те самые 4 байта, что составляют integer-значение), выделяет память под строку, а затем за одно или более событий OnRead читает собственно поступающие последовательные фрагменты строковых данных, аккумулируя их и ведя подсчет оставшихся (еще не полученных) стр.данных ... как только сч-к принятых стр.данных будет равен ПРЕФИКСУ, можно считать что строка принята целиком, ни больше ни меньше, и только тогда анализировать содержимое строки и переключаться если нужно на прием передаваемых следом франментов стрима, точно так же аккумулируя их и ведя сч-к принятых байт


 
Slym ©   (2005-03-25 11:12) [11]

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

еслиб делал я то:
1. Делал сервер в блокирующем режиме... (так гораздо удобнее и надежней)
2. отправлял заголовок с маркером конца (например #0, или #13#10)
"@-FileName-@#-12563-#"+#0
потом читал заголовок пока не науду маркер конца...
3. Апосле читаю переданые данные.


 
TankMan ©   (2005-03-25 12:04) [12]

А не могли бы вы привести пример работы в бокирующем режиме?


 
Slym ©   (2005-03-25 12:19) [13]

примерно так
Сервер

unit SocketThread;

interface
uses Windows,ScktComp,Classes;

const LogMessageStr="%s: raised exception %s with message %s";

type
 TSocketThread=class(TServerClientThread)
 private
   ln:string;
   procedure Work;
 protected
   procedure ClientExecute; override;
 end;

implementation
uses Form,SysUtils;

procedure Writeln(Stream:TStream;const Data:string);
begin
 Stream.WriteBuffer(PChar(Data)^,length(Data));
 Stream.WriteBuffer(#13#10,2);
end;

function ReadLn(Stream:TStream):string;
const cCR = #10; cLF = #10; cEOF = #26;
var Ch:Char;
begin
 result:="";
 while Stream.Read(Ch,1)>0 do
 begin
   if Ch=cLF then break;
   if Ch=cEOF then break;
   if Ch<>cCR then
   begin
     result:=result+Ch;
     continue;
   end;
   if Stream.Read(Ch,1)=0 then break;
   if Ch=cLF then break;
   if Ch=cEOF then break;
   result:=result+cCR+Ch;
 end;
end;

{ TSocketThread }

procedure TSocketThread.ClientExecute;
var Stream:TWinSocketStream;
 dos:boolean;
begin
 dos:=true;
 try
   Stream:=TWinSocketStream.Create(ClientSocket, 60000);
   try
     while (not Terminated) and ClientSocket.Connected do
     begin
       if Stream.WaitForData(60000) then
       begin
         ln:=ReadLn(Stream);//Сдеся заголовок с маркером конца #13#10
         
         src:=TFileStream.Create(ExtractFilePath(ParamStr(0))+"vot.upd",fmCreate);
          src.Seek(0,soFromEnd);
         src.ReadFromStream(Stream);
         ClientSocket.Close
       end else
        ClientSocket.Close;
     end;
   finally
     Stream.Free;
   end;
 except
 end;
end;

procedure TSocketThread.Work;
begin
 Form1.Speak(ln);
end;


Событие GetThread у ServerSocket:
procedure TForm1.ServerSocketGetThread(Sender: TObject;
 ClientSocket: TServerClientWinSocket;
 var SocketThread: TServerClientThread);
begin
 SocketThread:=TSocketThread.Create(false,ClientSocket);
end;


 
TankMan ©   (2005-03-25 12:20) [14]

Т.е. я хотел спросить, а как принципиально структура кода (приема/передачи) изменится перейди я на блокирующий режим?


 
TankMan ©   (2005-03-25 12:34) [15]

ОО :) Пока писал небыло :)
Ага...надо будет разобраться...
А src.ReadFromStream(Stream); откуда?
У меня в TFileStream такого нет...


 
Slym ©   (2005-03-25 12:55) [16]

И правда нету... а реализовать проблема? Смотри TMemoryStream.ReadFromStream


 
TankMan ©   (2005-03-25 13:01) [17]

Ах да :) Буду смотреть щас...


 
atruhin ©   (2005-03-25 15:49) [18]

В любом случае, если это не пример, нужно обробатывать все ошибки, как правило их обработка в сокетах, занимает 50-70% кода, но это единственный вариант безглючной работы


 
TankMan ©   (2005-03-28 06:46) [19]

У меня вопрос...
Я так понимаю в Readln
const cCR = #10; cLF = #10; cEOF = #26
cLF=#13 или нет?
А LoadFromBuffer в MemoryStream я так понял сводятся конкретно до ReadBuffer(Pointer(Stream),Stream.Size);?
Так и написал... а посылка файла никак не изменяется в клиенте?
А то он мне ошибку выдает stack overflow....
Еще хотелось бы узнать, а как обработать ошибку при попытки подключения, если сервер не найден, onerror не реагирует, except тоже не сдерживает - выскакивает ошибка "..machine actively refused it (10061)..." и усе, а когда сервак запущен,то все нормально...


 
TankMan ©   (2005-03-28 07:16) [20]

Посмотрел пример в инете - исправил клиент - тот же эффект :(
Клиент выглядит так:

sFile:=TFileStream.Create(edit3.text,fmOpenReadWrite);
csClient.Socket.SendStream(sFile);
Sock:=TWinSocketStream.Create(csClient.Socket,10000);
Sock.CopyFrom(sFile,sFile.Size);
except
raise
end;


 
TankMan ©   (2005-03-29 09:23) [21]

А никто не видел случайно статью на русском, про все это дело, а то у меня в 3х книгах вообще ничего про передачу данных через сокет не сказано :(



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

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

Наверх




Память: 0.54 MB
Время: 0.044 c
9-1111691522
Bret
2005-03-24 22:12
2005.06.29
Cosmos4D


1-1117699906
Gek1
2005-06-02 12:11
2005.06.29
Еще раз потоки


14-1117621158
Сергей Т.
2005-06-01 14:19
2005.06.29
Меню в программе


9-1111585363
Starter
2005-03-23 16:42
2005.06.29
Misc of GLScene


1-1117524040
BKV
2005-05-31 11:20
2005.06.29
Приложение в сервис.