Форум: "Сети";
Текущий архив: 2005.06.29;
Скачать: [xml.tar.bz2];
ВнизНе понимаю - почему ошибка при копировании через 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;
Скачать: [xml.tar.bz2];
Память: 0.52 MB
Время: 0.195 c