Jump to content

    

Проблемы с lwIP стеком

Здравствуйте, уважаемые форумчане!

Некоторое время назад передо мной была поставлена задача – разработать GPS/ГЛОНАСС трекер с поддержкой специфичного протокола обмена данными поверх TCP (данный протокол уже реализован на удаленном узле на Linux, а я просто должен под него подстроиться). Основные настройки устройства должны осуществляться по USB (mass storage device + внутренняя flash-память МК + fatfs). Программа должна крутиться под управлением freeRTOS + lwip. В качестве МК был выбран st32f407vg (ф.STMicroelectronics).

 

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

 

Это было краткое введение, теперь суть проблемы. Работаю с блокирующими BSD сокетами. По протоколу на моем устройстве поднят сервер и клиент. Когда приходит на сервер FIN от удаленного узла, я закрываю и сервер, и клиента. Затем снова открываю. Клиент довольно быстро выполняет connect и начинает передавать данные. С сервером дела обстоят иначе. Ниже представлен кусок кода, как я поднимаю сервер (код целиком я прикреплю в конце поста).

 

recv_serverfd = socket(AF_INET, SOCK_STREAM, 0);
if (recv_serverfd < 0) {Error_Handler();}
                    
memset(&recv_IpAddr, 0, sizeof(recv_IpAddr));
recv_IpAddr.sin_family = AF_INET;
recv_IpAddr.sin_port = htons(rs_port);
recv_IpAddr.sin_addr.s_addr = htonl(INADDR_ANY);
                    
len = sizeof(lingr);
                    
if (setsockopt(recv_serverfd, SOL_SOCKET, SO_LINGER, (void*)(&lingr), len) != 0)
{
    #ifdef    UART_PRINTF
        printf("Setsockopt(recv_serverfd) error\r\n");
    #endif    //UART_PRINTF
}
                    
do
{
    status = bind(recv_serverfd, (struct sockaddr*)&recv_IpAddr, sizeof(recv_IpAddr));
}
while (status < 0);
                    
listen(recv_serverfd, 1);

 

Функция bind в цикле раз за разом возвращает -1. И длиться это может по разному: от милисекунд до минут. Для меня последнее критично, т.к. слишком долго восстанавливается связь. Тут Вы скажете, что это нормально. Что сервер переоткрывается на том же порте, и bind будет висеть до тех пор пока закрытое соединение не передаст все данные из передающего буфера. Но есть одно «НО» - сервер у меня принимает данные, но не передает. Тогда откуда задержка? Это первый вопрос.

 

Логичным продолжением стало использование параметра SO_LINGER. Как написано у Стивенса, данный параметр позволяет изменить поведение функции close. По умолчанию функция close возвращает управление немедленно, но если в передающем буфере остались данные, система попытается доставить данные собеседнику (l_onoff = 0, l_linger = 0). Это как раз рассмотренный выше случай.

 

Я пробовал изменить параметр на l_onoff = 1, l_linger = 0. Т.е. соединение должно отправить сегмент RST и перейти в состояние CLOSED, минуя состояние TIME_WAIT. В итого, когда я закрываю сокет, то получаю либо зависание при освобождении памяти в ф-ии close, либо HardFault.

При изменении параметра на l_onoff = 1, l_linger = 1. Результат такой же, как и во втором случае.

Отсюда вопрос №2: может быть параметр SO_LINGER не работает должным образом в lwip? Или я что-то неправильно понимаю.

 

Ну и, наконец, подошли к третьему вопросу. После всех мытарств, решил закрывать не серверный сокет, а присоединенный сокет. И наткнулся на очередные грабли – функция accept вызывается ровно столько раз, сколько указано в параметре backlog функции listen. Т.е. если я пишу “listen(recv_serverfd, 5);”, то функция accept отрабатывает ровно 5 раз и потом зависает на веки вечные. У меня складывается впечатление, что когда я закрываю присоединенный сокет, функция close не освобождает память, и стек считает, что у меня дескриптор занят. Почему так происходит? Может быть кто-то сталкивался с подобным поведением и сможет пнуть меня в нужном направлении?

 

Я прикрепил файл, в котором представлен урезанный код процедуры EthernetTask. Целиком выкладывать не стал, т.к. там можно заблудиться...

Заранее спасибо.

Ethernet_task.txt

Share this post


Link to post
Share on other sites
Отсюда вопрос №2: может быть параметр SO_LINGER не работает должным образом в lwip?
Да запросто может такое быть. Что же Вы в код lwip не заглядывали чтоли?

Share this post


Link to post
Share on other sites
Да запросто может такое быть. Что же Вы в код lwip не заглядывали чтоли?

Код, естественно, смотрел. Функционал, касающийся Linger, присутствует. Я имел ввиду скрытые баги, про которые, возможно, все знают, и только я один прощелкал...

Edited by Бородатый физик

Share this post


Link to post
Share on other sites
Я имел ввиду скрытые баги, про которые, возможно, все знают, и только я один прощелкал...

Там есть честный багтрекер. Ознакомьтесь.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this