Модераторы: feodorv
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Ошибка accept(), Не получается подключится к порту 2 раз 
V
    Опции темы
VitaminCPP
Дата 16.1.2008, 23:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 10
Регистрация: 26.10.2007

Репутация: нет
Всего: нет



Есть сервер, есть клиент. Дескриптор сокета хранится в глобальной переменной sock. Вообще, куча глобальных данных, так надо. Если видите переменную и она не обьявлена в коде, значит глобальная. Сервер и клиент запускаются в новом потоке. 

Сервер:

Код

// Creates socket and sets it to be a listening socket
// Если пользователь в диалоге указал интерфейс, его адресс хранится в Address 
// и соответсвенно этот инферсейс используется, иначе INADDR_ANY
void SetUpListener()
{
    u_long nInterfaceAddr = INADDR_ANY;

    if (Address!=0 && 
        (nInterfaceAddr = inet_addr(Address)) == INADDR_NONE) 
    { sock = INVALID_SOCKET; return; }

    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock != INVALID_SOCKET)
    {
        sockaddr_in sinInterface;
        sinInterface.sin_family = AF_INET;
        sinInterface.sin_addr.s_addr = nInterfaceAddr;
        sinInterface.sin_port = htons(Port);
            
        if (bind(sock, (sockaddr*)&sinInterface, 
            sizeof(sockaddr_in)) !=    SOCKET_ERROR) 
            listen(sock, 1);
    }
}

// The main Server function, calls other functions written above
DWORD WINAPI DoServer (LPVOID lParam)
{
    StatusText(0, "Establishing the listener...");
    SetUpListener();
    if (sock == INVALID_SOCKET) 
    {
        StatusText(0, "Failed to establish the listener");
        return 1;
    }

    StatusText(0, "Waiting for a connection...");
    sockaddr_in sinRemote;
    int nAddrSize = sizeof(sinRemote);
    sock = accept(sock, (sockaddr*)&sinRemote, &nAddrSize);
    if (sock != INVALID_SOCKET)
    {
        StatusText(0, "Connection accepted!");
        std::stringstream buf; 
        buf << "Playing with " << inet_ntoa(sinRemote.sin_addr) 
            << ":" << ntohs(sinRemote.sin_port);
        StatusText(1, buf.str().c_str());
    } else {
        StatusText(0, "Failed accept connection");
        return 1;
    }

    return 1;
}
// этот код вызывается из другого места, не буду вдаваться в подробности проекта.
// важно, что я его вызываю перед повторным созданием сервера.
void ShutDownConnection()
{
    if (shutdown(sock, SD_SEND) == SOCKET_ERROR) return;

    if (closesocket(sock) == SOCKET_ERROR) return;
    return;
}


Клиент:

Код

void EstablishConnection(u_long RemoteAddr)
{
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock != INVALID_SOCKET) 
    {
        sockaddr_in sinRemote;
        sinRemote.sin_family = AF_INET;
        sinRemote.sin_addr.s_addr = RemoteAddr;
        sinRemote.sin_port = htons(Port);

        if (connect(sock, (sockaddr*)&sinRemote, sizeof(sockaddr_in)) ==
            SOCKET_ERROR) sock = INVALID_SOCKET;
    }
}

DWORD WINAPI DoClient (LPVOID lParam)
{
    // Getting host address
    u_long RemoteAddr = inet_addr(Address);
    if (RemoteAddr == INADDR_NONE)
    {
        StatusText(0, "Wrong IP-address or port!");
        return 1;
    }

    // Establish connection to the host
    in_addr Address;
    memcpy(&Address, &RemoteAddr, sizeof(u_long));
    std::stringstream buf;
    buf << "Connecting to " << inet_ntoa(Address) << ":" << Port;
    StatusText(0, buf.str().c_str());

    EstablishConnection(RemoteAddr);
    if (sock == INVALID_SOCKET)
    {
        StatusText(0, "Connection to server failed!");
        return 1;
    }
    buf.str("");
    buf << "Playing with " << inet_ntoa(Address) << ":" << Port;
    StatusText(1, buf.str().c_str());
    StatusText(0, "Connected!");

    return 1;
}

//Код закрытия сокета см. у сервера, для клиента вызыается тот же код.


Вобщем, проблема в следующем:
Я запускаю сервер, подключаюсь к нему клиентом через localhost. В глобальной сети программа не тестировалась. Потом, если я закрываю сокеты у клиента и сервера и пытаюсь подключить сокет сервера снова к тому же порту (тоесть запускаю сервер заново), получаю в статус баре строку Failed accept connection(см. вызов accept() у сервера). Код ошибки WSAEINVAL (10022). Другие порты работают. Если я не подключаю клиент, а просто создаю подключение сервера к порту, удаляю его и снова создаю, всё работает.

Windows XP, SP2
Visual Studio 2008

Как мне освобождать порт?
PM MAIL   Вверх
onsql
Дата 16.1.2008, 23:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 94
Регистрация: 3.12.2007

Репутация: нет
Всего: 3



Ну, вообще говоря, в функции void SetUpListener() никак не сигнализируется результат выполнения bind и listen. Могу предположить что во второй запуск bind возвращает что то типа address already in use.
PM MAIL   Вверх
VitaminCPP
Дата 17.1.2008, 00:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 10
Регистрация: 26.10.2007

Репутация: нет
Всего: нет



Почему не сигнализируется? Есть ведь проверка на SOCKET_ERROR в самой функции и на INVALID_SOCKET после вызова.
Я файрволом проверил... Он показывает, что программа закрывает сокет на самом деле. Когда запускаю сервер показывает что порт открыт, когда вызываю функцию - что закрыт... Блин, где же проблема...
PM MAIL   Вверх
onsql
Дата 17.1.2008, 00:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 94
Регистрация: 3.12.2007

Репутация: нет
Всего: 3



В упор не вижу где тут печать об ошибке если bind вернет SOCKET_ERROR
Код

...
    if (sock != INVALID_SOCKET)
    {
        sockaddr_in sinInterface;
        sinInterface.sin_family = AF_INET;
        sinInterface.sin_addr.s_addr = nInterfaceAddr;
        sinInterface.sin_port = htons(Port);
            
        if (bind(sock, (sockaddr*)&sinInterface, 
            sizeof(sockaddr_in)) !=    SOCKET_ERROR) 
            listen(sock, 1);
    }
...


Добавлено через 1 минуту и 8 секунд
Файрвол - это хорошо. А он показывает TCP соединения в состоянии FIN_WAIT, CLOSE_WAIT?
PM MAIL   Вверх
Олег2005
Дата 17.1.2008, 11:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Завсегдатай
Сообщений: 421
Регистрация: 26.5.2005
Где: Рига Латвия

Репутация: 6
Всего: 11



3 замечания:
 listen(sock, 1); - может вызывать проблемы
backlog не ставят 1 - может неверно формироваться очередь принятых соединений
 listen(sock, 5); - это минимально для всех видов стека
второе:
ShutDownConnection()
Зря там такой код - нужно просто делать closesocket()
Третье:
Для того чтобы выключать и быстро включать сервер без проблем надо на сервере для прослушивающего сокета ставить опцию SO_REUSEADDR
PM MAIL WWW MSN   Вверх
onsql
Дата 17.1.2008, 12:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 94
Регистрация: 3.12.2007

Репутация: нет
Всего: 3



Если посмотреть на код в самом первом посте, то, думаю, тут происходит следующее:
1. Вызов DoServer. Из него первым делом вызывается SetUpListener()
2. В SetUpListener создается сокет, допустим успешно, переменная sock проинициализирована, не равна INVALID_SOCKET
3. В SetUpListener Вызывается bind, который, допустим, возвращает ошибку address already in use. В этом случае, listen не будет вызван. Никакой печати об этом нет, никаких действий не предпринимается.
4. Далее выход из SetUpListener, возврат в DoServer где проверяется sock на значение INVALID_SOCKET. Но оно в порядке, сокет создан, однако bind на требуемый порт не сделан, listen не вызван.
5. Далее по коду DoServer, поскольку дескриптор сокета валидный, на него делается accept который возвращает ошибку из за условий описанных в п.4.

Вот такой сценарий и приводит видимо к описанной автором темы ситуации.
PM MAIL   Вверх
VitaminCPP
Дата 17.1.2008, 20:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 10
Регистрация: 26.10.2007

Репутация: нет
Всего: нет



onsql, ты прав. проблема в bind, ошибка Address already in use.
Попробовал поставить в опциях SO_REUSEADDR , сервер запускается сразу, клиент подключается, но данные не посылаются. 
Но я уже на верном пути, знаю точно что за ошибка. Тему пока не помечаю как решенную, может еще кто-то чего посоветует...
PM MAIL   Вверх
GrayCardinal
Дата 18.1.2008, 20:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Фигасе
****


Профиль
Группа: Завсегдатай
Сообщений: 3039
Регистрация: 9.11.2003

Репутация: 3
Всего: 58



Цитата

Но я уже на верном пути, знаю точно что за ошибка. Тему пока не помечаю как решенную, может еще кто-то чего посоветует... 

 smile 
"Омская 1" рулит smile
 smile 


--------------------
PM MAIL WWW   Вверх
VitaminCPP
Дата 21.1.2008, 00:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 10
Регистрация: 26.10.2007

Репутация: нет
Всего: нет



ладно, всё.
PM MAIL   Вверх
GremlinProg
Дата 23.1.2008, 17:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 1
Всего: 106



Насоветовали много, в деталях, по ошибкам, а главное - упустили.

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

Можно, конечно, использовать несколько серверов, но тогда уж и делить их нужно по портам (даже биндить можно на один адрес, но порты должны отличаться), в общем, это не так удобно и просто, как кажется. В развитии темы: даже в таком случае, при разделенных портами серверов, должен быть один - связующий, который раскидывает клиентов по свободным серверам, такая система даже не оправдывает свою себестоимость

По поводу портов: они будут свободны, пока не заняты )
Второй экземпляр сервера на том же адресе и том же порту в принципе не возможен до тех пор, пока первый экземпляр не закроется, отсюда, кстати, и происходят конфликты различных сетевых приложений, к примеру: проксирование на "общеизвестных" портах приводит к невозможности запуска различных "общеизвестных" служб, типа ftp, http, ssh, telnet и т.п.


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
VitaminCPP
Дата 25.1.2008, 00:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 10
Регистрация: 26.10.2007

Репутация: нет
Всего: нет



GremlinProg, у моего приложения на один сервер может быть только один клиент. Игра 1 на 1. Всё продумано  smile 
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Сети | Следующая тема »


 




[ Время генерации скрипта: 0.0528 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.