Select TCP Server на WinSock

Zakke
Дата: 28.04.2011 20:42:47
Пытаюсь написать сервер на основе сокетов. Сервер должен принимать сообщение от клиента, затем отправлять клиенту это же сообщение и отключать клиента.
Сервер запускается, пытаюсь подключиться с помощью клиента на данный сервер, но сервер, видимо, не обрабатывает подключение...

В чем может быть проблема?


program SelectServer;
{$APPTYPE CONSOLE}
uses
  WinSock,
  SysUtils,
  Windows;

var
  sock : array [1..FD_SETSIZE] of TSocket;
  WSA: WSAData;
  addr: TSockAddr;
  VListenSocket,vsocket : TSocket;
  VSockaddr : TSockAddr;
  i: integer;
  on_sock: integer = 1;
  off_sock: integer = 0;
  FDS: TFDSet;
  TV: TTimeVal;

const
  CPort = Word(12345);

procedure ServingClient(s:TSocket);
var
  sockname : tsockaddr;
  abuf : array of char;
  vbuf : string;
  vsize : integer;
  bufsize : integer;
  arg: integer;
begin
  if s = invalid_socket then exit;
  vsize := sizeof(tsockaddr);
  getpeername(s, sockname, vsize);
  writeln(format('client accepted, remote address [%s].',[inet_ntoa (sockname.sin_addr)]));

  recv(s,vbuf,SizeOf(vbuf),0);
  writeln('recv -',vbuf);

  send(s,vbuf,SizeOf(vbuf),0);
  writeln('send - ', vbuf);

  writeln(format('client disconnected, remote address [%s].',[inet_ntoa(sockname.sin_addr)]));
  ioctlsocket(s,FIONBIO,off_sock);
  closesocket(s);
end;

begin
  WriteLn('starting application...');

  if WSAStartup($101,WSA)<>0 then halt(1);
  VListenSocket := socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
  WriteLn(format('creating socket on port [%d].',[CPort]));

  if VListenSocket = invalid_socket then halt(1);
  FillChar(VSockAddr,SizeOf(TSockAddr),0);
  VSockAddr.sin_family := AF_INET;
  VSockAddr.sin_port := htons(CPort);
  VSockAddr.sin_addr.s_addr := inaddr_any;

  if bind(VListenSocket,VSockAddr,SizeOf(TSockAddr)) <> 0
  then halt(1);
  WriteLn('binding socket...');
  if listen(VListenSocket,SoMaxConn) <> 0
  then halt(1);
  WriteLn('socket status: listening.');

  TV.tv_sec:=0;
  TV.tv_usec:=0;

  repeat
    FD_ZERO(FDS);
    for i:=1 to FD_SETSIZE do
    begin
      sock[i]:=socket(AF_INET,SOCK_STREAM,0);
      ioctlsocket(sock[i],FIONBIO,on_sock);
      addr.sin_family:=AF_INET;
      addr.sin_port:=htons(Cport);
      addr.sin_addr.s_addr:=inaddr_any;
      FD_SET(sock[i],FDS);
    end;

    select(0,@FDS,nil,nil,@TV);
    if FDS.fd_count=0 then continue;
    for i:=0 to FDS.fd_count-1 do
    begin
      ServingClient(FDS.fd_array[i]);
    end;
  until false;

end.
Zakke
Дата: 28.04.2011 21:55:05
Видимо, ошибка в цикле for i:=1 to FD_SETSIZE do... ?
Фактически ведь сервер ждет ( по умолчанию) 64 клиента, а подключается всего один... А как можно в этот момент узнать число клиентов, которые хотят подключиться?
Zakke
Дата: 29.04.2011 23:31:49
немного переписал, получилось такое (нашел пример в инете):
program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Windows,
  WinSock;

var
  Sockets:array of TSocket;
  Addr:TSockAddr;
  Data:TWSAData;
  Len,I,J:Integer;
  FDSet:TFDSet;
  buf : string;

const
  CPort = Word(12345);

begin
  WriteLn('starting application...');
  if WSAStartup($101,Data) <> 0 then halt(1);
  SetLength(Sockets,1);
  Sockets[0]:=Socket(AF_Inet,Sock_Stream,0);
  WriteLn(format('creating socket on port [%d].',[CPort]));
  if Sockets[0] = invalid_socket then halt(1);
  Addr.sin_family:=AF_Inet;
  Addr.sin_port:=htons(CPort);
  Addr.sin_addr.S_addr:=InAddr_Any;
  FillChar(Addr.Sin_Zero,SizeOf(Addr.Sin_Zero),0);
  if bind(Sockets[0],Addr,SizeOf(TSockAddr)) <> 0 then halt(1);
  WriteLn('binding socket...');
  if listen(Sockets[0],SoMaxConn) <> 0 then halt(1);
  WriteLn('socket status: listening.');

  while True do
  begin
    // 1. Формирование множества сокетов
    FD_Zero(FDSet);
    for I:=0 to High(Sockets) do
      FD_Set(Sockets[I],FDSet);
    // 2. Проверка готовности сокетов
    Select(0,@FDSet,nil,nil,nil);
    // 3. Чтение запросов клиентов тех сокетов, которые готовы к этому
    I:=1;
    while I<=High(Sockets) do
    begin
      if FD_IsSet(Sockets[I],FDSet) then
        if Recv(Sockets[I],buf, SizeOf(buf),0)<=0 then
        begin
          // Связь разорвана, надо закрыть сокет
          // и удалить его из массива
          CloseSocket(Sockets[I]);
          for J:=I to High(Sockets)-1 do
            Sockets[J]:=Sockets[J+1];
          Dec(I);
          SetLength(Sockets,Length(Sockets)-1)
         end
         else
         begin
           writeln('recv -',buf);
           Send(Sockets[I],buf, SizeOf(buf), 0);
           writeln('send - ', buf);
         end;
        Inc(I)
      end;
      // 4. Проверка подключения нового клиента
      if FD_IsSet(Sockets[0],FDSet) then
      begin
        // Подключился новый клиент
        SetLength(Sockets,Length(Sockets)+1);
        Len:=SizeOf(TSockAddr);
        Sockets[High(Sockets)]:=Accept(Sockets[0],@Addr,@Len)
      end;
    end;
end.

Как только пытаюсь подключиться клиентом к серверу и отправить сообщение на сервер, клиент вылетает с сообщением:
"Ошибка при получении ответа от сервера: удаленный хост принудительно разорвал существующее подключение".
При этом, сервер вылетает и делфи указывает на строку: I:=1; (цикл для чтения запросов)
В чем может быть проблема?
Zakke
Дата: 04.05.2011 17:15:07
up