Форум: "Сети";
Текущий архив: 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