Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / Delphi [игнор отключен] [закрыт для гостей] / TCP-сокеты. Покритикуйте пожалуйста код / 24 сообщений из 24, страница 1 из 1
27.07.2017, 08:58:29
    #39495723
A-MaR
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
TCP-сокеты. Покритикуйте пожалуйста код
Имеются 2 приложения-службы, работающие по TCP-портам 49001 и 49002 (порты настраиваются). Используется TTCPBlockSocket из пакета Synapse.
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
unit uEchoSrv;

interface  

uses
  Windows, Messages, SysUtils, StrUtils, Variants,Classes,
  StdCtrls, blcksock, uLogging, uLoggingThread;

type
  TProcessingData = function(const AData: string): string of object;

  TListenerThread = class(TLoggingThread)
     constructor Create(ASuspended: boolean; AIP: string; APort: Integer);
     destructor Destroy;override;
  private
     FSocket: TTCPBlockSocket;//объект сокета
     FThreadList: TList; //список дескрипторов потоков для работы с клиентами
     FProcessingData: TProcessingData;
     FHost: string;
     FPort: Integer;
     function GetIsTerminated: boolean;
  protected
     procedure Execute;override;
     procedure ServerStatus(Sender: TObject; Reason: THookSocketReason;
  const Value: String);
  public
     property Socket: TTCPBlockSocket read FSocket;
     property Host: string read FHost write FHost;
     property Port: Integer read FPort write FPort;
     property IsTerminated: boolean read GetIsTerminated;
     property ProcessingData: TProcessingData read FProcessingData write FProcessingData;
  end;

  TTCPThread = class(TLoggingThread)
  private
    FSocket: TTCPBlockSocket;
  protected
    var
      FProcessingData: TProcessingData;
    procedure Execute;override;
  public
    //ASocket - дескриптор сокета из очереди подключений.
    constructor Create(ASuspended: boolean; ASocket: integer);
    destructor Destroy; override;
    property Socket:TTCPBlockSocket read FSocket;
  end;

implementation    
{ TListenerThread }
 
constructor TListenerThread.Create(ASuspended: boolean; AIP: string; APort: Integer);
begin
  FSocket:=TTCPBlockSocket.Create;
  FThreadList:=TList.Create;
  FHost := AIP;
  FPort := APort;
  FSocket.OnStatus := ServerStatus;
  inherited Create(ASuspended);
  LogWriter.Suffix := '_EchoServer';
end;
 
destructor TListenerThread.Destroy;
var T:TTCPThread;
begin
  //завершаем все работающие нити
  while FThreadList.Count <> 0 do
    begin
      T:=TTCPThread(FThreadList.Extract(FThreadList.Last));
      T.Terminate;
      T.WaitFor;
      T.Free;
    end;
  //освобождаем память
  FThreadList.Free;
  FSocket.Free;
  inherited;
end;

function TListenerThread.GetIsTerminated: boolean;
begin
  Result := Terminated;
end;
 
procedure TListenerThread.Execute;
var T:TTCPThread;
begin
  FSocket.CreateSocket;//создаем новый сокет
  //связываем сокет с локальным адресом
  //выбор номера порта оставляем на усмотрение Synapse
  //FSocket.Bind(FSocket.LocalName,'0');
  FSocket.Bind(FHost,IntToStr(FPort));
  if FSocket.LastError=0 then begin //связываение с локальным адресом прошло успешно
     FSocket.Listen; //переходим в режим ожидания
    repeat
      if FSocket.CanRead(100) then begin//можем произвести чтение
       
        //получаем дескриптор сокета и создаем новую нить для клиента
        T:=TTCPThread.Create(false,FSocket.Accept);
        //определяем обработчик события ONStatus для новой нити
        T.Socket.OnStatus:=FSocket.OnStatus;
        T.FProcessingData := FProcessingData;
        //добавляем указатель на нить в список
        FThreadList.Add(pointer(T));
      end;
    until Terminated;//"гуляем" по циклу до тех пор, пока пользователь не остановит
  end
  else
    Self.LogWriter.Write('Ошибка при подключении ' + FSocket.LastErrorDesc, leError);//ошибка связывания - показываем её пользователю
end;

procedure TListenerThread.ServerStatus(Sender: TObject; Reason: THookSocketReason;
  const Value: String);
begin
  case Reason of
    HR_Bind: begin
               Self.LogWriter.Write ('Bind: '+Value);
               {lblAddress.Caption:=Server.Socket.GetLocalSinIP+':'+
                   IntToStr(Server.Socket.GetLocalSinPort) }
             end;
    HR_CanRead: Self.LogWriter.Write('Готов к чтению');
    HR_CanWrite: Self.LogWriter.Write('Готов к записи');
    HR_Listen: Self.LogWriter.Write('Прослушивание');
    HR_Accept: Self.LogWriter.Write('Подключение с адреса '+ Socket.GetRemoteSinIP);
    HR_ReadCount: Self.LogWriter.Write('Прочитано символов '+Value);
    HR_WriteCount: Self.LogWriter.Write('Записано символов '+Value);
    HR_Wait: Self.LogWriter.Write('Ожидание');
    HR_Error: Self.LogWriter.Write('Ошибка '+ Socket.LastErrorDesc, leError);
  end;
end;  

constructor TTCPThread.Create(ASuspended: boolean; ASocket: integer);
begin
  FSocket:=TTCPBlockSocket.Create;
  FSocket.Socket:=ASocket;
  FSocket.GetSins;
  inherited Create(ASuspended);
  LogWriter.Suffix := '_EchoServer';
end;
 
destructor TTCPThread.Destroy;
begin
  FSocket.Free;
  inherited;
end;
 
procedure TTCPThread.Execute;
var S, S_Out:string;
begin
 //работаем пока не поступит сигнал на остановку
  while not Terminated do begin
    try
      //есть данные ожидающие чтения
      if FSocket.WaitingData <> 0 then begin
        S_Out := '';
        //получаем данные
        s:=FSocket.RecvPacket(2000);
        Self.LogWriter.SuspendedWrite('Получена строка ' + s);
        //ошибок при получении данных не было
        if FSocket.LastError = 0 then begin
          if Assigned(FProcessingData) then begin
            S_Out := FProcessingData(S);
            Self.LogWriter.SuspendedWrite('Ответ сервера ' + S_Out);
            FSocket.SendString(S_Out);
          end;
        end
        else
          Self.LogWriter.Write('Ошибка при получении данных ' + FSocket.LastErrorDesc, leError);//ошибка связывания - показываем её пользователю
        Self.LogWriter.WriteList;
      end;
    except
      On E:Exception do
        Self.LogWriter.Write('TTCPThread.Execute ' + e.Message); 
    end;
    sleep(10);//"спим" 10 миллисекунд
  end;
end;

end.



и клиентское приложение, общающееся с этими службами через указанные порты, в том числе регистрация этих сервисов, запуск и остановка
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
unit uEchoClnt;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes,
  blcksock, StdCtrls, uLogging;

type
  TEchoClient = class (TLoger)   
 
  const
    cReadTimeout = 30000;
    constructor Create;
    destructor destroy; override;
  private
    FEnabled: boolean;
    FSocket: TTCPBlockSocket;
    FHost: string;
    FPort: Integer;
  public
    function Connect(AData: string = ''): string;
    procedure Disconnect;
    function SendString(AData: string; NeedConfirm: boolean = True; CustomTimeout: Integer = cReadTimeout): string;
    property Enabled_: boolean read FEnabled write FEnabled;
    property Host: string read FHost write FHost;
    property Port: Integer read FPort write FPort;
  end;

implementation
USES uCassTypes;

constructor TEchoClient.Create;
begin
  inherited;
  LogWriter.Suffix := '_EchoClient';
  FSocket := TTCPBlockSocket.Create;
  FSocket.RaiseExcept:=True;
  Host := 'localhost';
  Port := 49001;
end;

destructor TEchoClient.Destroy;
begin
  FreeAndNil(FSocket);
  inherited Destroy;
end;

function TEchoClient.Connect(AData: string = ''): string;
var SomeStr: string;
begin
  try  
    FSocket.Connect(FHost, IntToStr(FPort));  
    Self.LogWriter.SuspendedWrite(Format('Выполнено подключение к хосту %s на порт %d', [FHost, FPort]));
    if AData = '' then  
      Self.LogWriter.WriteList;  
    Result := '';
  except  
    on E:Exception do begin  
      if AData <> '' then  
        SomeStr := ' Строка ' + AData  
      else  
        SomeStr := '';  
      Self.LogWriter.Write(Format('Ошибка при подключении к порту %d (%s).%s',
        [FPort, E.Message, SomeStr]), leError);
      Enabled_ := false;  
      Result := E.Message;  
    end;  
  end;
end;

procedure TEchoClient.Disconnect;
begin
  Self.LogWriter.Write('Отключение от хоста');
  FSocket.CloseSocket;
end;

function TEchoClient.SendString(AData: string; NeedConfirm: boolean = True; CustomTimeout: Integer = cReadTimeout): string;
//NeedConfirm - если требуется ответ, например, запрос какого-либо параметра.
var Sending: boolean;
    i: integer;
begin
  Sending := false;
  i := 0;
  while (not Sending) and (i < 3) do begin
    try
      if NeedConfirm then begin
        Disconnect;
        Connect(AData);
      end;
      Self.LogWriter.SuspendedWrite('Передача строки ' + AData);
      FSocket.SendString(AData);
      if NeedConfirm then begin
        Result := FSocket.RecvPacket(CustomTimeout);
        Self.LogWriter.Write('Ответ сервера ' + Result);
      end
      else
        Self.LogWriter.Write('Ожидание ответа не предусмотрено');
      Sending := true;
    except
      ON e:Exception do begin
        Result := cSendError;
        Self.LogWriter.Write(Format('Ошибка при передаче %s, порт %d, строка %s', [e.Message, FPort, AData]), leError);
        sleep(100);
        i := i + 1;
      end;
    end;
  end;
  if not Sending then begin
    Disconnect;
    Connect;
  end;
end;

end.



Настройками предусмотрен также вариант работы без использования сокетов (по аналогии с командой sc), но в Windows10 для управления службами необходим запуск клиентской части от имени админа, что не устраивает безопасников.

В общем, все это вполне работает, но беда в том, что к рабочему месту подключен фискальник, приложение работает с ним через COM-порт, но TCP также задействуется для работы в качестве онлайн-кассы (порт 7778). И через несколько часов работы фискальник начинает с завидным постоянством выпадать в ошибку -1 (Нет связи) именно на тех точках, где взаимодействие со службами ведется посредством TCP. Там где без TCP - проблем нет, специально менял режимы работы на одних и тех же точках. Вывод - что-то не так в приведенном коде, но что именно?

(С сокетами работаю впервые, первоначальный код был заимствован из книги о Synapse Владислава Баженова, за что ему большое спасибо)
...
Рейтинг: 0 / 0
27.07.2017, 09:11:00
    #39495730
A-MaR
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
TCP-сокеты. Покритикуйте пожалуйста код
P.S. SendString из клиентской части выполняется примерно раз в минуту, изначально параметр NeedConfirm всегда был True, сейчас планирую от этого уйти, но на проблемных точках еще не тестировал.
...
Рейтинг: 0 / 0
27.07.2017, 09:11:10
    #39495731
wadman
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
TCP-сокеты. Покритикуйте пожалуйста код
A-MaR,

логирование же есть... Попробуй при ошибках дернуть GetLastError и WSAGetLastError.
...
Рейтинг: 0 / 0
27.07.2017, 09:40:22
    #39495752
A-MaR
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
TCP-сокеты. Покритикуйте пожалуйста код
wadman,

Так в самих сокетах ошибок нет - проблема в том, что они каким-то образом вышибают фискальник. А фискальник все что может сообщить - это ошибка -1 (Нет связи). Ну еще в его системном логе вот такие сообщения: TComPort ReadFile ERROR: 0x000003E3, Операция ввода/вывода была прервана из-за завершения потока команд или по запросу приложения.
Я ему напрямую точно никаких команд на прерывание не выдаю, следовательно гадят работающие сокеты.
...
Рейтинг: 0 / 0
27.07.2017, 09:44:39
    #39495756
asutp2
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
TCP-сокеты. Покритикуйте пожалуйста код
A-MaR,

утечки дескрипторов случайно нет?
...
Рейтинг: 0 / 0
27.07.2017, 10:36:33
    #39495794
wadman
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
TCP-сокеты. Покритикуйте пожалуйста код
A-MaR TComPort ReadFile ERROR: 0x000003E3, Операция ввода/вывода была прервана из-за завершения потока команд или по запросу приложения.
Это проблема потока, где-то ошибка в нем.
Оберни всё в try except и добавь логирование Exception.
...
Рейтинг: 0 / 0
27.07.2017, 11:15:19
    #39495828
fortress
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
TCP-сокеты. Покритикуйте пожалуйста код
Если я правильно понял, то каждый раз при отправке строки (при NeedConfirm=True) будет заново устанавливаться соединение со службой. Если сокет уже подключен к серверу, зачем нужно его закрывать и подключаться снова?

По поводу ошибки фискальника. Может быть количество открытых соединений косвенно влияет на его работу? Например, как уже писали выше про утечку дескрипторов. Если попробовать во время проблем с фискальником проверить в системе количество открытых сетевых соединений?

Службам для обмена нужны именно сетевые сокеты TCP? Если они обе работают локально можно рассмотреть способ взаимодейcтвия через именованные каналы или разделяемую память (незнаю правда, как в этом случае обстоят дела с привелегиями).
...
Рейтинг: 0 / 0
27.07.2017, 11:17:56
    #39495831
asutp2
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
TCP-сокеты. Покритикуйте пожалуйста код
A-MaR,

в коде используется следующий участок:

Код: pascal
1.
2.
3.
4.
5.
      
      T:=TTCPThread(FThreadList.Extract(FThreadList.Last));
      T.Terminate;
      T.WaitFor;
      T.Free;



вопрос - у TTCPThread свойство FreeOnTerminate в какое значение установлено?
...
Рейтинг: 0 / 0
27.07.2017, 11:24:54
    #39495837
A-MaR
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
TCP-сокеты. Покритикуйте пожалуйста код
asutp2A-MaR,

утечки дескрипторов случайно нет?
По крайней мере в диспетчере задач дублирующих процессов нет, FASTMM4 не ругается (пробовал также запускать службы как обычное приложение).
...
Рейтинг: 0 / 0
27.07.2017, 11:25:51
    #39495840
A-MaR
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
TCP-сокеты. Покритикуйте пожалуйста код
wadmanA-MaR TComPort ReadFile ERROR: 0x000003E3, Операция ввода/вывода была прервана из-за завершения потока команд или по запросу приложения.
Это проблема потока, где-то ошибка в нем.
Оберни всё в try except и добавь логирование Exception.
Добавил по максимуму, на тестовом компе ошибок нет, но попробую завтра еще раз у клиента запустить.
...
Рейтинг: 0 / 0
27.07.2017, 11:32:11
    #39495850
A-MaR
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
TCP-сокеты. Покритикуйте пожалуйста код
fortressЕсли я правильно понял, то каждый раз при отправке строки (при NeedConfirm=True) будет заново устанавливаться соединение со службой. Если сокет уже подключен к серверу, зачем нужно его закрывать и подключаться снова?
Пробовал так. Если предыдущая отправка выполнялась с NeedConfirm=False, а текущая с True, то по непонятным причинам возвращается ответ предыдущей отправки. Как это лечить, пока не разобрался.

fortressЕсли попробовать во время проблем с фискальником проверить в системе количество открытых сетевых соединений?Спасибо, попробую.
...
Рейтинг: 0 / 0
27.07.2017, 11:47:48
    #39495876
asutp2
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
TCP-сокеты. Покритикуйте пожалуйста код
A-MaRasutp2A-MaR,

утечки дескрипторов случайно нет?
По крайней мере в диспетчере задач дублирующих процессов нет, FASTMM4 не ругается (пробовал также запускать службы как обычное приложение).
нет, речь не о процессах, а именно о дескрипторах
...
Рейтинг: 0 / 0
27.07.2017, 16:18:05
    #39496169
A-MaR
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
TCP-сокеты. Покритикуйте пожалуйста код
asutp2нет, речь не о процессах, а именно о дескрипторах
Понял, о чем речь. Блин, действительно в одной из служб все дескрипторы TTCPThread аккуратно прибивались при остановке службы, но в процессе работы копились и копились. Возможно в этом и причина.
...
Рейтинг: 0 / 0
28.07.2017, 04:27:50
    #39496394
A-MaR
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
TCP-сокеты. Покритикуйте пожалуйста код
Странно: в типе THookSocketReason предусмотрен код HR_SocketClose с комментарием {:Socket closed by CloseSocket method.}, но даже если включить его в обработку статусов в TListenerThread.ServerStatus, сервер все равно не замечает, когда клиент выполняет у себя CloseSocket (возможно по этой причине автор и не включил его в первоначальный пример). Как в этом случае отлавливать отключение клиента, чтобы вовремя прибивать его дескриптор на сервере?
...
Рейтинг: 0 / 0
28.07.2017, 07:24:57
    #39496401
A-MaR
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
TCP-сокеты. Покритикуйте пожалуйста код
В общем, опытным путем выяснил, что этот статус HR_SocketClose срабатывает при закрытии потока на сервере, а не на клиенте. Так что видимо единственный выход - перед выполнением CloseSocket на клиенте отправлять серверу специальную команду без запроса подтверждения, и уже по этой команде сервер будет прибивать поток. Как-то примерно так (может есть более оптимальное решение?):
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
procedure TTCPThread.Execute;
var S, S_Out:string;
begin
 //работаем пока не поступит сигнал на остановку
  while not Terminated do begin
    try
      //есть данные ожидающие чтения
      if FSocket.WaitingData <> 0 then begin
        S_Out := '';
        //получаем данные
        s:=FSocket.RecvPacket(2000);
        Self.LogWriter.SuspendedWrite('Получена строка ' + s);
        //ошибок при получении данных не было
        if FSocket.LastError = 0 then begin
          if Assigned(FProcessingData) then begin
            S_Out := FProcessingData(S);
            Self.LogWriter.SuspendedWrite('Ответ сервера ' + S_Out);
            FSocket.SendString(S_Out);
          end;
        end
        else
          Self.LogWriter.Write('Ошибка при получении данных ' + FSocket.LastErrorDesc, leError);//ошибка связывания - показываем её пользователю
        //Если команда на завершение - сбрасываем флаг активности
        if UpperCase(s) = 'BYE' then
          IsActive := False;
        Self.LogWriter.WriteList;
      end;
    except
      On E:Exception do
        Self.LogWriter.Write('TTCPThread.Execute: ' + e.Message, leError);
    end;
    sleep(10);//"спим" 10 миллисекунд
  end;
end;

procedure TListenerThread.Execute;
var T:TTCPThread;

  procedure KillInactive;
  var i: integer;
      CurrentThread: TTCPThread;
  begin
    for i := FThreadList.Count-1 downto 0 do begin
      CurrentThread := TTCPThread(FThreadList[i]);
      if CurrentThread <> nil then begin
        if not CurrentThread.IsActive then begin
          FThreadList.Delete(i);
          CurrentThread.Terminate;
          CurrentThread.WaitFor;
          CurrentThread.Free;
          Self.LogWriter.Write(Format('Поток %d завершил работу и будет удален. Оставшееся количество потоков %d', [i, FThreadList.Count]));
        end;
      end;
    end;
  end;


begin
  try
    FSocket.CreateSocket;//создаем новый сокет
    //связываем сокет с локальным адресом
    //выбор номера порта оставляем на усмотрение Synapse
    //FSocket.Bind(FSocket.LocalName,'0');
    FSocket.Bind(FHost,IntToStr(FPort));
    if FSocket.LastError=0 then begin //связываение с локальным адресом прошло успешно
       FSocket.Listen; //переходим в режим ожидания
      repeat
        try
          if FSocket.CanRead(100) then begin//можем произвести чтение

            //получаем дескриптор сокета и создаем новую нить для клиента
            T:=TTCPThread.Create(false,FSocket.Accept);
            //Устанавливаем флаг активности
            T.IsActive := True;
            T.FreeOnTerminate := False;
            //определяем обработчик события ONStatus для новой нити
            T.Socket.OnStatus:=FSocket.OnStatus;
            T.FProcessingData := FProcessingData;
            //добавляем указатель на нить в список
            FThreadList.Add(pointer(T));
            Self.LogWriter.SuspendedWrite(Format('Количество TCP-потоков %d', [FThreadList.Count]));
          end;
          //Проверка на наличие неактивных потоков
          KillInactive;
        except
          on E:Exception do
            Self.LogWriter.Write('TListenerThread repeat: ' + e.Message, leError);
        end;
      until Terminated;//"гуляем" по циклу до тех пор, пока пользователь не остановит
    end
    else
      Self.LogWriter.Write('Ошибка при подключении ' + FSocket.LastErrorDesc, leError);//ошибка связывания - показываем её пользователю
    Self.LogWriter.WriteList;
  except
    on E:Exception do
      Self.LogWriter.Write('TListenerThread.Execute: ' + e.Message, leError);
  end;
end;

...
Рейтинг: 0 / 0
28.07.2017, 08:31:19
    #39496422
asutp2
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
TCP-сокеты. Покритикуйте пожалуйста код
A-MaR,

добавь поддержку таймаута при обмене с клиентами. Допустим не было за последние N секунд поступлений данных от клиента - тогда принудительно рвем с ним соединение
...
Рейтинг: 0 / 0
28.07.2017, 08:59:42
    #39496429
wadman
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
TCP-сокеты. Покритикуйте пожалуйста код
A-MaRи уже по этой команде сервер будет прибивать поток. Как-то примерно так (может есть более оптимальное решение?):
Это нормальное решение. Плюсом можно пинговать и если пинга не было 1-5 минут, то такое соединение считать мертвым.

П.С. Хотя, в ics закрытие соединения клиентом улавливается серверной стороной. Вот как они это делают? :-)
...
Рейтинг: 0 / 0
28.07.2017, 11:22:59
    #39496549
alekcvp
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
TCP-сокеты. Покритикуйте пожалуйста код
wadmanП.С. Хотя, в ics закрытие соединения клиентом улавливается серверной стороной. Вот как они это делают? :-)
Возможно так.

Но вообще, при попытке чтения из закрытого сокета должна возникать ошибка, возможно она и возникает, поэтому имеет смысл сделать что-то вроде:
автор
Код: pascal
1.
2.
3.
4.
5.
6.
7.
case FSocket.WaitingData of 
 -1: // WSAGetLastError и посмотреть что там находится.
  0: Sleep(10); // спим и продолжаем
else
  { Приём и обработка данных }
end;
{ тут Sleep() уже не нужен }    
...
Рейтинг: 0 / 0
28.07.2017, 11:24:09
    #39496551
alekcvp
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
TCP-сокеты. Покритикуйте пожалуйста код
alekcvp,
Причёт именно WSAGetLastError, потому что в Synapse в функции WaitingData почему-то не вызывается CheckError();
...
Рейтинг: 0 / 0
28.07.2017, 11:28:54
    #39496556
alekcvp
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
TCP-сокеты. Покритикуйте пожалуйста код
Хотя нет, поторопился :(
Вот как WaitingData реализована в BlockSock.pas:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
function TBlockSocket.WaitingData: Integer;
var
  x: Integer;
begin
  Result := 0;
  if synsock.IoctlSocket(FSocket, FIONREAD, x) = 0 then
    Result := x;
  if Result > c64k then
    Result := c64k;
end;



Если её выполнить на закрытом сокете, то, возможно, возникнет ошибка, но вы о ней не узнаете, т.к. функция тупо вернёт 0.
Поэтому, возможно, вместо неё стоит вызвать напрямую IoctlSocket(FSocket, FIONREAD, Value) и анализировать её код возврата и потом уже значение Value.
...
Рейтинг: 0 / 0
28.07.2017, 11:41:38
    #39496565
YuRock
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
TCP-сокеты. Покритикуйте пожалуйста код
wadmanП.С. Хотя, в ics закрытие соединения клиентом улавливается серверной стороной. Вот как они это делают? :-)Ну понятно, что (скорее всего) на сервере recv висит и ждет либо данных, либо SOCKET_ERROR. Я, во всяком случае, именно так и делаю обычно.
...
Рейтинг: 0 / 0
28.07.2017, 13:05:08
    #39496629
A-MaR
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
TCP-сокеты. Покритикуйте пожалуйста код
Всем спасибо! В боевом режиме буду тестировать уже в понедельник;)
...
Рейтинг: 0 / 0
28.07.2017, 13:12:54
    #39496633
Квейд
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
TCP-сокеты. Покритикуйте пожалуйста код
asutp2A-MaR,

в коде используется следующий участок:

Код: pascal
1.
2.
3.
4.
5.
      
      T:=TTCPThread(FThreadList.Extract(FThreadList.Last));
      T.Terminate;
      T.WaitFor;
      T.Free;



Выделенное - лишнее, Terminate и WaitFor вызывает деструктор
...
Рейтинг: 0 / 0
28.07.2017, 13:19:35
    #39496642
YuRock
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
TCP-сокеты. Покритикуйте пожалуйста код
Квейдasutp2A-MaR,

в коде используется следующий участок:

Код: pascal
1.
2.
3.
4.
5.
      
      T:=TTCPThread(FThreadList.Extract(FThreadList.Last));
      T.Terminate;
      T.WaitFor;
      T.Free;




Выделенное - лишнее, Terminate и WaitFor вызывает деструктор
Да, вообще можно
Код: pascal
1.
TTCPThread(FThreadList.Extract(FThreadList.Last)).Free


вместо этих 4-х строк и доп. переменной
...
Рейтинг: 0 / 0
Форумы / Delphi [игнор отключен] [закрыт для гостей] / TCP-сокеты. Покритикуйте пожалуйста код / 24 сообщений из 24, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]