Форум программистов
 
Контакты: о проблемах с регистрацией, почтой и по другим вопросам пишите сюда - alarforum@yandex.ru, проверяйте папку спам! Обязательно пройдите активизацию e-mail.

Вернуться   Форум программистов > Delphi > Работа с сетью в Delphi
Регистрация

Восстановить пароль
Повторная активизация e-mail


Донат для форума - использовать для поднятия настроения себе и модераторам

А ещё здесь можно купить рекламу за 25 тыс руб в месяц! ) пишите сюда - alarforum@yandex.ru

Ответ
 
Опции темы
Старый 23.11.2012, 17:25   #1
Dee63
Пользователь
 
Аватар для Dee63
 
Регистрация: 09.03.2010
Адрес: Самара
Сообщений: 48
Репутация: 10

icq: девять-четыре-пять-ч
По умолчанию Socket-ы и многопоточность

Всем привет!
Писал я тут приложеньице клиент-серверное, суть которого от клиента собрать инфу и передать ее на сервер, а на сервере обработать и вернуть клиенту ответ.

Так вот, использовал сокеты в режиме stNonBlocking ибо очень мал объем запросов и клиентов онлайн.

Теперь задача усложнилась- клиентов в разы больше и запросов прилетает, соответственно, больше.

Начал курить в сторону stThreadBlocking.
Но не понимаю до конца, и найти не могу толкового описания.

По сути, вопрос касается сервера.
Цель: держать онлайн подключенными минимум 20-200 клиентов (в последствии минимум станет равен 1000+), причем они могут в любой момент прислать запрос и его надо не потерять и вовремя ответить на него.

Представляю себе это так:
При подключении каждого клиента к сокету создается отдельный поток (по onGetThread), в Execute которого происходит исполнение инструкций, соответствующих открытию подключения (логирование и т.п.).
Затем мне нужно как-то получать инфу от клиента.
\\\\
В режиме stNonBlocking все просто делалось- на onClientRead вешался обработчик и все хорошо работало
\\\\\
Как повесить обработчик в реализации потока?
Я так предполагаю, что нужно в потоке ждать прихода этого сообщения, но непойму как.
Т.е. я понимаю, что нужно периодически поток пробуждать и проверять пришло ли что-то и уже дальше обрабатывать. Или тут я не правилно представляю?

И также меня мучает вопрос как мне всего многообразия клиентских потоков выбирать, тот, которому я должен отправить ответ.
\\\\
В предыдущей реализации я по имени хоста определял адресата.
\\\\
И как тут делается отправка?

Для ясности общая схема работы сервера такова:

Сообщения от клиента складываются в общую очередь на обработку.
Я имею отдельный специальнообученый поток для работы с этой очередью и данными в ней- передача в БД и исключение переданного элемента из очереди.
Также я имею специальный отдельный поток для забора информации из БД и ее помещения в другую специальную очередь. Тут содержатся ответы на сообщения клиентов.
Еще у меня поток есть, который обрабатывает очередь с ответами для клиентов и рассылает их нужным клиентам.


Собственно именно эту конструкцию мне необходимо подружить с потоками сокета.


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

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

Спасибо за понимание и помощь!
жду с нетерпением
Dee63 вне форума   Ответить с цитированием
Старый 24.11.2012, 22:06   #2
xoodoo
Форумчанин
 
Регистрация: 11.04.2012
Сообщений: 212
Репутация: 69

icq: 118024746
По умолчанию

Цитата:
нужно .. ждать прихода ..сообщения, но непойму как
см. ClientSocket.ReceiveText и ClientSocket.ReceiveBuf
либо см. TWinSocketStream
xoodoo вне форума   Ответить с цитированием
Старый 28.11.2012, 12:10   #3
Slym
Профессионал
 
Регистрация: 07.12.2011
Адрес: Siberia
Сообщений: 1,021
Репутация: 759
По умолчанию

На 1000+ клиентов stThreadBlocking не потянет, не хватит памяти
от 0 до 500 вполне работоспособно и пишется многократно проще чем stNonBlocking
пример Эха

Код:
program CharGen;

{$APPTYPE CONSOLE}

uses
  SysUtils,ScktComp,WinSock;

type
  TServerClientThreadEx=class(TServerClientThread)
  protected
    procedure ClientExecute; override;
  end;

// вся работа тут
procedure TServerClientThreadEx.ClientExecute;
var
  Buf:array[byte] of char;
  i:integer;
begin
  try
    while (not Terminated) and ClientSocket.Connected do
    begin
      i:=ClientSocket.ReceiveBuf(Buf,SizeOf(Buf));
      if i<=0 then
        ClientSocket.Close;
      ClientSocket.SendBuf(Buf,i);
    end;
  except
    Terminate;
    HandleException;
  end;
end;

procedure GetThread(Self:TObject;Sender: TObject;ClientSocket: TServerClientWinSocket; var SocketThread: TServerClientThread);
begin
  writeln('GetThreadEvent ',ClientSocket.RemoteAddress,':',ClientSocket.RemotePort);
  SocketThread:=TServerClientThreadEx.Create(false,ClientSocket);
end;

function Proc2Method(Code, Data: Pointer):TMethod;
begin
  result.Code:=Code;
  result.Data:=Data;
end;

var Server:TServerWinSocket;
begin
  Server:=TServerWinSocket.Create(INVALID_SOCKET);
  try
    Server.ThreadCacheSize := 10;
    Server.OnGetThread:=TGetThreadEvent(Proc2Method(@GetThread,Server));
    Server.Listen('', '', '', StrToIntDef(ParamStr(1),19), SOMAXCONN);
    while Server.Connected do
     Sleep(100);
  finally
    Server.Free;
  end;
end.
В этом коде ошибка
__________________
Не стесняемся, плюсуем!

Последний раз редактировалось Slym; 28.11.2012 в 15:08.
Slym вне форума   Ответить с цитированием
Старый 28.11.2012, 12:13   #4
Slym
Профессионал
 
Регистрация: 07.12.2011
Адрес: Siberia
Сообщений: 1,021
Репутация: 759
По умолчанию

а проще stThreadBlocking потому что в потоке ты один на один с конкретным клиентом поток ждет посылки от клиента и ждет пока ответ не уйдет... все строго линейно как readln(s);writeln(s); безсобытийно. Вышел из потока - соединение разорвал. Для новичков я советую именно stThreadBlocking
__________________
Не стесняемся, плюсуем!
Slym вне форума   Ответить с цитированием
Старый 06.12.2012, 09:22   #5
Dee63
Пользователь
 
Аватар для Dee63
 
Регистрация: 09.03.2010
Адрес: Самара
Сообщений: 48
Репутация: 10

icq: девять-четыре-пять-ч
По умолчанию

Код:
i:=ClientSocket.ReceiveBuf(Buf,SizeOf(Buf));
      if i<=0 then
        ClientSocket.Close;
      ClientSocket.SendBuf(Buf,i);
Если делаеть socket.close, то не будет ли завершаться поток?
или при таком варианте ClientSocket.Connected останется true т.к. клиент остается подключенным?
Dee63 вне форума   Ответить с цитированием
Старый 06.12.2012, 11:07   #6
Slym
Профессионал
 
Регистрация: 07.12.2011
Адрес: Siberia
Сообщений: 1,021
Репутация: 759
По умолчанию

ClientSocket.Connected станет false и поток завершится...
можно тупо завершить поток breakом тогда перед своей смертью поток сам отдисконектит сокет
__________________
Не стесняемся, плюсуем!
Slym вне форума   Ответить с цитированием
Старый 20.12.2012, 16:05   #7
ispovedn1k
 
Регистрация: 20.12.2012
Сообщений: 7
Репутация: 10
По умолчанию

Я пользовался для разработки следующим примером: http://softengines.ru/showthread.php?t=6863

Кстати, у меня есть вопросы по поводу обработки и корректного закрытия соединения. Кто может, ответьте, пожалуйста. Создал тут тему: http://programmersforum.ru/showthread.php?p=1154481
ispovedn1k вне форума   Ответить с цитированием
Старый 20.12.2012, 19:47   #8
ДралсяСошибками
Форумчанин
 
Аватар для ДралсяСошибками
 
Регистрация: 31.05.2011
Сообщений: 302
Репутация: 171
По умолчанию

Не осилил первый пост, слишком много букв, посмотри исходник.
Вложения
Тип файла: zip клиент-сервер.zip (50.5 Кб, 107 просмотров)
ДралсяСошибками вне форума   Ответить с цитированием
Старый 19.04.2013, 16:27   #9
Dee63
Пользователь
 
Аватар для Dee63
 
Регистрация: 09.03.2010
Адрес: Самара
Сообщений: 48
Репутация: 10

icq: девять-четыре-пять-ч
По умолчанию

Цитата:
Сообщение от ДралсяСошибками Посмотреть сообщение
Не осилил первый пост, слишком много букв, посмотри исходник.
Привет!
Посмотрел, повертел.

Пытаюсь внедрить в свой проект.

в var добавляю
Код:
  WWSAData: TWSAData;
  WSocket, WListenSocket: TSocket;
  WSockAddr: TSockAddr;
  TreadID: THandle;
Затем создаю процедуру

Код:
procedure TForm1.SocketThread;
var
  Buf: Array of Char;
  Str: String;
  Size, BufSize: Integer;
  ClientAddr: TSockAddr;
  ClientSocket: TSocket;
begin
  ClientSocket := WSocket;
    if ClientSocket = INVALID_SOCKET then Exit;
      Size := SizeOf(TSockAddr);
      GetPeerName(ClientSocket, ClientAddr, Size);
      WriteLn('Client Connect: ' + inet_ntoa(ClientAddr.sin_addr) + ':' + IntToStr(htons(ClientAddr.sin_port)));
      Size := SizeOf(Integer);
      GetSockOpt(WSocket, SOL_SOCKET, SO_RCVBuf, @BufSize, Size);
      SetLength(Buf, BufSize);
        repeat
          Size := Recv(ClientSocket, Buf[0], BufSize, 0);
            if Size <= 0 then Break;
              SetLength(Str, Size);
              lstrcpyn(@Str[1], @Buf[0], Size + 1);
              WriteLn('Client: ' + inet_ntoa(ClientAddr.sin_addr) + ':' + IntToStr(htons(ClientAddr.sin_port)) + ' Send: ' + Str);
                if Send(ClientSocket, Str[1], Length(Str), 0) = SOCKET_ERROR then Break;
        until False;
  WriteLn('Client Disconnect: ' + inet_ntoa(ClientAddr.sin_addr) + ':' + IntToStr(htons(ClientAddr.sin_port)));
  SetLength(Buf, 0);
  CloseSocket(ClientSocket);
end;
т.е. просто копипаст для начала

ну и на кнопку активации сокета прописываю

Код:
  if WSAStartup($101, WWSAData) <> 0 then Halt;
    WListenSocket := Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
      if WListenSocket = INVALID_SOCKET then Halt;
        FillChar(WSockAddr, SizeOf(TSockAddr), 0);
        WSockAddr.sin_family := AF_INET;
        WSockAddr.sin_port := htons(1111);
        WSockAddr.sin_addr.s_addr := inet_addr('192.168.103.101');
          if Bind(WListenSocket, WSockAddr, SizeOf(TSockAddr)) <> 0 then Halt;
            if Listen(WListenSocket, SOMAXCONN) <> 0 then Halt;
              WriteLn('Listening...');
                repeat
                  WSocket := Accept(WListenSocket, nil, nil);
                  CreateThread(nil, 0, @SocketThread, nil, 0, TreadID);
                until False;
  CloseSocket(WListenSocket);
  WSACleanup;
Собственно вопрос возникает не у меня, а у компилятора.

Цитата:
Сообщение от Компилятор
[Error] Unit1.pas(725): Variable required
на строчку
Код:
CreateThread(nil, 0, @SocketThread, nil, 0, ThreadID);
И курсор ставит после @SocketThread,

Не понимаю какую переменную он хочет тут видеть?
Dee63 вне форума   Ответить с цитированием
Ответ

Опции темы

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.

Быстрый переход

Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Многопоточность lalilulelo Операционные системы общие вопросы 20 16.04.2012 19:33
многопоточность MasterSporta Общие вопросы C/C++ 4 30.09.2011 13:27
Многопоточность и с++ Koshmarovsky Visual C++ 13 06.09.2010 20:39
Многопоточность kroŧ Общие вопросы Delphi 5 21.06.2010 10:47
Многопоточность CrazyDude Общие вопросы Delphi 1 18.04.2010 19:00


23:28.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2019, Jelsoft Enterprises Ltd.