Сервер виснет после первого соединения
От: Alex_v99 Россия  
Дата: 19.03.09 13:36
Оценка:
Добрый день.

Я переписал из учебника простейший сервер:

    int sock, listener;
    struct sockaddr_in addr;
    char buf[1024];
    int bytes_read;

    listener = socket(AF_INET, SOCK_STREAM, 0);
    if(listener < 0)
    {
        perror("socket");
        exit(1);
    }
    
    addr.sin_family = AF_INET;
    addr.sin_port = htons(3425);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    if(bind(listener, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    {
        perror("bind");
        exit(2);
    }

    listen(listener, 5);
    
    while(1)
    {
        sock = accept(listener, NULL, NULL);
        if(sock < 0)
        {
            perror("accept");
            exit(3);
        }

        while(1)
        {
            bytes_read = recv(sock, buf, 1024, 0);
            if(bytes_read <= 0) break;
            
            cout << buf << endl;
        }
    
        close(sock);
    }


Проблема в том, что первое соединение ловится и корректно обрабатывает всё принимаемое, а вот после разрыва соединения клиентом заново соединиться уже не получается. Судя по логам, сервер после первого дисконнекта снова повисает в "sock = accept(listener, NULL, NULL);", но уже новые соединения не видит.

Как бы мне сделать так, чтобы сервер последовательно (!) обрабатывал подключения клиентов? Т.е. как только один клиент закончил соединение, сервер сразу же мог бы соединиться с новым клиентом.

Спасибо.
WBR, Alex.
Re: Сервер виснет после первого соединения
От: stbzh  
Дата: 19.03.09 16:29
Оценка:
А точно подвисает на accept? Я бы предположил, что ты некорректно закрываешь соединение на клиенте и и з-за этого recv блокируется. Если это винда и все навтройки выстроены по умолчанию (не изменено время ожидания и соединение не отслеживает таймаут), то несколько минут винда будет думать, что соединение живое и recv не вернет 0.
Re[2]: Сервер виснет после первого соединения
От: Alex_v99 Россия  
Дата: 20.03.09 09:20
Оценка:
Здравствуйте, stbzh, Вы писали:

S>А точно подвисает на accept? Я бы предположил, что ты некорректно закрываешь соединение на клиенте и и з-за этого recv блокируется. Если это винда и все навтройки выстроены по умолчанию (не изменено время ожидания и соединение не отслеживает таймаут), то несколько минут винда будет думать, что соединение живое и recv не вернет 0.


Сервер под Debian etch4, клиент под Windows XP, связь через локалку. Настройки везде умолчальные. Сервер на 99% висит в accept — я логи после каждого действия в cout вывожу (выше я их постирал, чтоб не засоряли код). Да, такая деталь — сервер крутится не в основном потоке программы, а в отдельно созданном параллельном main'у.

Я и сам подозреваю, что проблема в клиенте, но вот где именно?..

Вот код клиента:

    pSocket = new CSocket();
    CString Str;
    Str = "192.168.0.22";
    int nPort = 3425;

    pSocket->Create(nPort, SOCK_STREAM, NULL);

    pSocket->Connect(Str, nPort);

    Str = "SESSION BEGIN AB5643F8865CD0\n";
    pSocket->Send(Str.GetBuffer(Str.GetLength()), Str.GetLength(), 0);
    Str = "GET STATE AB5643F8865CD0\n";
    pSocket->Send(Str.GetBuffer(Str.GetLength()), Str.GetLength(), 0);

    pSocket->Close();
    
    delete pSocket;
    pSocket = NULL;


Вроде всё по учебнику, а не работает.
WBR, Alex.
Re: Сервер виснет после первого соединения
От: TarasCo  
Дата: 21.03.09 09:37
Оценка:
A_>
A_>        close(sock);
A_>


Это в общем случае UB, надо closesocket делать
Да пребудет с тобою сила
Re: Сервер виснет после первого соединения
От: andreytsb Украина  
Дата: 21.03.09 13:43
Оценка:
Здравствуйте, Alex_v99, Вы писали:

A_>Добрый день.


A_>Я переписал из учебника простейший сервер...



В идеальных условиях (на локальной машине) даже при одновременном подключении нескольких клиентов (с помощью telnet) сервер обработал всех поочередно: как только отсоединялся текущий — обрабатывалось следующее соединение).
Проблема очень интересная. Чтобы полностью разобратся нужно лицезреть полный код клиента и сервера.
Re[2]: Сервер виснет после первого соединения
От: Alex_v99 Россия  
Дата: 23.03.09 13:48
Оценка:
TarasCo:

TC>Это в общем случае UB, надо closesocket делать


Простите, но что есть "UB" и где взять функцию closesocket? В каталоге <include/*.h> её нету, и компилятор её не знает.

--------------------------------------------------------
andreytsb:

A>В идеальных условиях (на локальной машине) даже при одновременном подключении нескольких клиентов (с помощью telnet) сервер обработал всех поочередно: как только отсоединялся текущий — обрабатывалось следующее соединение).

A>Проблема очень интересная. Чтобы полностью разобратся нужно лицезреть полный код клиента и сервера.

Вот код сервера, собран с помощью GNU GCC под Debian Etch4:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <iostream>

int main()
{
    int sock, listener;
    struct sockaddr_in addr;
    char buf[1024];
    int bytes_read;

    listener = socket(AF_INET, SOCK_STREAM, 0);
    if (listener < 0)
    {
        return 1;
    }

    addr.sin_family = AF_INET;
    addr.sin_port = htons(3425);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(listener, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    {
        return 2;
    }
    while (1)
    {
        listen(listener, 1);

        while(1)
        {
            sock = accept(listener, NULL, NULL);
            if (sock < 0)
            {
                return 3;
            }

            while(1)
            {
                bytes_read = recv(sock, buf, 1024, 0);

                if (bytes_read <= 0) break;
                    send(sock, buf, bytes_read, 0);

                std::cout << buf << std::endl;
            }

            close(sock);
        }
    }

    return 0;
}


Клиент, который собственно не работает. Собран под MSVC6.0 + MFC в виде простейшего диалога с кнопкой. Код выполняется по нажатию на эту кнопку. Первое нажатие отрабатывает как положено, сервер всё принимает/отсылает/печатает, второе и последующие нажатия — никакой реакции сервера. В отладчике видно, что pUM_Socket->Connect() возвращает 0.

void CUmEmulatorDlg::OnButtonConnect() 
{
    if (pUM_Socket) // переменная объявлена как CSocket* CUmEmulatorDlg::pUM_Socket;
    {
        AfxMessageBox("Already connected!");
        return;
    }

    pUM_Socket = new CSocket();
    CString Str;
    Str = "192.168.0.22";
    int nPort = 3425;

    pUM_Socket->Create(nPort, SOCK_STREAM, NULL);
    pUM_Socket->Connect(Str, nPort);

    Str = "SESSION BEGIN AB5643F8865CD0\n";
    pUM_Socket->Send(Str.GetBuffer(Str.GetLength()), Str.GetLength(), 0);
    pUM_Socket->Receive(Str.GetBuffer(Str.GetLength()), Str.GetLength(), 0);

    Str = "GET STATE AB5643F8865CD0\n";
    pUM_Socket->Send(Str.GetBuffer(Str.GetLength()), Str.GetLength(), 0);
    pUM_Socket->Receive(Str.GetBuffer(Str.GetLength()), Str.GetLength(), 0);

    pUM_Socket->Close();
    
    delete pUM_Socket;
    pUM_Socket = NULL;
}


Для проверки написал простенький аналог этого клиента под Debian, но запустить его, к сожалению, могу лишь локально с сервером, а по реальной сети запускать его не на чем. Этот клиент работает корректно, сколько бы раз его ни запускали, при том, что он сам по себе делает пяток независимых соединений:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <iostream>

char message[] = "Hello there!\n";
char buf[sizeof(message)];

int main()
{
    int sock;
    struct sockaddr_in addr;

    for (int i=0; i<5; i++)
    {    sock = socket(AF_INET, SOCK_STREAM, 0);
        if(sock < 0)
        {
          return 1;
        }

        addr.sin_family = AF_INET;
        addr.sin_port = htons(3425);
        addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
        if(connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
        {
         return 2;
        }

        send(sock, message, sizeof(message), 0);
        recv(sock, buf, sizeof(message), 0);

        std::cout << buf << std::endl;
        close(sock);
    }
    return 0;
}


======================================================
Пока писал этот текст, провёл несложный эксперимент:
Запустил сервер. Подключился удалённым клиентом несколько раз. Как и раньше, получилось лишь единожды. После этого, не выключая сервер, запустил локальный клиент и сервер корректно обработал его запросы.

Очевидно, что либо клиент под MFC работает некорректно, либо сервер как-то иначе обрабатывает именно loopback-соединения (вообще-то он и знать об этом не должен, но мало ли...).

Может у кого-нибудь есть мысли, где тут собака порылась?..
WBR, Alex.
Re[3]: Сервер виснет после первого соединения
От: andreytsb Украина  
Дата: 23.03.09 14:30
Оценка:
Здравствуйте, Alex_v99, Вы писали:

A_>Может у кого-нибудь есть мысли, где тут собака порылась?..



Попробуй переписать код клиента без использования сокетов MFC.
Мне кажется, что с ними не все чисто.
Re[4]: Сервер виснет после первого соединения
От: Alex_v99 Россия  
Дата: 24.03.09 10:48
Оценка:
Здравствуйте, andreytsb, Вы писали:

A>Попробуй переписать код клиента без использования сокетов MFC.

A>Мне кажется, что с ними не все чисто.

Странно всё это и непонятно. Раньше я писал сервер-клиенты под MFC и они прекрасно работали, а тут впервые полез писать под линукс и такая непонятка.
WBR, Alex.
Re[4]: Сервер виснет после первого соединения
От: Alex_v99 Россия  
Дата: 24.03.09 12:14
Оценка:
Здравствуйте, andreytsb, Вы писали:

A>Попробуй переписать код клиента без использования сокетов MFC.

A>Мне кажется, что с ними не все чисто.

BINGO! Переписал без MFC и всё сразу заработало как и положено. Интересно, почему же такой глюк у MFC приключился?

Проблема решена. Спасибо!
WBR, Alex.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.