Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2007.12.02;
Скачать: CL | DM;

Вниз

TCP сервер   Найти похожие ветки 

 
Nucer   (2007-10-25 16:05) [0]

Где можно найти пример грамотно написанного многопоточного TCP сервера? Пытаюсь сам реализовать уже второй раз с нуля: вроде бы ничего сложного, но при большом числе клиентов появляются AccessViolation.


 
Eraser ©   (2007-10-25 16:06) [1]


> Nucer   (25.10.07 16:05) 

компонент TIdTCPServer, пока что нареканий не было.


 
Zeqfreed ©   (2007-10-25 16:07) [2]

apache.org :)


 
Nucer   (2007-10-25 17:06) [3]

2Eraser
А есть какой-нибудь простой пример использования TIdTCPServer?


 
atruhin ©   (2007-10-25 17:16) [4]

Что подразумеваешь под сервером?
Когда то писал, написан на WinAPI, с настраиваемым пулом потоков.
Могу кинуть на почту. Но придется переработать, т.к. модуль использовался в большом проекте.


 
Eraser ©   (2007-10-25 17:21) [5]


> Nucer   (25.10.07 17:06) [3]

http://www.atozed.com/indy/demos/10/index.ru.aspx


 
Nucer   (2007-10-25 21:49) [6]

Есть уже реализованный клиент. Общение с сервером происходит по принципу "запрос - ответ" и продолжается буквально несколько секунд (по протоколу TCP). Клиент шлет одно сообщение, сервер отвечает одним сообщением (и так 3 раза). Сообщение: первые 2 байта - длина сообщения, дальше - данные. Длина сообщений не больше 200 байт.
Требуется написать сервер. В принципе всего одно требование - должен закрывать соединения по определенному таймауту (если клиент не шлет сообщения)... ну и главное - стабильно работать.
Уже неделю как минимум времени потратил, уже 2 раза написал готовое приложение (первый раз на основе TServerSocket, второй раз просто с использованием библиотеки WinSock). В принципе работает... но работает крайне не стабильно (при большом количестве клиентом вываливается с "Access Violation".
Пытался найти в интернет каркас для подобного сервера (пример) - ничего нет.


 
Sha ©   (2007-10-25 23:01) [7]

Погугли на тему IOCompletionPort.


 
Zeqfreed ©   (2007-10-25 23:16) [8]

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <netdb.h>
#include <unistd.h>
#include <strings.h>

#define MAX_SILENCE_TIME 10

static struct ClientNode {
   int sock;
   int time;
   struct ClientNode *next;
} clients = {0, 0, 0};

void add_client(int sock)
{
   struct ClientNode *n = &clients;
   struct ClientNode *new;

   while (n->next != 0) n = n->next;

   new = malloc(sizeof(struct ClientNode));
   new->time = time(NULL);
   new->sock = sock;
   n->next = new;
}

void delete_client(struct ClientNode *parent, struct ClientNode *client)
{
       struct ClientNode *tmp = client->next;
       free(client);
       parent->next = tmp;
}

int init_sock(int port)
{
   struct sockaddr_in addr;

   int sock;
   int flags;

   //Make a socket
   if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
       perror("socket()");
       exit(1);
   }

   //Set up non-blocking mode
       if ((flags = fcntl(sock, F_GETFL, 0)) < 0) {
           fprintf(stderr, "fcntl() read failed\n");
       } else {
           flags |= O_NONBLOCK;
           if (fcntl(sock, F_SETFL, flags) < 0) {
               fprintf(stderr, "fcntl() write failed\n");
           }
       }

       //Form an address
       bzero(&addr, sizeof(struct sockaddr_in));
       addr.sin_port = htons(port);
       addr.sin_addr.s_addr = inet_addr("127.0.0.1");
       addr.sin_family = AF_INET;
   
       //Bind the socket to address formed
       if (bind(sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)) < 0) {
           perror("bind");
           exit(1);
       }
   
       //Start listening for clients
       listen(sock, 10);
   
       return sock;
}

int main(int argc, char ** argv)
{
       int sock = init_sock(10101);
       if (!sock) exit(1);
   
       struct timeval wait;
       struct sockaddr_in addr;
       fd_set fds;
       char buffer[5000];
       struct ClientNode *n;
       int msock, nsock;
       int ret, len;
       char *cip;
   
       while (1) {  
           FD_ZERO(&fds);
           FD_SET(sock, &fds);
           msock = sock;
           
           n = &clients;
           while (n->next != NULL) {
               FD_SET(n->next->sock, &fds);
               if (n->next->sock > msock) msock = n->next->sock;
               n = n->next;
           }
   
           wait.tv_sec = 1;
           wait.tv_usec = 0;
   
           ret = select(msock + 1, &fds, NULL, NULL, &wait);
           if (ret == -1) {
               perror("select()");
               exit(-1);
           } else {
               if (FD_ISSET(sock, &fds)) {
                   len = sizeof(addr);
                   nsock = accept(sock, (struct sockaddr *) &addr, &len);
                   add_client(nsock);
                   cip = (char *)inet_ntoa(addr.sin_addr);
                   fprintf(stderr, "SERVER: new client %d from %s\n", nsock, cip);
               }
               
               n = &clients;
               while (n->next != NULL) {
                   if (FD_ISSET(n->next->sock, &fds)) {
                       len = recv(n->next->sock, buffer, sizeof(buffer), 0);
                       buffer[len] = "\0";
                       fprintf(stderr, "SERVER: received data from %d: %s\n", n->next->sock, buffer);
                       n->next->time = time(NULL);
                   } else {
                       if (time(NULL) - n->next->time > MAX_SILENCE_TIME) {
                           fprintf(stderr, "SERVER: no response from %d in %d seconds. Shutting the connection down\n", n->next->sock, MAX_SILENCE_TIME);
                           shutdown(n->next->sock, SHUT_RDWR);
                           delete_client(n, n->next);    
                           continue;
                       }
                   }
                   n = n->next;
               }
               
           }
       }
   
       return 0;
}


Сойдет как каркас? :)


 
Nucer   (2007-10-25 23:30) [9]

2Zeqfreed
Да, что-то вроде того... сейчас попробую перевести на Delphi


 
Zeqfreed ©   (2007-10-26 01:52) [10]

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <netdb.h>
#include <unistd.h>
#include <strings.h>

#define MAX_SILENCE_TIME 10

static struct ClientNode {
       int sock;
       int time;
       struct ClientNode *next;
} clients = {0, 0, 0};

void add_client(int sock)
{
       struct ClientNode *n = &clients;
       struct ClientNode *new;
   
       while (n->next != 0) n = n->next;
   
       new = malloc(sizeof(struct ClientNode));
       new->time = time(NULL);
       new->sock = sock;
       new->next = NULL;
       n->next = new;
}

struct ClientNode *delete_client(struct ClientNode *client)
{    
       struct ClientNode *n = &clients;
       struct ClientNode *tmp = client->next;
       
       while (n->next != client) n = n->next;
       free(client);
       n->next = tmp;
       
       return tmp;
}

int init_sock(int port)
{
       struct sockaddr_in addr;
   
       int sock;
       int flags;
       int opt;
   
       //Make a socket
       if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
           perror("socket()");
           exit(1);
       }
   
       //Set up non-blocking mode
       if ((flags = fcntl(sock, F_GETFL, 0)) < 0) {
           fprintf(stderr, "fcntl() read failed\n");
       } else {
           flags |= O_NONBLOCK;
           if (fcntl(sock, F_SETFL, flags) < 0) {
               fprintf(stderr, "fcntl() write failed\n");
           }
       }
           
       opt = 1;
       setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));        
   
       //Form an address
       bzero(&addr, sizeof(struct sockaddr_in));
       addr.sin_port = htons(port);
       addr.sin_addr.s_addr = inet_addr("127.0.0.1");
       addr.sin_family = AF_INET;
   
       //Bind the socket to address formed
       if (bind(sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)) < 0) {
           perror("bind");
           exit(1);
       }
   
       //Start listening for clients
       listen(sock, 10);
   
       return sock;
}

int main(int argc, char ** argv)
{
       int sock = init_sock(10101);
       if (!sock) exit(1);
   
       struct timeval wait;
       struct sockaddr_in addr;
       fd_set fds;
       char buffer[5000];
       struct ClientNode *n;
       int msock, nsock;
       int ret, len;
       char *cip;
   
       while (1) {  
           FD_ZERO(&fds);
           FD_SET(sock, &fds);
           msock = sock;
           
           n = clients.next;
           while (n != NULL) {
               FD_SET(n->sock, &fds);
               if (n->sock > msock) msock = n->sock;
               n = n->next;
           }
   
           wait.tv_sec = 1;
           wait.tv_usec = 0;
   
           ret = select(msock + 1, &fds, NULL, NULL, &wait);
           if (ret == -1) {
               perror("select()");
               exit(-1);
           } else {            
               if (FD_ISSET(sock, &fds)) {
                   len = sizeof(addr);
                   nsock = accept(sock, (struct sockaddr *) &addr, &len);
                   add_client(nsock);
                   cip = (char *)inet_ntoa(addr.sin_addr);
                   fprintf(stderr, "SERVER: new client %d from %s\n", nsock, cip);
               }
           
               n = clients.next;
               while (n != NULL) {                
                   if (FD_ISSET(n->sock, &fds)) {
                       len = recv(n->sock, buffer, sizeof(buffer), 0);
                       if (len == 0) {
                           fprintf(stderr, "SERVER: client %d dropped connection\n", n->sock);
                           shutdown(n->sock, SHUT_RDWR);
                           n = delete_client(n);    
                           continue;
                       }
                       buffer[len] = "\0";
                       fprintf(stderr, "SERVER: received data from %d: %s\n", n->sock, buffer);
                       n->time = time(NULL);
                   } else {
                       if (time(NULL) - n->time > MAX_SILENCE_TIME) {
                           fprintf(stderr, "SERVER: no response from %d in %d seconds. Shutting the connection down\n", n->sock, MAX_SILENCE_TIME);
                           shutdown(n->sock, SHUT_RDWR);
                           n = delete_client(n);    
                           continue;
                       }
                   }
                   
                   n = n->next;
               }
               
           }
       }
   
       return 0;
}


Исправленная версия. А то я там чего-то накосячил с логикой списка :)
Эту версию протестировал чуть получше, вроде нормально работает. Завтра может клиент еще напишу — протестирую по полной :)


 
Slym ©   (2007-10-26 04:34) [11]

большом числе клиентов - это сколько? 16,64,256,1024 или 65536?
Проверь это :)
program CharGen;

{$APPTYPE CONSOLE}

uses
 SysUtils,ScktComp,WinSock;

type
 TServerClientThreadEx=class(TServerClientThread)
 protected
   procedure ClientExecute; override;
 end;

procedure TServerClientThreadEx.ClientExecute;
var
 Buf:array[byte] of char;
 i:integer;
begin
 try
   while (not Terminated) and ClientSocket.Connected do
   begin
     i:=ClientSocket.ReceiveBuf(Buf,SizeOf(Buf));
     if i<=0 then
       ClientSocket.Close;
     ClientSocket.SendBuf(Buf,i);
   end;
 except
   Terminate;
   HandleException;
 end;
end;

procedure GetThread(Self:TObject;Sender: TObject;ClientSocket: TServerClientWinSocket; var SocketThread: TServerClientThread);
begin
 writeln("GetThreadEvent ",ClientSocket.RemoteAddress,":",ClientSocket.RemotePort);
 SocketThread:=TServerClientThreadEx.Create(false,ClientSocket);
end;

function Proc2Method(Code, Data: Pointer):TMethod;
begin
 result.Code:=Code;
 result.Data:=Data;
end;

var Server:TServerSocket;
begin
Server:=TServerSocket.Create(nil);
try
  Server.ServerType:=stThreadBlocking;
  Server.Port:=3128;
  Server.OnGetThread:=TGetThreadEvent(Proc2Method(@GetThread,Server));
  Server.Open;
  while Server.Active do Sleep(100);
finally
  Server.Free;
end;
end.


 
Nucer   (2007-10-26 07:52) [12]

2 Slym
В минуту около 100 соединений при длительности каждого около 3х секунд

Как в вашем примере реализовать завершение соединения при отсутствии данных со стороны клиента на протяжении, к примеру, 10 секунд?


 
Сергей М. ©   (2007-10-26 08:19) [13]


> появляются AccessViolation


Никакие примеры тебе не помогут, пока не научишься пользоваться отладчиком.


 
Zeqfreed ©   (2007-10-26 09:15) [14]

Для 100 клиентов на 3 секунды создавать по потоку это круто, особенно если учесть, что в Виндоус это достаточно затратная операция.


 
Slym ©   (2007-10-29 07:50) [15]

Nucer   (26.10.07 7:52) [12]
отсутствии данных со стороны клиента


procedure TPortMapClientThread.ClientExecute;
var
 FDSet: TFDSet;
 TimeVal: TTimeVal;
 Buf:array[0..4095] of char;
 r,s:integer;
begin
 try
   RemoteSocket.Open("",FPortMap.RemoteHost,"",FPortMap.RemotePort);
   TimeVal.tv_sec := FPortMap.ClientTimeout div 1000;
   TimeVal.tv_usec := (FPortMap.ClientTimeout mod 1000) * 1000;

   while ClientSocket.Connected and RemoteSocket.Connected do
   begin
     FD_ZERO(FDSet);
     FD_SET(ClientSocket.SocketHandle, FDSet);
     FD_SET(RemoteSocket.SocketHandle, FDSet);
     if select(0, @FDSet, nil, nil, @TimeVal)>0 then

     begin
       if FD_ISSET(ClientSocket.SocketHandle, FDSet) then
       begin
         r:=ClientSocket.ReceiveBuf(Buf,Length(Buf));
         if r=0 then exit;
         s:=RemoteSocket.SendBuf(Buf,r);
         if r<>s then exit;
       end;
       if FD_ISSET(RemoteSocket.SocketHandle, FDSet) then
       begin
         r:=RemoteSocket.ReceiveBuf(Buf,Length(Buf));
         if r=0 then exit;
         s:=ClientSocket.SendBuf(Buf,r);
         if r<>s then exit;
       end;
     end else
       exit;
   end;
 finally
   RemoteSocket.Close;
 end;
end;


 
modus   (2007-10-29 08:36) [16]

Вот пример достаточно интересно  спроектированного TCP сервера, хотя, на мой взгляд, автор поторопился, из-за чего внутренности сервера получились сильно связными.

http://www.codeproject.com/useritems/vmClientServerService.asp


 
Slym ©   (2007-10-29 09:05) [17]

Zeqfreed ©   (26.10.07 9:15) [14]
Для 100 клиентов на 3 секунды создавать по потоку это круто, особенно если учесть, что в Виндоус это достаточно затратная операция.

Согласен... можно размер кэша потоков поставить побольше... и автар просил МНОГОПОТОЧНЫЙ - много и получил :)
Предложи сервер на IOComletitionPort или ни фибрах, только не на апи а на компонентах чтоб чайнику на форму кинуть и заработало


 
DVM ©   (2007-10-29 11:34) [18]


> Nucer   (25.10.07 16:05)  

поищи tinyweb - это WEB сервер на Delphi чиста на API. Написан, по-моему, теми же, кто писал The Bat!


 
VG   (2007-10-30 14:47) [19]

Уже более года занимаюсь разработкой производительного TCP сервера...

На стандартных компонентах (типа TServerSocket ) сервер вешается, когда одновременно на него сбрасывают информацию более 20 клиентов (пакеты данных около 1 КБ). Сервер выдаёт ошибки Access violation или TServerClientWinSocket TThreadError: Неверный дескриптор (6))

Проблема здесь скорее всего связана с нестабильностью WinSocket"ов...

Исследовал приведённую выше program CharGen. Та же история.

Пробую сейчас Indy и ещё некоторые спец. компоненты, написанные для игровых серверов.


 
Eraser ©   (2007-10-30 14:58) [20]


> VG   (30.10.07 14:47) [19]


> на него сбрасывают информацию более 20 клиентов

ошибка у вас в алгоритме, 20 клиентов это не нагрузка.

> Пробую сейчас Indy

хорошее решение для серверов средней производительности (несколько десятков или сотен клиентов), с т.з. программирования - удобнее всего, imho.
если количество клиентов около 500 и более, тогда лучше использовать асинхронные ICS, чтобы не плодить потоки по чем зря.


 
Anatoly Podgoretsky ©   (2007-10-30 15:07) [21]

> VG  (30.10.2007 14:47:19)  [19]

WinSocket имеет ограничение порядка 30000 сокетов, а не какие то 20
Есть реальные приложения с таким количеством сокетов, но на основе ICS
А 20 клиентов и Инди потянет.


 
Palladin ©   (2007-10-30 15:46) [22]


> Anatoly Podgoretsky ©   (30.10.07 15:07) [21]

вопрос тут возник, есть ли смысл уходить с indy на ics если я использую только TIdHTTPServer

вроде как бы с одной стороны TIdHTTPServer уже многопоточный сам по себе и получается более удобный, есть ли приемущества у ics и какие?


 
Anatoly Podgoretsky ©   (2007-10-30 16:09) [23]

> Palladin  (30.10.2007 15:46:22)  [22]

Если задачи простые и упираются в простую линейную логику, то не стоит.


 
umbra ©   (2007-10-30 16:12) [24]


> Есть реальные приложения с таким количеством сокетов, но
> на основе ICS
> А 20 клиентов и Инди потянет.

Для фанатов ицс могу сказать, что в инди можно делать сервера не на потоках, а на фибрах.


 
Slym ©   (2007-10-31 04:37) [25]

VG   (30.10.07 14:47) [19]
Исследовал приведённую выше program CharGen

А если жирное убрать?
Slym ©   (26.10.07 4:34) [11]
procedure GetThread(Self:TObject;Sender: TObject;ClientSocket: TServerClientWinSocket; var SocketThread: TServerClientThread);
begin
writeln("GetThreadEvent ",ClientSocket.RemoteAddress,":",ClientSocket.RemotePort);
SocketThread:=TServerClientThreadEx.Create(false,ClientSocket);
end;


 
VG ©   (2007-10-31 13:11) [26]

Скоро выложу на всеобщее обозрение 1 из вариантов своей версии, без лишнего кода...



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

Текущий архив: 2007.12.02;
Скачать: CL | DM;

Наверх




Память: 0.57 MB
Время: 0.017 c
3-1184763707
tiktak
2007-07-18 17:01
2007.12.02
Преимущества создания баз данных при помощи MS Access и Delphi


15-1193597756
vpbar
2007-10-28 21:55
2007.12.02
Шрифт в объекте Формула (Microsoft Equation)


6-1175356270
Tiberius
2007-03-31 19:51
2007.12.02
Как запретить TWebBrowser.GoBack


6-1175071587
inex
2007-03-28 12:46
2007.12.02
определение имени хоста по его адресу


15-1193766687
@!!ex
2007-10-30 20:51
2007.12.02
где почитать про дебагинг под Delphi