Модераторы: Snowy, Poseidon, MetalFan
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Вопрос по серверной части программы 
:(
    Опции темы
Dmitry_177
Дата 17.3.2007, 19:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 418
Регистрация: 22.9.2006

Репутация: нет
Всего: нет



Пишу серверную часть программы, которая будет работать с клиентской частью(их будет несколько и надо так, чтобы они все могли одновременно подключиться к серверу). Вот меня интересует следующий вопрос, после ожидания, т.е. выполнения функции listen, сервер должен создать сокет функцией accept, чтобы произошло подключение до конца.. А если будет несколько подключений? Как быть тогда? Делаю так:

Код

var
  hThread: THandle;
  idThread: DWORD;
  Len:Integer;
  Sock, AcceptedSock: TSocket;
...

function ThreadProc(Param: Pointer): DWORD; stdcall;
var
  PacketSize: Integer;
  RecvInt: Integer;
begin
  while true do
    begin
      PacketSize := recv(AcceptedSock,  RecvInt, SizeOf(RecvInt), 0);
      if PacketSize > 0 then
        begin
          // сообщение принято, анализируем его

        end;
    end;
end;


begin
...

  wile true do
    begin
      listen(Sock, SOMAXCONN);

      Len := SizeOf(TSockAddr);

      AcceptedSock := accept(Sock, @Server, @Len);

      hThread := CreateThread(nil, 0, @ThreadProc, nil, 0, idThread);
    end;
end;


И вот в переменной AcceptedSock тогда будет последний сокет, который был создан функцией accept? Если да, то тогда произойдет сбой с теми сокетами которые были подключены ранее и все потоки которые были созданы будут уже работать с последним.. Вот как мне эти сокеты упорядочить? Создать массив или есть способ лучше?

И тоже самое с хендлами потоков, чтобы потом их при отключении правильно закрывать..
PM MAIL   Вверх
ama_kid
Дата 18.3.2007, 10:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


АСУТП-кодер
***


Профиль
Группа: Комодератор
Сообщений: 1460
Регистрация: 5.3.2007
Где: Москва

Репутация: нет
Всего: 95



Я то же самое успешно реализовывал (только на Visual C++), поэтому могу кое-что посоветовать...
У функции CreateThread четвертый параметр - это параметр, передаваемый в созданный поток function ThreadProc(Param: Pointer): DWORD; stdcall;
Поэтому, мне кажется, будет лучше передавать принятый сокет непосредственно в поток, чтобы не зависеть от остальных потоков. Что-то типа такого (пишу непосредственно в форум, поэтому могут быть ошибки с типами-указателями, но самое главное - показать идею):
Код

var
  hThread: THandle;
  idThread: DWORD;
  Len:Integer;
  Sock, AcceptedSock: TSocket;
...
function ThreadProc(Param: Pointer): DWORD; stdcall;
var
  PacketSize: Integer;
  RecvInt: Integer;
  MySock : ^TSocket;
begin
 MySock:=(TSocket^)Param;   // Приводим параметр потока к требуемому указателю
  while true do
    begin
      PacketSize := recv(MySock^,  RecvInt, SizeOf(RecvInt), 0);
      if PacketSize > 0 then
        begin
          
          // сообщение принято, анализируем его
        end;
    end;
end;
begin
...
  wile true do
    begin
      listen(Sock, SOMAXCONN);
      Len := SizeOf(TSockAddr);
      AcceptedSock := accept(Sock, @Server, @Len);
      hThread := CreateThread(nil, 0, @ThreadProc, @AcceptedSock  , 0, idThread);
      WaitForSingleObject(hThread,2000);\
      CloseHandle(hThread);                     / Так можно делать только, если поток обработки клиента не слишком тяжелый и не зацикленный...
    end;
end;


Еще один момент - функцию listen надо вынести за цикл (ибо это можно сделать один раз) и цикл организовать по accept. Приведу, на всякий случай, свой рабочий пример, если что - можешь поразбираться:
Код

unsigned __stdcall RunServerInThread( void* lParam )   // Функция серверного потока
{
  sockaddr_in server_addr;
  HOSTENT *hst;
  SOCKET client_socket;        // сокет для клиента
  sockaddr_in client_addr;    // адрес клиента (заполняется системой)                        
// Открываем сокет
  if ((ServerSocket=socket(AF_INET,SOCK_STREAM,0))<0)
  {
   _endthreadex(-1);
   return -1;
  }
//Формируем адрес
  server_addr.sin_family=AF_INET;
  server_addr.sin_port=htons(MY_PORT);
  server_addr.sin_addr.s_addr=0;
// биндим адрес
  if (bind(ServerSocket,(sockaddr *) &server_addr,
              sizeof(server_addr)))
  {
    closesocket(ServerSocket);  
    _endthreadex(-1);
    return -1;
  }
//Запускаем сокет на прослушивание
  if (listen(ServerSocket, 0x100))
  {
    closesocket(ServerSocket);
    _endthreadex(-1);
    return -1;
  }

  int client_addr_size=sizeof(client_addr);
//акцептим клиентов в цикле. Функция accept является блокирующей, поэтому, чтобы завершить этот цикл - из основного процесса надо закрыть ServerSocket
  while(client_socket=accept(ServerSocket, (sockaddr *)&client_addr, &client_addr_size))
  {
    if (client_socket==INVALID_SOCKET) break; // При закрытии ServerSocket это условие срабатывает (Закрываем при завершении приложения)
    hst=gethostbyaddr((char *)&client_addr.sin_addr.s_addr,4, AF_INET);
    HANDLE thID;
    thID = (HANDLE)_beginthreadex(NULL,0,&ServerToClient,&client_socket,0,NULL);// Запускаем поток обработки клиента с параметром client_socket  - этот параметр передается в создаваемый поток
    WaitForSingleObject(thID, 2000);   // Ждем завершения
    CloseHandle(thID);                         // Закрываем хендл
  }
  _endthreadex(0);
  return 0;
}

// Эта функция создается в отдельном потоке и
// обсуживает очередного подключившегося клиента
// независимо от остальных
unsigned __stdcall ServerToClient(void* client_socket)
{
  SOCKET my_sock;
  TCP_Data buf;
  const int BUF_SIZE = 200;
  my_sock=((SOCKET *) client_socket)[0];

  int bytes_recv=0;
  ZeroMemory(&buf, sizeof(TCP_Data));
  while((bytes_recv=recv(my_sock,(char*)&buf,100,0))&& (bytes_recv !=SOCKET_ERROR))
{
        ...
    // Здесь идет анализ принятого буфера
        ...
}
  // закрываем сокет
  closesocket(my_sock);
  _endthreadex(0);
  return 0;
}




--------------------
самурай без меча подобен самураю с мечом, но только без меча 
PM MAIL   Вверх
Dmitry_177
Дата 19.3.2007, 08:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 418
Регистрация: 22.9.2006

Репутация: нет
Всего: нет



У меня получилось =))) Работает =)))
ama_kid, вместо MySock:=(TSocket^)Param; пишем это: MySock := Param; и все работает..

Выложу пример, если кому будет интересно и заглянет в код и найдет ошибки, прошу сообщить об этом..
Код

var
  WSAData: TWSAData;
  DBSock, AcceptSocket: TSocket;
  DBServer: TSockAddrIn;
  AMessage: TMsg;
  idThread: DWORD;
  Len: Integer;

function ThreadProc(Param: Pointer): DWORD; stdcall;
var
  AcceptSock: ^TSocket;
  PacketSize: Integer;
  RecvInt: Integer;
begin
  AcceptSock := Param;
  while true do
    begin
      PacketSize := recv(AcceptSock^,  RecvInt, SizeOf(RecvInt), 0);
      if PacketSize > 0 then
        begin
            case RecvInt of
              001: begin
                     MessageBox(0, '001', 'KLDBServer', 0);
                   end;
...

            end;
        end;
    end;
  CloseSocket(AcceptSock^);
end;

begin
  WSAStartUp($0101, WSAData);
  DBSock := Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
  if DBSock = INVALID_SOCKET then
    Exit;

  FillChar(DBServer, SizeOf(DBServer), 0);
  DBServer.sin_family := PF_INET;
  DBServer.sin_port := htons(3030);
  DBServer.sin_addr.S_addr := INADDR_ANY;
  FillChar(DBServer.sin_zero, SizeOf(DBServer.sin_zero), 0);
  if Bind(DBSock, DBServer, SizeOf(DBServer)) = SOCKET_ERROR then
    Exit;

  listen(DBSock, SOMAXCONN);
  Len := SizeOf(TSockAddr);

  while true do
    begin
      AcceptSocket := accept(DBSock, @DBServer, @Len);
      CloseHandle(CreateThread(nil, 0, @ThreadProc, @AcceptSocket, 0, idThread));
    end;

  CloseSocket(DBSock);
  WSACleanUp;
end.


и еще странность, по идее мне не нужен id потока, пишу в функции CreateThread вместо idThread это: nil выдает ошибку..

Это сообщение отредактировал(а) Dmitry_177 - 19.3.2007, 08:54
PM MAIL   Вверх
ama_kid
Дата 19.3.2007, 11:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


АСУТП-кодер
***


Профиль
Группа: Комодератор
Сообщений: 1460
Регистрация: 5.3.2007
Где: Москва

Репутация: нет
Всего: 95



Dmitry_177
Все-таки правильней между CreateThread (кстати, обрати внимание на функцию BeginThread, лучше использовать её) и CloseHandle применять WaitForSingleObject. Ибо поток к закрытию хендла может не завершиться, а закрытие хендла работающего потока не есть гут. Понятно, что ты сделал это для экономии на переменной хендла, но имхо, на таких вещах не экономят... А вот с idThread - это да, здесь можно было бы сэкономить, но дельфи этого не дает, ибо этот параметр объявлен как var, поэтому в отличие от C++ туда надо передавать адрес реальной переменной....


--------------------
самурай без меча подобен самураю с мечом, но только без меча 
PM MAIL   Вверх
dumb
Дата 19.3.2007, 15:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


sceloglauxalbifacies
****


Профиль
Группа: Экс. модератор
Сообщений: 2929
Регистрация: 16.6.2006

Репутация: 5
Всего: 158



Цитата(ama_kid @  19.3.2007,  11:42 Найти цитируемый пост)
Ибо поток к закрытию хендла может не завершиться, а закрытие хендла работающего потока не есть гут.

совершенно законная операция, не имеющая никаких "не гутовых" последствий.
PM MAIL   Вверх
ama_kid
Дата 19.3.2007, 16:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


АСУТП-кодер
***


Профиль
Группа: Комодератор
Сообщений: 1460
Регистрация: 5.3.2007
Где: Москва

Репутация: нет
Всего: 95



Цитата(dumb @  19.3.2007,  15:37 Найти цитируемый пост)
совершенно законная операция

Конечно, законная, ибо не запрещенная прямо... Но закрывая хендл потока, теряешь над ним контроль, и в случае его зацикливания (читай - мемори-лика) отсутствует возможность проинформировать об этом разработчика\юзера (с помощью той же WaitForSingleObject)... А при наличии огромной кучи таких потоков (каждый со своим некорректным кодом) это может привести к неопознанным AV.

P.S. Конечно, в данном случае вряд ли что-то такое будет, но все-таки закрывать хендл проверенно завершенного потока - это хорошая привычка, которая помогает уменьшить вероятность появления мемори-ликов...


--------------------
самурай без меча подобен самураю с мечом, но только без меча 
PM MAIL   Вверх
Dmitry_177
Дата 19.3.2007, 22:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 418
Регистрация: 22.9.2006

Репутация: нет
Всего: нет



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

И еще вопрос, вот я посылаю в клиенте и принимаю в этом сервере переменную типа integer, т.к. мне так удобней, т.к. можно воспользоваться case .. of, а многие другие протоколы, тот же smtp передают строку.. вот с чем лучше работать со строкой или с integer? с integer более наглядный код, а со строкой одни условия будут..

насчет BeginThread обращал внимание.. учту что ей лучше воспользоваться, но чем она лучше?
PM MAIL   Вверх
ama_kid
Дата 20.3.2007, 07:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


АСУТП-кодер
***


Профиль
Группа: Комодератор
Сообщений: 1460
Регистрация: 5.3.2007
Где: Москва

Репутация: нет
Всего: 95



Цитата(Dmitry_177 @  19.3.2007,  22:26 Найти цитируемый пост)
Почему не воспользовался select-ом, потому что у меня подключение долгое.. т.е. постоянное висит, и нету постоянно обмена данными.. 

А, теперь понятно, тогда нормально...

Цитата(Dmitry_177 @  19.3.2007,  22:26 Найти цитируемый пост)
вот с чем лучше работать со строкой или с integer? с integer более наглядный код, а со строкой одни условия будут

Это как тебе удобнее, ты в любом случае передаешь буффер, а как ты интерпретируешь этот буфер - твое дело...

Цитата(Dmitry_177 @  19.3.2007,  22:26 Найти цитируемый пост)
насчет BeginThread обращал внимание.. учту что ей лучше воспользоваться, но чем она лучше? 

BeginThread - это функция над М$-овсокой _beginthread. О том, чем она лучше, можешь почитать MSDN KB. Упрощенно говоря, при использовании CreateThread могут быть небольшие утечки памяти...



--------------------
самурай без меча подобен самураю с мечом, но только без меча 
PM MAIL   Вверх
Dmitry_177
Дата 20.3.2007, 10:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 418
Регистрация: 22.9.2006

Репутация: нет
Всего: нет



Честно говоря сам не знаю почему как же это работает, но работает.., если всем потокам я передаею параметром адрес одной и той же переменной, хранящей хэендл самого последнего созданного гнезда.. И почему-то работает это, проверял, подключался к нему несколькими компьютерами и все он принимает с любого компьютера, если они все подключены будут..
PM MAIL   Вверх
dumb
Дата 20.3.2007, 13:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


sceloglauxalbifacies
****


Профиль
Группа: Экс. модератор
Сообщений: 2929
Регистрация: 16.6.2006

Репутация: 5
Всего: 158



как бы там ни было, передавай не указатель, а непосредственно TSocket - это ж обычный dword по сути.
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Delphi: Сети"
Snowy
Poseidon
MetalFan

Запрещено:

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делится вскрытыми компонентами

  • Литературу по Дельфи обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь
  • 90% ответов на свои вопросы можно найти в DRKB (Delphi Russian Knowledge Base) - крупнейшем в рунете сборнике материалов по Дельфи

Если Вам помогли и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, Snowy, Poseidon, MetalFan.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Delphi: Сети | Следующая тема »


 




[ Время генерации скрипта: 0.0792 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.