Форум программистов, компьютерный форум, киберфорум
C/С++ под Linux
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.86/21: Рейтинг темы: голосов - 21, средняя оценка - 4.86
 Аватар для KuKu
1563 / 1041 / 94
Регистрация: 17.04.2009
Сообщений: 2,995

Покритикуйте сервер

21.09.2011, 23:17. Показов 4177. Ответов 38
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Добрый день! Посмотрите и скажите пожалуйста возможные ошибки/недоработки/потенциальные косяки у класса mServer. Особенно интересуют потенциальные косяки при большом количестве подлючений. Надеюсь разобраться будет легко в том, что понаписано - если что пишите, а то сам забываю что, где написано
Server.cpp - main file

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>
#include "mServer.h"
using namespace std;
 
class newServer:public mServer
{
public:
    newServer():mServer()
    {
 
    }
    virtual int mGrabberListen(int sock_id)
    {
        cout << "connect:" << sock_id << endl;
        return(0);
    }
    virtual int mGrabberRecv(char* message_in, int sock_id, int length)
    {
        cout << sock_id<< ":["<< length << "]:"<< message_in << endl;
        (*this).mSend(sock_id, message_in);
        return(0);
    }
    virtual int mGrabberClose(int sock_id)
    {
        cout << sock_id<< ":closed" << endl;
        return(0);
    }
};
int main()
{
 
    int result;
    sem_t sem1;
           sem_init(&sem1, 0, 0);
 
    newServer::size_in = 1024;
    newServer::max_connection = 64;
 
    newServer server;
 
    result = server.mBind(8080, 0);
    if (result<0) return(0);
    result = server.mListenStart();
    if (result<0) return(0);
    cout << "i'm listening" << endl;
    sem_wait(&sem1);
    return 0;
}

mServer.h

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#ifndef MSERVER_H_
#define MSERVER_H_
 
#include <string.h>
#include <stack>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/time.h>
#include <sys/types.h>
 
class mServer
{
private:
    int             listener;       // socket-listener
    int*                sock;           // client's socket
    pthread_t           threadListener;     // listener's socket
    pthread_t*          threadSock;     // client's thread
    std::stack<short>       freeSock;       // stack free sockets
    sockaddr_in         addr;           // my address
    char**              message_in;     // array incoming messages
    sem_t               sem;            // for stack synchronize
 
    int mRecv(int sock_id);                 // receive message
    int mClose(int sock_id);                // close connection
protected:
    bool isListen;                      // service working ?
    int mSend(int sock_id,char* message);           // send message
public:
    static int size_in;                 // max size incoming message's
    static int max_connection;              // max number of connections
 
    mServer();
    virtual ~mServer();
 
    int mBind(unsigned int port, char* address);
    int mListenStart();                         // start service
 
    friend void* bRecv(void* argv);                     // client's thread-receive-function
    friend void* bListen(void* argv);                   // thread-listen function
 
    virtual int mGrabberRecv(char* message_in, int sock_id, int length)     // event-function receive message
    {return(0);}
    virtual int mGrabberListen(int sock_id)                 // event-function open connection
    {return(0);}
    virtual int mGrabberClose(int sock_id)                  // event-function close connection
    {return(0);}
};
#endif

mServer.cpp

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include "mServer.h"
 
int mServer::size_in = 256;
int mServer::max_connection = 64;
 
void* bRecv(void* argv)
{
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
    mServer* cm = (mServer*) argv;
    int i = (*cm).freeSock.top();
    (*cm).freeSock.pop();
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    sem_post(&((*cm).sem));
 
    int flag = 1;
    while(flag>0)  // <0 - error, 0 - close
    {
        flag = (*cm).mRecv(i);
        if (flag>0) (*cm).mGrabberRecv((*cm).message_in[i], i, flag);
    }
    return(0);
}
void* bListen(void* argv)
{
    mServer* cm = (mServer*) argv;
    int i;
    int buf;
    while ((*cm).isListen)
    {
        buf = accept((*cm).listener, NULL, NULL);
        if (buf < 0)
        {
            (*cm).mGrabberListen(-1);
        }
        else
        {
            sem_wait(&((*cm).sem));
            pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
            i = (*cm).freeSock.top();
            (*cm).sock[i] = buf;
            if (pthread_create(&((*cm).threadSock[i]), NULL, bRecv, argv)!=0)
                return(0);
            (*cm).mGrabberListen(i);
            pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
        }
    }
    return(0);
}
mServer::mServer()
{
    isListen = false;
    sock = new int[max_connection];
    message_in = new char*[max_connection];
    threadSock = new pthread_t[max_connection];
 
    for(short i=max_connection-1;i>=0;--i)
    {
        sock[i] = 0;
        threadSock[i] = 0;
        freeSock.push(i);
        message_in[i] = new char[size_in];
    }
 
}
mServer::~mServer()
{
    delete[] sock;
    for(int i=0;i<max_connection;++i)
        delete[] message_in[i];
    delete[] message_in;
    delete[] threadSock;
    sem_destroy(&sem);
    close(listener);
}
int mServer::mBind(unsigned int port, char* address)
{
    listener = socket(AF_INET, SOCK_STREAM, 0);
    if (listener<0) return(-1);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);//htonl(0xC0A80365);
    if (bind(listener, (struct sockaddr *)&addr, sizeof(addr)) < 0) return(-1);
    if (listen(listener, max_connection)<0) return(-1);
    return(0);
}
int mServer::mSend(int sock_id, char* message)
{
    int i = strlen(message);
    message[i]='\0';
    int res = send(sock[sock_id], message,i+1,0);
    return(res);
}
int mServer::mRecv(int sock_id)
{
    int i = recv(sock[sock_id], message_in[sock_id], size_in, 0);
    if (i<=0)
    {
        mClose(sock_id);
    }
    return(i);
}
int mServer::mClose(int sock_id)
{
    close(sock[sock_id]);
    sem_wait(&sem);
    freeSock.push(sock_id);
    mGrabberClose(sock_id);
    if (threadSock[sock_id]!=0) pthread_cancel(threadSock[sock_id]);
    threadSock[sock_id] = 0;
    sock[sock_id] = 0;
    sem_post(&sem);
    return(0);
}
int mServer::mListenStart()
 {
    if (sem_init(&sem, 0, 1)!=0) return(-1);
    if(pthread_create(&threadListener, NULL, bListen, (void*)(this))!=0)
        return(-1);
    isListen = true;
    return(0);
}
0
Лучшие ответы (1)
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
21.09.2011, 23:17
Ответы с готовыми решениями:

Ввод/вывод. Покритикуйте код
Как можно это написать еще лучше (производительнее, эстетичнее, логичнее, экономнее по памяти и строкам)? char *DATA_TEXT =...

Покритикуйте - ка!:)
Покритикуйте - ка!:) www.izobret.tu1.ru

Покритикуйте
mstumaster.ru что то меня смущает в юзабилити, я уже на него смотреть не могу беспристрастно b-( ну и советы по другим...

38
Эксперт С++
 Аватар для niXman
3211 / 1459 / 74
Регистрация: 09.08.2009
Сообщений: 3,441
Записей в блоге: 2
21.09.2011, 23:53
ну... даже и не знаю с чего начать... ну во первых - велосипед.
может почитаешь для начала это?: https://www.cyberforum.ru/faq/thread317829.html
1
 Аватар для KuKu
1563 / 1041 / 94
Регистрация: 17.04.2009
Сообщений: 2,995
22.09.2011, 00:19  [ТС]
Буст да, наверное, поэффективнее написан, чем мой код) Но хотелось бы пока узнать свои заблуждения и ошибки.
0
Формучанин
364 / 296 / 42
Регистрация: 02.11.2010
Сообщений: 1,245
22.09.2011, 03:12
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
        while ((*cm).isListen)
        {
                buf = accept((*cm).listener, NULL, NULL);
                if (buf < 0)
                {
                        (*cm).mGrabberListen(-1);
                }
                else
                {
                        sem_wait(&((*cm).sem));
                        pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
                        i = (*cm).freeSock.top();
                        (*cm).sock[i] = buf;
                        if (pthread_create(&((*cm).threadSock[i]), NULL, bRecv, argv)!=0)
                                return(0);
                        (*cm).mGrabberListen(i);
                        pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
                }
        }
бесконечный цикл.

http://www.opennet.ru/man.shtm... &russian=2
нить нужно создавать либо уже detached либо использовать pthread_join()/pthread_detach()
1
1259 / 650 / 44
Регистрация: 06.02.2011
Сообщений: 1,654
22.09.2011, 09:32
Цитата Сообщение от KuKu Посмотреть сообщение
Особенно интересуют потенциальные косяки при большом количестве подлючений.
Это сплошной косяк. Модель нить на клиента при большом количестве соединений крайне малоэффективна. Запускать нить на клиента имеет смысл только в случаях когда заведомо известно что клиентов мало (сравнимо с количеством ядер cpu) и присутствует ресурсоемкая обработка данных. Если вам все равно очень хочется нитей - делайте threadpool.
Еще в дополнение сказанного другими участниками не нравиться это:
Цитата Сообщение от KuKu Посмотреть сообщение
C++
1
2
int i = strlen(message);
message[i]='\0';
1
 Аватар для KuKu
1563 / 1041 / 94
Регистрация: 17.04.2009
Сообщений: 2,995
22.09.2011, 11:39  [ТС]
Цитата Сообщение от nxnx Посмотреть сообщение
бесконечный цикл.
Ну он не совсем бесконечный) А как иначе? Считить сколько клиентов не хватает до максимума, а потом выключать цикл или что ?
Цитата Сообщение от g_u_e_s_t Посмотреть сообщение
Модель нить на клиента при большом количестве соединений крайне малоэффективна.
Можно по поводу этого линк или просто в какую сторону копать. Что используют, если не нить на клиент? - первое, что приходит а голову, обработку несколько клиентов в одну нить с неблокирующими сокетами. Это так или не угадал ?
Цитата Сообщение от g_u_e_s_t Посмотреть сообщение
Еще в дополнение сказанного другими участниками не нравиться это:
А тут что не так ?)
0
1259 / 650 / 44
Регистрация: 06.02.2011
Сообщений: 1,654
22.09.2011, 11:52
Цитата Сообщение от KuKu Посмотреть сообщение
Можно по поводу этого линк или просто в какую сторону копать. Что используют, если не нить на клиент? - первое, что приходит а голову, обработку несколько клиентов в одну нить с неблокирующими сокетами. Это так или не угадал ?
Угадал. Для старта классика: http://www.kegel.com/c10k.html

Цитата Сообщение от KuKu Посмотреть сообщение
А тут что не так ?)
я что-то упустил и где-то гарантируется, что message это Си строка?
1
 Аватар для KuKu
1563 / 1041 / 94
Регистрация: 17.04.2009
Сообщений: 2,995
22.09.2011, 11:58  [ТС]
Цитата Сообщение от g_u_e_s_t Посмотреть сообщение
я что-то упустил и где-то гарантируется, что message это Си строка?
То есть лучше ничего не ставить или \n ?
0
1259 / 650 / 44
Регистрация: 06.02.2011
Сообщений: 1,654
22.09.2011, 12:11
Нужно определиться должно это быть строкой или все-таки просто массив байт и работать с ним соответственно. Если вам нужна именно строка, то имеет смысл читать из сокета на байт меньше размера буфера и добавлять \0 к полученным данным.
1
Формучанин
364 / 296 / 42
Регистрация: 02.11.2010
Сообщений: 1,245
22.09.2011, 15:49
Цитата Сообщение от KuKu Посмотреть сообщение
Ну он не совсем бесконечный) А как иначе? Считить сколько клиентов не хватает до максимума, а потом выключать цикл или что ?
В данном коде цикл бесконечный. В качестве условия цикла нужно ставить результат accept()
Если выдал ошибку-прервать цикл. ошибка accept() будет символизировать о том что сервер надо перезапустить.

Добавлено через 12 минут
объясню по другому про strlen()


C
1
2
int i = strlen(message);
message[i]='\0';
например, если strlen() реализована так:
C
1
2
3
4
5
6
size_t strlen(const char * str)
{
    const char *s;
    for (s = str; *s; ++s);
    return(s - str);
}
а теперь я перепишу код, который в итоге выполнится:

C
1
2
3
4
5
6
const char *s;
for (s = message; *s; ++s);
//цикл закончится тогда, когда встретится нулевой символ
size_t len=(s - message);
//кстати, у нулевого символа [индекс] len, а ещё len это длина строки
message[len]='\0';//логичный вопрос, а нафига эта строка нужна?
ну и как уже было упомянуто выше, где написано что message содержит ноль?
1
725 / 224 / 73
Регистрация: 01.03.2011
Сообщений: 643
22.09.2011, 15:53
Цитата Сообщение от nxnx Посмотреть сообщение
ошибка accept() будет символизировать о том что сервер надо перезапустить.
Бред.
Представь себе апач перезапускающийся когда accept() вернет например ECONNABORTED.
0
Формучанин
364 / 296 / 42
Регистрация: 02.11.2010
Сообщений: 1,245
22.09.2011, 15:55
Цитата Сообщение от prik Посмотреть сообщение
Бред.
Представь себе апач перезапускающийся когда accept() вернет например ECONNABORTED.
возможно не так выразился. Я привёл пример что можно сделать.
0
725 / 224 / 73
Регистрация: 01.03.2011
Сообщений: 643
22.09.2011, 15:59
По моему, единственное что там надо сделать, это явно написать while (1) чтоб никого не смущать никогда не меняющимся условием.
0
 Аватар для KuKu
1563 / 1041 / 94
Регистрация: 17.04.2009
Сообщений: 2,995
23.09.2011, 18:26  [ТС]
Цитата Сообщение от g_u_e_s_t Посмотреть сообщение
Угадал. Для старта классика: http://www.kegel.com/c10k.html
Статейка занятная, но немного голова кругом. Правильно ли понял, что даже при использование неблокирующих сокетов, лучше иметь несколько потоков, каждый из которых обрабатывает свою группу сокетов?
0
1259 / 650 / 44
Регистрация: 06.02.2011
Сообщений: 1,654
23.09.2011, 19:09
Цитата Сообщение от KuKu Посмотреть сообщение
Правильно ли понял, что даже при использование неблокирующих сокетов, лучше иметь несколько потоков, каждый из которых обрабатывает свою группу сокетов?
Вы слишком "радикально" формулируете вопрос. Все зависит от того, что именно сервер делает и какие требования вы к нему предъявляете.
Если рассматривать ваш случай (echo-server), то думаю до 10K клиентов можно спокойно держать в одну нить и до некого числа клиентов будет выигрывать у варианта с нитями. Он и 100К конечно сможет, но латентность будет совсем дикая. Соответственно вариант с пулом нитей (по нитке на ядро cpu) будет эффективней.
1
 Аватар для KuKu
1563 / 1041 / 94
Регистрация: 17.04.2009
Сообщений: 2,995
23.09.2011, 19:28  [ТС]
А латентность в каком смысле? - Клиенты, которые бездействуют или клиент что-то отправил и ждет пока до него срок дойдет?
Вот, если к примеру взять какой-нибудь ирк сервер. Грубо говоря там 5к людей, три четверти висят мертвым грузом, четверть раз в 10 секунд отправляют по 200 байт. Какую структуру выбрать ? 1 нить, 10 нитей, 100 нитей или только опытным путем? Адекватно ли будет, если я на своем компьютере создам сервер и 5к клиентов и буду долбиться всеми клиентами в сервер?
0
1259 / 650 / 44
Регистрация: 06.02.2011
Сообщений: 1,654
23.09.2011, 19:38
Цитата Сообщение от KuKu Посмотреть сообщение
А латентность в каком смысле?
В смысле времени которое пройдет между отправкой сообщения клиентом и получением им ответа от сервера.
Цитата Сообщение от KuKu Посмотреть сообщение
Клиенты, которые бездействуют
Они серверу не страшны т.к. процесора не хотят.
Цитата Сообщение от KuKu Посмотреть сообщение
Адекватно ли будет, если я на своем компьютере создам сервер и 5к клиентов и буду долбиться всеми клиентами в сервер?
Затрудняюсь, я не читал стандарт IRC протокола. Но 5к могу вполне себе жить будут, а сильно агрессивных клиентов мешающих жить серверу наверно можно отстреливать))) (повторюсь стандарт не читал)

Up: про структуру. Пишите в одну нить, но так, чтоб легким движением рук превращалось в мульти.
1
Временно недоступен
 Аватар для #pragma
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
24.09.2011, 02:08
Вот тут написано, что высокопроизводительные сервера не используют нити :
http://ru.wikipedia.org/wiki/%... 0%B8%D0%B9
Я где-то на сайте nginx видел ссылки на документы, про то, как это всё делается (вроде тут пишут http://www.kegel.com/c10k.html)

P.S. Не увидел,что ссылку эту уже дал g_u_e_s_t
1
Эксперт С++
 Аватар для niXman
3211 / 1459 / 74
Регистрация: 09.08.2009
Сообщений: 3,441
Записей в блоге: 2
24.09.2011, 02:29
Лучший ответ Сообщение было отмечено как решение

Решение

#pragma, существует еще такое понятие как сопрограммы. конкретно в примерах asio, демонстрируется использование сопрограмм и одного потока для реализации безстекового http-сервера. следует отметить тот факт, что на основе сопрограмм, реализуются микропотоки, кол-во которых может превышать 1000000 без затрат на переключение контекста. в то время как используя потоки, при кол-ве 20000, четырехядерная машина умирает только на переключениях контекста.

от себя скажу, что на основе сопрограмм, реализовывал многопоточную машину состояний способную отслеживать состояние ~300000 объектов, в одном потоке, без расходов холостого хода.
3
1259 / 650 / 44
Регистрация: 06.02.2011
Сообщений: 1,654
24.09.2011, 09:35
Цитата Сообщение от #pragma Посмотреть сообщение
Вот тут написано, что высокопроизводительные сервера не используют нити :
Это не правда. Если машина многоядерная, то глупо жить на одном ядре. Тот же nginx вполне себе умеет нити.
Цитата Сообщение от niXman Посмотреть сообщение
существует еще такое понятие как сопрограммы. конкретно в примерах asio, демонстрируется использование сопрограмм и одного потока для реализации безстекового http-сервера.
На юниксах это makecontext()/swapcontext() ?
Цитата Сообщение от niXman Посмотреть сообщение
отслеживать состояние ~300000 объектов,
Каких-то своих объектов или объектов ОС?

2ТС: посмотрел на IRC, имхо там основная тяжесть не собственно I/O с клиентами, а взаимодействие с другими серверами.
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
24.09.2011, 09:35
Помогаю со студенческими работами здесь

Покритикуйте
нужна критика по дизайну http://www.unitytrans.ru/

Покритикуйте
Приветствую всех. В сфере продвижения и оптимизации я относительно недавно. Сайт прогнал по каталогам, купил несколько легеньких ссылок,...

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

Покритикуйте код
В продолжение моей прошлой темы. Накидал тут игрушку на swing 2048, воспользовался советом который дали другому пользователю. Раньше я уже...

Покритикуйте код
Добрый вечер. Выполнял одно тестовое задание (уже получил отрицательный ответ), буду рад, если скажите все ошибки, а также, как их можно...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
SDL3 для Desktop (MinGW): Вывод текста со шрифтом TTF с помощью библиотеки SDL3_ttf на Си и C++
8Observer8 24.03.2026
Содержание блога Финальные проекты на Си и на C++: finish-text-sdl3-c. zip finish-text-sdl3-cpp. zip
Жизнь в неопределённости
kumehtar 23.03.2026
Жизнь — это постоянное существование в неопределённости. Например, даже если у тебя есть список дел, невозможно дойти до точки, где всё окончательно завершено и больше ничего не осталось. В принципе,. . .
Модель здравоСохранения: работники работают быстрее после её введения.
anaschu 23.03.2026
geJalZw1fLo Корпорация до введения программа здравоохранения имела много невыполненных работниками заданий, после введения программы количество заданий выросло. Но на выплатах по больничным это. . .
1С: Контроль уникальности заводского номера
Maks 23.03.2026
Алгоритм контроля уникальности заводского (или серийного) номера на примере документа выдачи шин для спецтехники с табличной частью. Данные берутся из регистра сведений, по которому настроено. . .
Хочу заставить корпорации вкладываться в здоровье сотрудников: делаю мат модель здравосохранения
anaschu 22.03.2026
e7EYtONaj8Y Z4Tv2zpXVVo https:/ / github. com/ shumilovas/ med2. git
1С: Программный отбор элементов справочника по группе
Maks 22.03.2026
Установка программного отбора элементов справочника "Номенклатура" из модуля формы документа. В качестве фильтра для отбора справочника служит группа номенклатуры. Отбор по наименованию группы. . .
Как я обхитрил таблицу Word
Alexander-7 21.03.2026
Когда мигает курсор у внешнего края таблицы, и нам надо перейти на новую строку, а при нажатии Enter создается новый ряд таблицы с ячейками, то мы вместо нервных нажатий Энтеров мы пишем любые буквы. . .
Krabik - рыболовный бот для WoW 3.3.5a
AmbA 21.03.2026
без регистрации и смс. Это не торговля, приложение не содержит рекламы. Выполняет свою непосредственную задачу - автоматизацию рыбалки в WoW - и ничего более. Однако если админы будут против -. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru