-
Добрый день,
С помощью компонент TTcpClient / TClientSocket соединюсь с удаленным сервером на котором стоит 100 мегабитная сетевая карточка и получаю поток данных (примерно 15 мегабайт в минуту). Все бы ничего, да только когда мое приложение начинает заниматься другими вещами (графика, винчестер) происходит какой то сдвиг во входящих данных, проявляющийся в том что сегодняшние данные поступают завтра. После закрытия и повторного открытия порта сдвиг прекращается и все идет корректно.
Чтение буфера для ClientSocket идет по событию
procedure TForm_Main.ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket); var S: AnsiString; begin S := Socket.ReceiveText(); if S = '' then Exit; Buf := Buf + S; Inc(BytesRead, Length(S)); RedrawStatusBar; end;
Чтение буфера для TcpClient происходит в потоке c Priority := tpTimeCritical;
procedure TTcpPort.Execute; begin while not Terminated and TcpClient.Connected do begin BufData := ReadPort; Synchronize(SynchroMethod); end; end;
function TTcpPort.ReadPort: String; const BufLen = 1024 * 30; var d: array[1..BufLen] of Char; i, BytesRead: Integer; begin try //Result := TcpClient.Receiveln; BytesRead := TcpClient.ReceiveBuf(d, SizeOf(d), 0); except on E: Exception do Exit; end; Result := ''; for i := 1 to BytesRead do Result := Result + d[i]; end;
procedure TTcpPort.SynchroMethod; begin with F_Radar do begin if BufData <> '' then RawDataProcessing.TreatBuffer(BufData); BufData := ''; end; end;
Однако в обоих случаях проблема одинаковая. Если кто то сталкивался с таким, прошу помочь!
-
Да, после того как повтыкал Apllication.ProcessMessages в процедуры работы с графикой и винтом, ситуация улучшилась, но не разрешилась!
-
Может у тебя работа со строками не правильная? Посмотри как твоя прога загружает проц... Если примерно 100% при отсутствующих Apllication.ProcessMessages, то добавь после Synchronize Sleep(1); посмотришь разницу... :) Еще за один раз (за ИНДИ не знаю), ReceiveBuf/ReceiveText больше 8192 не читает, если ты сам не конфигурировал размеры буферов!
function TTcpPort.ReadPort: String; const BufLen = 1024 * 30; var d: array[1..BufLen] of Char; i, BytesRead: Integer; begin try //Result := TcpClient.Receiveln; BytesRead := TcpClient.ReceiveBuf(d, SizeOf(d), 0); except on E: Exception do Exit; end; Result := ''; for i := 1 to BytesRead do Result := Result + d[i]; end;
отмеченное жирным полная хрень!!!!
Var S:String; Size,Size1:Integer; Begin Size:=TcpClient.Receiveln; {Socket.ReceiveLength} Try SetLength(S,Size); Size1:=Tcp.ReceiveBuf(S[1],Size,0); If (Size1>0)and(Size1<>Size) Then {делай чего нибудь} Begin SetLength(S,Size1); End; Result:=S; finally SetLength(S,0); end; End;
Обрати внимание на жирным выделенное!!!!!!
-
Priority := tpTimeCritical; Полная фигня!!!
Зачем тебе это? А если к диску обратиться из этой нитки, то вообще вешаться? Пока не видел ни одной задачи, чтобы для принятия данных по сети нужен был приоритет реального времени...
-
> Size:=TcpClient.Receiveln; {Socket.ReceiveLength}
Метод не работает, Size - Integer, TcpClient.Receiveln - String.
-
>Alik
А что по комментариям трудно догадаться?
Size:=Socket.ReceiveLength; Ищи подобную ф-цию в своем компоненте или напиши свою!
-
Если не найдешь в компоненте...
Function ReceiveLength:Integer; Begin IOCTLSocket(FSock,FIONREAD,Result); End;
-
Я еще раз говорю, что скорее всего у тебя не правильная обработка данных приходящих от сервера! Сервер твой?
-
> Я еще раз говорю, что скорее всего у тебя не правильная > обработка данных приходящих от сервера! > Сервер твой?
Сервер это наше железо, работающее с сетевой картой WIZ830MJ, есть подозрение, что электронщик еще не полностью разобрался со всеми регистрами и прерываниями от карточки и проблемы начинаются с его стороны когда сервер не может протолкнуть данные клиенту по причине занятости последнего. Старые данные все таки досылаются завтрашним днем, но сдвинутые и перемешанные. Стоит клиенту закрыть и открыть свой порт, все нормализуется.
-
> Var > S:String; > Size,Size1:Integer; > Begin > Size:=TcpClient.Receiveln; {Socket.ReceiveLength} > Try > SetLength(S,Size); > Size1:=Tcp.ReceiveBuf(S[1],Size,0); > If (Size1>0)and(Size1<>Size) Then > {делай чего нибудь} > Begin > SetLength(S,Size1); > End; > Result:=S; > finally > SetLength(S,0); > end; > End;
Сделал следующее:
function TTcpPort.ReadPort: String; const BufLen = 1024 * 10; var BytesRead: Integer; begin try SetLength(Result, BufLen); BytesRead := TcpClient.ReceiveBuf(Result[1], BufLen, 0); SetLength(Result, BytesRead); except on E: Exception do Exit; end; end;
Выглядет более правильно, но проблема остается
-
Т.е. вы сами пишете стек TCPIP? Я чего-то не пойму... :\ Если у Вас ТСР реализовано правильно, то какие это данные вчерашние? ТСР гарантирует доставку последовательно пакетов, иначе разрывается связь с соответствующей ошибкой(таймаута или д.р.)! Вопрос в чем, как ты инициализируешь передачу данных от сервера или как/откуда сервер знает, что тебе надо передавать данные? Может сам сервер не правильно посылает данные? А открытие и закрытие порта лишь "лечение гланд через @опу..."! Софт на сервере Ваш или сторонний? Как работает алгоритм на сервере?
-
Электрон > FireMan_Alexey © (23.09.11 19:01) [10]
Электронщик на низком уровне на программируемой логической матрице (ПЛМ) дает команды записи, чтения и т.д. сетевой карте, там же следит за ее прерываниями, т.е полностью управляет сетевухой, но видимо что то не доделывает.
Данные формируются постоянно один раз в 4 миллисекунды по 808 байт и поступают на вход нашей железки. Задача сервера (железки с сетевой картой) их передавать.
Сервер в принипе честно все укладывает в исходящий буфер и думает, что данные уходят и буфер очищается. В действительности же если клиент не может получать данные по каким либо причинам, сервер продолжает класть в буфер по 808 байт, а сетевая карта не протолкнула еще предыдущие данные и происходит сдвижка.
Сейчас я стал обрабатывать событие TcpClient.OnError, ошибок нет.
По сему я делаю предварительный вывод, что проблема возникает на сервере. Очевидно, что он должен рулить затыки связи и задержки передачи, иначе ни одна сеть не смогла бы работать.
-
В принципе ты прав, а не возможность доставить пакет клиенту за время Т яв-ся ошибкой... :) или при посылке данных при не пустом буфере должна возникать ситуация с отказом передачи данных, которые твой сервер должен обрабатывать! Вот еще вопрос, на который ты мне не ответил: Где реализован стек ТСР? Как я понял у тебя сетевая с Ethernet (Канальный уровень) протоколом, а ТСР (транспортный) который правильно формирует последовательность пакетов! Еще, так называемая Железка(Сервер) поддерживает подключения клиентов? Если да, то сколько и как реализован алгоритм рассылки твоих сообщений? Я думаю у Вас на сервере просто не правильно построена логика обмена с клиентом... Т.е., возможно, необходимо чтобы клиент сам посылал запрос на данные с сервера... (как ответ на пинг)
-
В железе реализована прямая стыковка ПЛИС и сетевого модуля. По мере формирования железякой данных они бросаются ПЛИСом в регистр TX сетевого модуля, как только накопилась пачка в 808 байт (раз в 4 миллисекунды) ПЛИС отдает модульку команду SEND и данные улетают к клиенту. Сетевой модулек имеет свои шины, буфера TX и RX, адресное пространство аж на 8 сокетов. При включении питания ПЛИС записывает в модулек таблицу настроек (IP адрес, Port, и т.д.) При запросе подключения от клиента сетевой модулек формирует прерывание, на которое ПЛИС дает команду OPEN_Socket. При поступлении данных от клиента, модулек формирует прерывание RECIEVE, потом ПЛИС читает регистр входящего буфера и т.д....
В принципе все работает красиво, но сервер сейчас не обрабатывает прерывания SEND_OK и SEND_FAILED, которое возникает вслед за командой отправки данных (после заданного таймаута и заданного кол-ва попыток передачи). То есть ПЛИС игнорирует эти сообщения, а сетевой модулек оказывается что нет. В исходящем регистре модулька остается след от непереданных данных.
Здесь как бы все понятно, но не понятно мне другое - с помощью компоненты TTcpClient получается осуществить коннект только в режиме BlockMode := bmBlocking; С помощью компоненты ClientSocket1 можно работать в режиме ClientType := ctNonBlocking.
Сам пока не смог понять причину такого поведения !?
-
> Здесь как бы все понятно, но не понятно мне другое - с помощью > компоненты TTcpClient получается осуществить коннект только > в режиме BlockMode := bmBlocking; > С помощью компоненты ClientSocket1 можно работать в режиме > ClientType := ctNonBlocking.
Мой совет, почитай про модуль winsock/winsock2 не обязательно все переписывать на winsock АПИ, просто для понимания работы блок/не блок режимов надо ручками попробовать, а в компонентах могут быть нюансы... С TTcpClient даже не разбирался, когда-то перелопатил (Client/Server)Socket сделал для себя выводы, написал для себя несколько классов, для различных задач. Сейчас открыл реализацию TTcpClient, дело в том, что при не блок режиме при коннекте куда либо возвращается ошибка типа WSAEWOULDBLOCK:
> With a nonblocking socket, the connection attempt cannot > be completed immediately. In this case, connect will return > SOCKET_ERROR, and WSAGetLastError will return WSAEWOULDBLOCK. > In this case, the application can: > > 1. Use select to determine the completion of the connection > request by checking if the socket is writeable, or > 2. If your application is using WSAAsyncSelect to indicate > interest in connection events, then your application will > receive an FD_CONNECT notification when the connect operation > is complete, or > 3. If your application is using WSAEventSelect to indicate > interest in connection events, then the associated event > object will be signaled when the connect operation is complete. >
А в реализации самого компонента это не учтено, во всяком случае в моем Делфи 6 :) И он выдает ошибку! А в (Client/Server)Socket коннект реализован чуть-чуть по другому :)
-
> FireMan_Alexey © (25.09.11 01:18) [14]
В итоге, судя по сообщению коннект происходит, но легче от этого не становится )
WSAEISCONN 10056 Socket is already connected. A connect request was made on an already-connected socket. Some implementations also return this error if sendto is called on a connected SOCK_DGRAM socket (for SOCK_STREAM sockets, the to parameter in sendto is ignored) although other implementations treat this as a legal occurrence.
Вообщем буду изучать винсокеты и попробую реализвать на инди клиенте.
Большое спасибо FireMan_Alexey за помощь и дельные советы!!!
-
-
Я понял, ты сразу после коннета в не блок режиме пытаешься что-то прочитать/записать, но факт коннекта в не блок режиме является событие FD_CONNECT, которое приходит ввиде сообщения при использовании WSAAsyncSelect или Event-а при использовании WSAEventSelect. А до момента прихода события сокет не считается с кем-либо соединенным. Я для себя сделал модуль когда-то, для блок режима, который очень удобно использовать(для меня, конечно) в отдельном потоке. Если хочешь, могу поделиться... ;)
-
> FireMan_Alexey © (26.09.11 01:17) [17] > Я понял, ты сразу после коннета в не блок режиме пытаешься > что-то прочитать/записать, но факт коннекта в не блок режиме > является событие FD_CONNECT, которое приходит ввиде сообщения > при использовании WSAAsyncSelect или Event-а при использовании > WSAEventSelect. А до момента прихода события сокет не считается > с кем-либо соединенным. Я для себя сделал модуль когда-то, > для блок режима, который очень удобно использовать(для > меня, конечно) в отдельном потоке. Если хочешь, могу поделиться. > .. ;)
Да, если можно, дай ссылочку откуда скачать или можно слить сюда: asumrl@mail.ru
-
если сообщение 808 байт зачем читать BufLen = 1024 * 30; столько? unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Controls, Forms, ScktComp, StdCtrls;
type
TForm1 = class(TForm)
Memo1: TMemo;
private
public
end;
var
Form1: TForm1;
implementation
type
TClientSocketThread=class(TThread)
private
Buf:AnsiString;
protected
procedure Execute; override;
procedure SynchroMethod;
end;
procedure TClientSocketThread.Execute;
function RecvBufFully(Peer:TCustomWinSocket;var Buf; Count: Integer):boolean;
var pBuf:PByte;
s:integer;
begin
result:=false;
pBuf:=@Buf;
while count>0 do
begin
s:=Peer.ReceiveBuf(pBuf^,Count);
if s=0 then exit;
inc(pBuf,s);
dec(Count,s);
end;
result:=true;
end;
const SizeOfMsg=808;
var Sock:TClientSocket;
begin
SetLength(Buf,SizeOfMsg);
Sock:=TClientSocket.Create(nil);
try
Sock.ClientType:=ctBlocking;
Sock.Host:='127.0.0.1';
Sock.Port:=11111;
while not Terminated do
begin
if not Sock.Active then
Sock.Active:=true;
if not RecvBufFully(Sock.Socket,PChar(Buf)^,Length(Buf)) then
begin
Sock.Active:=false;
continue;
end;
Synchronize(SynchroMethod);
end;
finally
Sock.Free;
end;
end;
procedure TClientSocketThread.SynchroMethod;
begin
Form1.memo1.text:=Buf;
end;
end.
-
Пример использования.
Sock.Host:='127.0.0.1'; Sock.Port:=8080; If Not Sock.Connect Then Begin ShowMessage(IntToStr(Sock.Error)); Exit; End; While (not Terminated)and(Not Sock.Disconnected) do Begin Sock.GetStatus; If Sock.ErrorPresent Then {obrabativaem oshibku} If Sock.CanRead Then Size:=Sock.ReceiveLength; //Potom chitaem -> Sock.Read If Sock.CanWrite Then //Pichem End; Sock.Close;
-
Сам модуль.
unit NewSock;
interface Uses WinSock;
CONST SOCKET_ERROR=WINSOCK.SOCKET_ERROR; Type TNewSock=Class Private FSock :Integer; FClose :Boolean; FError :Integer; FCanRead :Boolean; FCanWrite:Boolean; FOnError :Boolean; FAddr :TSockAddr; FHost :String; FPort :Word; Public Procedure Close; Procedure GetStatus(Const TimeOut:Integer=50); Function GetRemoteAddress:Integer; Function GetRemoteHost:String; Function Connect:Boolean; Function Write(Var Buff; Size:Integer):Integer; Function Read (Var Buff; Size:Integer):Integer; Function ReceiveLength:Integer; Constructor Create(Const Sock:Integer=Invalid_Socket); Destructor Destroy;Override; Property Host:String Read FHost Write FHost; Property Port:Word Read FPort Write FPort; Property CanRead:Boolean Read FCanRead; Property CanWrite:Boolean Read FCanWrite; Property Disconnected:Boolean Read FClose; Property ErrorPresent:Boolean Read FOnError; Property Error:Integer Read FError; End;
Function InetAddr(Addr:Integer):String;
implementation
Constructor TNewSock.Create; Begin Inherited Create; FClose:=Sock=Invalid_Socket; FSock:=Sock; {IF Not FClose Then Begin FCanWrite:=True; End Else} FCanWrite:=False; FCanRead:=False; FOnError:=False; FError:=0; End;
Destructor TNewSock.Destroy; Begin // Close; Inherited Destroy; End;
Procedure TNewSock.Close; Begin If FSock<>Invalid_Socket Then Begin If CloseSocket(FSock)<>0 Then Begin FError:=WSAGetLastError; FOnError:=True; End; FSock:=Invalid_Socket; FCanRead:=False; FCanWrite:=False; End; FClose:=True; End;
Procedure TNewSock.GetStatus; Var FD_READS,FD_WRITES,FD_ERROR:TFDSET; T:TTimeVal; R:Integer; Begin // If FClose Then Exit; T.tv_usec:=TimeOut Mod 1000; T.tv_sec:=TimeOut Div 1000; FD_ZERO(FD_READS); FD_SET(FSock,FD_READS); FD_WRITES:=FD_READS; FD_ERROR:=FD_READS;
//FD_SET(FSock,FD_WRITES);
R:=Select(1,@FD_READS,@FD_WRITES,@FD_ERROR,@T); If R=Socket_Error Then Begin FError:=WSAGetLastError; FOnError:=True; Exit; End Else FError:=0; If R>0 Then Begin If FD_ISSET(FSock,FD_READS) Then FCanRead:=True Else FCanRead:=False; If FD_ISSET(FSock,FD_WRITES) Then FCanWrite:=True; If FD_ISSET(FSock,FD_ERROR) Then Begin FOnError:=True; R:=SizeOf(FError); R:=GetSockOpt(FSock,SOL_SOCKET,SO_ERROR,@FError,R); {If R=SOCKET_ERROR Then FError:=WSAGetLastError;} End Else FOnError:=False; End; End;
Function TNewSock.GetRemoteAddress; Var _Addr:TSockAddr; _Size:Integer; Begin Result:=-1; If Not FClose Then Begin _Size:=SizeOf(_Addr); FError:=GetPeerName(FSock,_Addr,_Size); If FError=SOCKET_ERROR Then Begin FError:=WSAGetLastError; FOnError:=True; Exit; End; Result:=_Addr.sin_addr.S_addr; End; End;
Function TNewSock.GetRemoteHost; Var _Addr:Integer; Host:PHostEnt; Begin _Addr:=GetRemoteAddress; Host:=GetHostByAddr(@_Addr,SizeOf(_Addr),AF_INET); Result:=''; If Host<>Nil Then Result:=Host.h_name; End;
Function TNewSock.Write; Var R:Integer; Begin R:=Send(FSock,Buff,Size,0); If R=SOCKET_ERROR Then Begin FError:=WSAGetLastError; If FError=WSAEWOULDBLOCK Then Begin FCanWrite:=False; R:=0; FError:=0; End Else FOnError:=True; End; Result:=R; End;
Function TNewSock.Read; Var R:Integer; Begin R:=Recv(FSock,Buff,Size,0); If R=0 Then Close; If R=SOCKET_ERROR Then Begin FError:=WSAGetLastError; If FError=WSAEWOULDBLOCK Then Begin FCanRead:=False; FError:=0; End Else FOnError:=True; R:=0; End; Result:=R; End;
Function TNewSock.ReceiveLength; Begin If IOCTLSocket(FSock,FIONREAD,Result)=SOCKET_ERROR Then Begin FError:=WSAGetLastError; FOnError:=True; End; End;
Function TNewSock.Connect; Var PHost:PHostEnt; R:Integer; Begin Result:=False; If Not FClose Then Exit; If FHost='' Then Exit; If FPort=0 Then Exit; FSock:=Socket(AF_INET,SOCK_STREAM,IPPROTO_IP); If FSock=Invalid_Socket Then Begin FError:=WSAGetLastError; FOnError:=True; Close; Exit; End; FAddr.sin_family:=AF_INET; FAddr.sin_port:=HToNS(FPort); FAddr.sin_addr.S_addr:=Inet_addr(PChar(FHost)); If FAddr.sin_addr.S_addr=INADDR_NONE Then Begin PHost:=GetHostByName(PChar(FHost)); If PHost=Nil Then Begin FError:=WSAGetLastError; FOnError:=True; Exit; End; Move(PHost^.h_addr_list^[0], FAddr.sin_addr, PHost^.h_length); End; R:=WinSock.Connect(FSock,FAddr,SizeOF(FAddr)); If R=SOCKET_ERROR Then Begin FError:=WSAGetLastError; FOnError:=True; Exit; End; FClose:=False; Result:=True; End;
Function InetAddr; Var _Addr:TSockAddr; Begin _Addr.sin_addr.S_addr:=Addr; Result:=inet_ntoa(_Addr.sin_addr); End;
VAR WSAD:TWSAData;
Initialization Begin WSAStartup($202,WSAD); End;
Finalization Begin WSACleanup; End;
end.
-
Спасибо всем, буду пробовать!
|