Форум: "Сети";
Текущий архив: 2004.08.01;
Скачать: [xml.tar.bz2];
ВнизНеблокирующий режим в сокетах Найти похожие ветки
← →
kalishenko (2004-05-28 19:39) [0]Сокет никак не активизируется. Не пойму, почему. Вот пример:
const
CLOSE_SOCK = 0;
CONNECTING_SOCK = 1;
CONNECTED_SOCK = 2;
TIME_OUT = 30;
maxproccess = 2;
var
WSData: WSAData;
Return: integer;
Addr: TSockAddrIn;
Buf: array [0..100] of char;
sock : array [1..maxproccess] of TSocket;
stat : array [1..maxproccess] of Byte;
time : array [1..maxproccess] of Byte;
x : integer;
on_sock : LongInt = 1;
off_sock : LongInt = 0;
wfds_empty : Boolean;
wfds : Tfdset;
tv : Ttimeval;
procedure TForm1.Button4Click(Sender: TObject);
begin
FillChar(Buf,100,0);
WSAStartup($101,WSData);
for x := 1 to maxproccess do
stat[x] := CLOSE_SOCK;
repeat
for x := 1 to maxproccess do
begin
if stat[x] = CLOSE_SOCK then
begin
sock[x] := socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
time[x] := TIME_OUT;
Return := ioctlsocket(sock[x],FIONBIO,on_sock);
addr.sin_family := AF_INET;
addr.sin_port := htons(10001);
addr.sin_addr.s_addr := INADDR_ANY;
Return := connect(sock[x],addr,sizeof(addr));
stat[x] := CONNECTING_SOCK;
end;
end;
FD_ZERO(wfds);
wfds_empty:=true;
for x:=1 to maxproccess do
begin
if stat[x]=CONNECTING_SOCK then
begin
FD_SET(sock[x],wfds);
wfds_empty:=false;
end;
end;
if not wfds_empty then
begin
tv.tv_sec:=1;
tv.tv_usec:=0;
Return := select(0,nil,@wfds,nil,@tv);
end;
for x:=1 to maxproccess do
begin
if stat[x]<>CLOSE_SOCK then
begin
dec(time[x]);
if time[x]=0 then
begin
MessageBox(0,"Ваше время вышло!","",MB_OK);
stat[x]:=CLOSE_SOCK;
ioctlsocket(sock[x],FIONBIO,off_sock);
closesocket(sock[x]);
end;
end;
if stat[x]=CONNECTING_SOCK then
begin
if FD_ISSET(sock[x],wfds) then
stat[x]:=CONNECTED_SOCK;
end;
if stat[x]=CONNECTED_SOCK then
begin
MessageBox(0,"Привет!","",MB_OK);
FD_CLR(sock[x],wfds);
send(sock[x],buf,sizeof(buf),0);
Form1.Caption := Buf;
stat[x]:=CLOSE_SOCK;
ioctlsocket(sock[x],FIONBIO,off_sock);
closesocket(sock[x]);
end;
end;
Application.ProcessMessages;
until
false;
WSACleanup;
end;
← →
SammIk © (2004-05-28 20:12) [1]Попробуи
WSAStartup($0002,WSData);
Может поможет
← →
kalishenko (2004-05-28 20:25) [2]Не, Вы наверное не так меня поняли. WSAStartUp здесь непричем. Все с ним нлормально. И сокеты открываются (sock[x] := socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);), и замечательно переходят в неблокирующий режим (Return := ioctlsocket(sock[x],FIONBIO,on_sock);), затем, как и в хелпе написано, connect выдает SOCKET_ERROR для неблок. режима (Return := connect(sock[x],addr,sizeof(addr));), но затем сокеты никак не хотят select-иться (Return := select(0,nil,@wfds,nil,@tv);), и функция FD_ISSET говорит, что сокет не установлен, поэтому условие (if FD_ISSET(sock[x],wfds) then stat[x]:=CONNECTED_SOCK;) не выполняется. А почему, не пойму.
← →
Verg © (2004-05-28 20:30) [3]
> addr.sin_family := AF_INET;
> addr.sin_port := htons(10001);
> addr.sin_addr.s_addr := INADDR_ANY;
> Return := connect(sock[x],addr,sizeof(addr));
Так делать нельзя. Нельзя потключаться к INADDR_ANY.
← →
SammIk © (2004-05-28 20:36) [4]Выдержка из мсдн по поводу, select
Version: Requires Windows Sockets 2.0.
Header: Declared in Winsock2.h.
Library: Use Ws2_32.lib.
← →
kalishenko (2004-05-28 20:37) [5]А к кому можно, если это сервер?
← →
SammIk © (2004-05-28 20:38) [6]и про FD_SET
Version: Requires Windows Sockets 2.0.
Header: Declared in Winsock2.h.
← →
SammIk © (2004-05-28 20:41) [7]Есле это сервер, то надо биндить и листать, а не конектица
← →
Verg © (2004-05-28 20:41) [8]
> [5] kalishenko (28.05.04 20:37)
> А к кому можно, если это сервер?
Не понял. В каком месте это сервер?
Это классический клиент, если он вызывает connect - "соединиться" по-русски...
← →
kalishenko (2004-05-28 20:46) [9]>>Sammik
По-моему, Winsock 2.0 здесь ни причем, если еще в 1.1 эти функции задекларированы.
← →
SammIk © (2004-05-28 20:49) [10]Тебе лучше знать чем моему sdk.
← →
kalishenko (2004-05-28 20:50) [11]Ой, сорри, обознался. Но я попробовал и просто 127.0.0.1 - никакого эффекта.
← →
SammIk © (2004-05-28 20:57) [12]вопервых попробуи второи сокет $02,
а вовторых после каждого вызова сокетных ф-ии
поставь WSAGetLastError, и анализируи результат, возвращаемыи ф-ями.
Должно помочь
← →
Verg © (2004-05-28 20:58) [13]
> [11] kalishenko (28.05.04 20:50)
> Ой, сорри, обознался. Но я попробовал и просто 127.0.0.1
> - никакого эффекта.
Покажи код - как пробовал.
А где сервер 1001 порта на 127.0.0.1?
Он вообще есть?
← →
kalishenko (2004-05-28 21:23) [14]Извините, протормозил. Забыл запустить сервер. :)
Тогда попутно вопрос: как реализовать неблокирующий режим для сервера?
← →
Verg © (2004-05-28 21:45) [15]Да точно так же
socket;
ioctlsocket(...,FIONBIO...);
bind()
listen()
Select()
accept
Ожидать готовности входящего соединения надо включив сокет в readfds набор для ф-ции Select.
Или без select - просто периодически вызывая Accept, который в случае отсутствия готовых соединений вернет INVALID_SOCKET, а WSAGetLastError будет равен WSAEWOULDBLOCK.
← →
kalishenko (2004-05-28 22:46) [16]Вот так?
for x := 1 to maxproccess do
begin
if stat[x] = CLOSE_SOCK then
begin
sock[x] := socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
time[x] := TIME_OUT;
ioctlsocket(sock[x],FIONBIO,on_sock);
addr.sin_family := AF_INET;
addr.sin_port := htons(10001);
addr.sin_addr.s_addr := INADDR_ANY;
Return := bind(sock[x],addr,sizeof(addr));
Return := listen(sock[x],5);
end;
end;
for x := 1 to maxproccess do
if accept(sock[x],nil,nil) <> INVALID_SOCKET then stat[x]:=ACCEPTED_SOCK
else Return := WSAGetLastError;
for x := 1 to maxproccess do
begin
if stat[x]=ACCEPTED_SOCK then
begin
recv(Return,buff,sizeof(buff),0);
Form1.Caption := Buff;
stat[x]:=CLOSE_SOCK;
ioctlsocket(sock[x],FIONBIO,off_sock);
closesocket(sock[x]);
end;
end;
← →
Verg © (2004-05-28 23:02) [17]Тебе никто не даст установить несколько сокетов на прослушку одного и того же порта на одном и том же локальном адресе (в данном случае INADDR_ANY)
Результатом accept в случае не INVALID_SOCKET будет новый сокет ассоциированный с установленным соединением (клиентом). Т.е. это тот сокет, посредством которого сервер в последствии будет обмениваться информацией с клиентом.
Сам же слушающий (серверный) сокет не предназначен для приема или передачи информации. Его задача только лишь принимать запросы на входящие соединения и складывать их во внутреннюю очередь, извлекать из которой установленные соединения ты и будешь ф-цией accept....
Пока встречный вопрос: а зачем тебе все это на чистом Winsock API реализовывать? Возьми TClient или TServerSocket, ну или компоненты Indy...
← →
kalishenko (2004-05-28 23:20) [18]Просто здесь на форуме много раз высказывали мысль, что в TClientSocket и TServerSocket было обнаружено много ошибок. Да и просто захотелось узнать все изнутри. Кроме того, думаю использовать тот или иной вариант (блок. режим или неблок. режим) в консольной проге.
А приведенный мною выше код не работает, как хотелось бы. Все время вылетает INVALID_SOCKET после accept и WSAGetLastError равен WSAEWOULDBLOCK.
← →
Verg © (2004-05-28 23:54) [19]
> А приведенный мною выше код не работает, как хотелось бы.
> Все время вылетает INVALID_SOCKET после accept и WSAGetLastError
> равен WSAEWOULDBLOCK.
Конечно, ведь соединения-то никто и не пытался установить с твоим сервером. Ты должен "ждать" либо через select либо в блокирующем режиме
просто вызвать accept - он сам подождет
.
> Просто здесь на форуме много раз высказывали мысль, что
> в TClientSocket и TServerSocket было обнаружено много ошибок.
Ошибок, не ошибки - неточности есть некоторые, но, поверь их исчезающе малое число, особенно по сравнению с тем, которе ты сам можешь наделать пытаясь "вспахать" это все с нуля.
Вот посмотри. Примерчик самого наипростейшего, однопоточного TCP сервера (ЭХО-сервер) на WinsockAPI.
Этот сервер - пожалуй, самое простое, что можно сделать, но в то же время он крайне неэффективен, так что может рассматриваться только как учебный примерчик для начала.program EchoServer;
{$APPTYPE CONSOLE}
uses
Windows,
Winsock,
Classes,
SysUtils;
var
WSAData : TWSAData;
SSocket : TSocket;
LAddr : TSockAddrIn;
Fds,
Rfds : TFDSet;
I : integer;
function ToOem(const S : string):string;
begin
Result := S;
if Result<>"" then
CharToOemBuff(pchar(Result), pchar(Result), length(Result));
end;
procedure PrintErr(Err : DWORD; Wt : boolean);
begin
Writeln(ToOem(SysErrorMessage(Err)));
if Wt then ReadLn;
end;
procedure ExitErr;
begin
if (WSAGetLastError<>NO_ERROR) and
(WSAGetLastError<>WSAENOTSOCK) and
(WSAGetLastError<>WSAEINTR) then
PrintErr(WSAGetLastError, true);
if SSocket<> INVALID_SOCKET then
CloseSocket(SSocket);
if WSAData.wVersion <> 0 then
WSACleanup;
halt(0);
end;
function checksocket(Ret : integer):integer;
begin
Result := Ret;
if Result = SOCKET_ERROR then
ExitErr
end;
procedure DoRead(S : TSocket);
var Res : integer;
Buffer : array[0..255] of byte;
begin
Res := recv(S, Buffer[0], sizeof(Buffer), 0);
if Res > 0 then
Res := send(S, Buffer[0], Res, 0);
if Res <= 0 then
begin
if Res < 0 then
PrintErr(WSAGetLastError, false);
FD_CLR(S, Fds);
if CloseSocket(S) = SOCKET_ERROR then
PrintErr(WSAGetLastError, false);
end;
end;
var
NewSocket : TSocket;
begin
if WSAStartup(MAKEWORD(1,1), WSAData) <> NO_ERROR then ExitErr;
SSocket := socket(PF_INET, SOCK_STREAM, 0);
if SSocket = INVALID_SOCKET then ExitErr;
FillChar(LAddr, sizeof(LAddr), 0);
LAddr.sin_family := AF_INET;
LADDR.sin_port := htons(8088);
LAddr.sin_addr.S_addr := htonl(INADDR_ANY);
checksocket( bind(SSocket, LAddr, sizeof(LAddr)) );
checksocket( listen(SSocket, SOMAXCONN) );
FD_ZERO( Fds );
FD_SET( SSocket, Fds );
Repeat
RFds := Fds;
checksocket( select(0, @RFds, nil, nil, nil) );
for I:=0 to RFds.fd_count -1 do
begin
if RFDs.fd_array[I] = SSocket then
begin
NewSocket := accept( SSocket, nil, nil );
if NewSocket <> INVALID_SOCKET then
begin
if Fds.fd_count < FD_SETSIZE then
FD_SET( NewSocket, Fds )
else
if CloseSocket(NewSocket) = SOCKET_ERROR then
PrintErr(WSAGetLastError, false);
end else
PrintErr(WSAGetLastError, false);
end else
DoRead(RFDs.fd_array[I]);
end;
until False;
end.
← →
kalishenko (2004-06-01 09:21) [20]А почему он неэффективен? Из-за "Repeat ... Until False"?
← →
Digitman © (2004-06-01 10:29) [21]
> почему он неэффективен?
как минимум потому что этот сервер обслуживает клиентов последовательно - всех гнезда сервера инициализируются для работы в синхронном блокирующем режиме и обработка транспортных событий этих гнезд ведется последовательно в единственном (основном) код.потоке сервер-процесса
← →
Verg © (2004-06-01 10:55) [22]
> [20] kalishenko (01.06.04 09:21)
> А почему он неэффективен? Из-за "Repeat ... Until False"?
Не, из-за
for I:=0 to RFds.fd_count -1 do
Последовательный сервер... в принципе все сказано в [21]
Страницы: 1 вся ветка
Форум: "Сети";
Текущий архив: 2004.08.01;
Скачать: [xml.tar.bz2];
Память: 0.52 MB
Время: 0.043 c