Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Сети";
Текущий архив: 2007.11.18;
Скачать: [xml.tar.bz2];

Вниз

Вопрос по серверной части программы   Найти похожие ветки 

 
Dmitry_177   (2007-03-17 19:49) [0]

Пишу серверную часть программы, которая будет работать с клиентской частью(их будет несколько и надо так, чтобы они все могли одновременно подключиться к серверу). Вот меня интересует следующий вопрос, после ожидания, т.е. выполнения функции 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? Если да, то тогда произойдет сбой с теми сокетами которые были подключены ранее и все потоки которые были созданы будут уже работать с последним.. Вот как мне эти сокеты упорядочить? Создать массив или есть способ лучше?

И тоже самое с хендлами потоков, чтобы потом их при отключении правильно закрывать..


 
Сергей М. ©   (2007-03-19 08:41) [1]


> в переменной AcceptedSock тогда будет последний сокет, который
> был создан функцией accept?


Ну что значит "последний" ?

Не последний, а первый в очереди поступивших на сервер клиентских запросов на соединение.


> огда произойдет сбой с теми сокетами которые были подключены
> ранее


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


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


Передавай во вновь создаваемый поток параметром хэндл гнезда, полученный при accept()


 
Dmitry_177   (2007-03-19 09:01) [2]

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

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.


И еще такой вопросик, как мне определить что клиент неожиданно отключился? Чтобы поток, который был для него создан не висел на функции recv, а чтоб его завершить?


 
Сергей М. ©   (2007-03-19 09:07) [3]


> Работает


Иожет и работает, пока у тебя 1 клиент.
Но как только будет более одного, алгоритм твой терпит полное фиаско.


> как мне определить что клиент неожиданно отключился?


recv() вернет значение <= 0


 
Dmitry_177   (2007-03-19 22:12) [4]

Сергей М, работает!! и с несколькими компьютерами...=)) проверял..


 
Dmitry_177   (2007-03-19 22:23) [5]

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


 
Сергей М. ©   (2007-03-20 08:13) [6]


> Dmitry_177   (19.03.07 22:12) [4]


> работает!! и с несколькими компьютерами...=)) проверял


Да как же это может работать, если всем потокам ты передаешь параметром адрес одной и той же переменной, хранящей хэендл самого последнего созданного гнезда ?! Каждый поток со своим собственным гнездом должен работать ! А у тебя все потоки работают с одним и тем же гнездом))


> с чем лучше работать со строкой или с integer?


И какое отношение это имеет к Сетям ? Никакого.


 
Dmitry_177   (2007-03-20 10:32) [7]

Сергей М., попробуй, у меня все работает со всеми компьютерами.. Честно говоря сам не знаю почему, т.к. действительно передаю в поток адрес одной и той же переменной..


 
Сергей М. ©   (2007-03-20 10:49) [8]


> Dmitry_177   (20.03.07 10:32) [7]


Даже и пробовать не буду, потому что ошибка в логике видна даже невоор.глазом.

Передавать хэндл гнезда следует не по ссылке, а по значению !

var
hThread: THandle;
idThread: DWORD;
Len:Integer;
ListeningSocket, ClientSocket: TSocket;
...

function ThreadProc(ClientSocket: TSocket): DWORD;
var
PacketSize: Integer;
RecvInt: Integer;
begin
while true do
  begin
    PacketSize := recv(ClientSocket,  RecvInt, SizeOf(RecvInt), 0);
    if PacketSize > 0 then
      begin
       ...
      end
    else
      Break;
  end;
  CloseHandle(ClientSocket);
end;

...

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

while ... do
  begin
    ClientSocket := accept(ListeningSocket, @DBServer, @Len);
    if ClientSocket <> INVALID_SOCKET then
      hThread := BeginThread(nil, 0, @ThreadProc, Pointer(ClientSocket), 0, idThread));
    ....
  end;
...
CloseSocket(ListeningSocket);


 
Dmitry_177   (2007-03-20 11:11) [9]

В ThreadProc может CloseHandle(hThread); вместо CloseHandle(ClientSocket);?

И сокет ненадо закрывать т.е. так: CloseSocket(ClientSocket);?


 
Сергей М. ©   (2007-03-20 11:20) [10]


> В ThreadProc может CloseHandle(hThread); вместо CloseHandle(ClientSocket);
> ?


Нет, именно CloseHandle(ClientSocket).


> сокет ненадо закрывать


Как это не надо ? Раз уж сокет был создан, кто-то где-то должен же его уничтожить ? Подумай ..


 
Dmitry_177   (2007-03-20 11:23) [11]

опечатался, т.е. надо закрывать? CloseSocket(ClientSocket);?


 
Сергей М. ©   (2007-03-20 11:38) [12]


> Dmitry_177   (20.03.07 11:23) [11]


Конечно надо !

Другой вопрос, кто будет за этло ответственен - слушающий поток (создавший гнездо ClientSocket) или транспортный поток (который собственно и работает с гнездом ClientSocket).

Представь себе ситуацию - клиент законнектился к серверу и молчит как партизан, при этом транспортный поток "висит" на вызове recv() бесконечно долго (ибо у тебя блокирующий режим) и ни на что более не реагирует.

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

1. Закрыв из любого другого потока (например, слушающего) гнездо ClientSocket, с которым работает соотв.транспортный поток. В этом случае recv() вернет -1, что является фактом разрыва соединения. Но для этого нужно где-то запоминать значение ClientSocket, а ты этого не делаешь.

2. Терминировав соотв.транспортный поток (TerminateThread) с последующим обязательным закрытием соотв. CloseSocket (ибо утечка неизбежна). Ни то ни другое при твоем подходе не возможно - ты не запоминаешь ни hThread ни SocketHandle.



Страницы: 1 вся ветка

Форум: "Сети";
Текущий архив: 2007.11.18;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.49 MB
Время: 0.04 c
2-1193178890
хочу знать...
2007-10-24 02:34
2007.11.18
Преобразование типов!


6-1174036472
salexn
2007-03-16 12:14
2007.11.18
TSocketConnection и плохая сетка


2-1193162827
MAXHo
2007-10-23 22:07
2007.11.18
В чем может быть проблема?


15-1191982209
Slider007
2007-10-10 06:10
2007.11.18
С днем рождения ! 10 октября 2007 среда


2-1192859788
Kolan
2007-10-20 09:56
2007.11.18
Как при сворачивании модальной формы свернуть все приложение?





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский