Форум: "Прочее";
Текущий архив: 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.042 c