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

Вниз

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;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.56 MB
Время: 0.043 c
15-1193818424
ocean
2007-10-31 11:13
2007.12.02
EMail-дизайн


8-1170495818
Салат
2007-02-03 12:43
2007.12.02
Цвет из градиента


2-1194291948
ProgRAMmer Dimonych
2007-11-05 22:45
2007.12.02
Проблема с TFileStream


15-1193927282
oldman
2007-11-01 17:28
2007.12.02
Зашкаливает частотку монитора... :(


11-1179300786
Моторокер
2007-05-16 11:33
2007.12.02
TKOLComboBox. Добавление элементов на onDropDown





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский