Перейти к содержанию
    

Почему FTP-сервер не видит передаваемых ему данных?

Всем привет!

Пишу минимальный сервер. Использую STM32F407+W5500, Filezilla.

После получения команды LIST, открываю второе соединение, дожидаюсь подключения к нему клиента, которому отправляю список файлов. Но клиент висит на ожидании получения списка файлов. Прочитал RFC959, но ответа там не вижу. Клиент ошибок не выдаёт. Что я не учёл?

 

Screenshot_476.jpg

Screenshot_477.jpg

Screenshot_478.jpg

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

20 минут назад, tonyk_av сказал:

Но клиент висит на ожидании получения списка файлов.

Ну так поднимите сервер на компе и проверьте на заведомо рабочем, если заработает, смотрите шарком, что не так делаете... Можно еще попробовать в качестве клиента тотал коммандер, я им проверял свой сервер.

Изменено пользователем mantech

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

По названию темы у Вас FTP сервер не видит передаваемых ему данных. А в тексте говорите что клиент не видит данных.

???

там вроде никаких премудростей нет. У меня так работает

		case FTP_CMD_LIST:
		{
			if (*dataConnMode == NO_CONNECTION)
			{
				FTP_SendReply(conn, FTP_REPLY_503);
				break;
			}
			err = ERR_VAL;
			if(*dataConnMode == ACTIVE_MODE)
			{
				if ((pcb->ConnectIP.addr == 0) || (pcb->ConnectPort == 0))
				{
					FTP_SendReply(conn, FTP_REPLY_503);
				}
				data_conn = netconn_new(NETCONN_TCP);
				netconn_set_recvtimeout(data_conn, 1000 * 60 * 2); //2 min timeout
				data_conn->pcb.tcp->keep_idle = 5000;
				data_conn->pcb.tcp->keep_intvl = 1000;
				data_conn->pcb.tcp->keep_cnt = 5;
				data_conn->pcb.tcp->so_options |= SOF_KEEPALIVE;
				err = netconn_connect(data_conn, &pcb->ConnectIP, pcb->ConnectPort);
			}
			else
			{
				if(*dataConnMode == PASV_MODE_CONNECTED)
				{
					err = ERR_OK;
					data_conn = pasvConn;
				}
			}
			LOG_CONSOLE ("\r\nLIST command for: '%s\n",fullPath);
			if (err == ERR_OK)
			{
				//file_err = f_getcwd(pcb->buff, sizeof(pcb->buff));
				snprintf (pcb->buff, FTP_COMMON_BUFF_SIZE, "%s",fullPath);
				//    if (file_err != FR_OK)
				//    {

				//     FTP_SendReply(conn, FTP_REPLY_550);
				//    }
				//    else
				//    {
				LOG_CONSOLE ("FTPserv: LIST  cmd for  '%s'\n",pcb->buff);
				FTP_SendReply(conn, FTP_REPLY_150);
				FTP_SendList(data_conn, pcb, fullPath);
				FTP_SendReply(conn, FTP_REPLY_226);
				//    }
			}
			else
			{
				FTP_SendReply(conn, FTP_REPLY_425);
			}
			if(*dataConnMode == PASV_MODE_CONNECTED)
			{
				*dataConnMode = PASV_MODE_FINISHED;
				netconn_close(data_conn);
				netconn_delete(data_conn);
			}
			else
			{
				if(*dataConnMode == ACTIVE_MODE)
				{
					*dataConnMode = NO_CONNECTION;
					netconn_close(data_conn);
					netconn_delete(data_conn);
				}
			}
			break;
		}

/**
  * \brief  FTP_SendList() - Sends the list of files in actual server to open connection
  * \param  *conn - net connection type and status
  * \param  *pcb - FTP connection status and information
  * \retval None
*/
void FTP_SendList(struct netconn *conn, ftp_pcb_t *pcb, char* fullPath)
{
	FRESULT file_err;
	err_t err;
	DIR fdir;
	FILINFO fno;
	uint32_t size;
	TCHAR *pntName;
	#define timeStringLEN 30
	char timeString[timeStringLEN];

	if ((fullPath[0] == '/') && (fullPath[1] == '\0'))	// maybe fullPath is "/"
	{//it's a root directory, show all available disks
		for (uint8_t n = 0; n < FF_VOLUMES; n++)
		{
			size = snprintf(pcb->buff, sizeof(pcb->buff), "%c%s %s %s %s %u %s /%s\r\n",
			'd', "rw-------", "1", "user", "group", 0, "Jan 01 2018", VolumeStr[n] );
			if (size>=sizeof(pcb->buff))
			{// the string length was truncut from nChars to maxLength
				size = sizeof(pcb->buff)-1;
				pcb->buff[size] = '\0';
			}
			err = netconn_write(conn, pcb->buff, size, NETCONN_COPY);
			if (err != ERR_OK) return;
		}
		return;
	}
	file_err = f_opendir_safe(&fdir, fullPath,F_SAFE_DELAY);
	if (file_err == FR_OK)
	{
		// read all the entries in directory
		while(1)
		{
			file_err = f_readdir(&fdir, &fno);
			if (file_err != FR_OK || fno.fname[0] == 0) break;
			// if file starts by . it is the . or .. usual links
			if (fno.fname[0] == '.') continue;
			pntName = fno.fname;

			{//prepare time string
				struct tm fileTime_tm;
				time_t fileTime_timet, currTime_timet;
				double deltaT;
				double time180DaysInSeconds = 180.0 * 24.0 * 3600.0;
				//file time
				fileTime_tm.tm_year = (((fno.fdate >> (25 - 16)) & 0x7F) + 1980) - 1900;
				fileTime_tm.tm_mon = ((fno.fdate >> (21 - 16)) & 0x0F) - 1; 	//in struct tm: month is 0 to 11 range, but FAT uses 1 to 12 range
				fileTime_tm.tm_mday = (fno.fdate >> (16 - 16)) & 0x1F;
				fileTime_tm.tm_hour = (fno.ftime >> 11) & 0x1F;
				fileTime_tm.tm_min = (fno.ftime >> 5) & 0x3F;
				fileTime_tm.tm_sec = ((fno.ftime) & 0x01F) << 1;	//2-second count, valid value range 029 inclusive (0  58 seconds).
				fileTime_timet = mktime(&fileTime_tm);
				//current time
				currTime_timet = get_time_seconds();
				//difference
				deltaT = difftime(currTime_timet, fileTime_timet);

				if (deltaT >= time180DaysInSeconds)
				{
 					// format for times older than 180 days
 					strftime(timeString,timeStringLEN,"%b %d %Y", &fileTime_tm);
				}
				else
				{
 					//format for time more recent than 180 days
	 				strftime(timeString, timeStringLEN, "%b %d %H:%M", &fileTime_tm);
				}
			}

			size = snprintf(pcb->buff, sizeof(pcb->buff), "%c%s %s %s %s %u %s %s\r\n",
			(fno.fattrib & AM_DIR) ? 'd' : '-', "rw-------", "1", "user", "group", fno.fsize, timeString, pntName );
			if (size>=sizeof(pcb->buff))
			{// the string length was truncut from nChars to maxLength
				size = sizeof(pcb->buff)-1;
				pcb->buff[size] = '\0';
			}
			err = netconn_write(conn, pcb->buff, size, NETCONN_COPY);
			if (err != ERR_OK) break;
		}
		f_closedir(&fdir);
	}
}

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 hours ago, Ruslan1 said:

там вроде никаких премудростей нет. У меня так работает

Ruslan1, благодарю! Заработало!

Сравнил код выше и свой. Можно сказать, что совпадает один в один, но у себя в коде после сравнения увидел ошибку, состоящую в том, что я из-за неправильного условия устанавливал соединение по каналу передачи данных не по команде PASV, а по LIST. Разница буквально в одну строчку, которая воткнулась мне в глаз и указала на мою ошибку.

 

P. S. Пример написан, по-моему, очень внятно, всё понятно, хотя тут netconn, а у меня класс ввода вывода на базе socket от Wiznet.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Не буду плодить темы, спрошу тут.

Передача пошла, но Filezilla не правильно интерпретирует ответ на команду LIST.

Отправляю с сервера:

image.thumb.png.9f5951f7e19f66b3d538fdf4731de2d9.png

Клиент отображает:

image.thumb.png.ed39b4925286957f4d3fe4772a4cc5cc.png

Месяц, число и год слились с именем файла и директории. Что я делаю не так?

 

P. S. Посмотрел пример выше от Ruslan1, но разницы в формате вывода не заметил.

Изменено пользователем tonyk_av

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 5/16/2023 at 8:25 PM, tonyk_av said:

Что я делаю не так?

Могу предположить, что вы делаете опечатку в названии месяца, и Filezilla не может его распарсить.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

13 hours ago, esaulenka said:

Могу предположить, что вы делаете опечатку в названии месяца, и Filezilla не может его распарсить.

👍

Хорошая мысль! Проверю.

Ну и сам напартачил тоже.😥

image.png.18d51b1ac1a4848b4f55c33fc4862b31.png

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 5/18/2023 at 7:11 PM, esaulenka said:

Могу предположить, что вы делаете опечатку в названии месяца, и Filezilla не может его распарсить.

Так и оказалось! Благодарю!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Всем привет!

В процессе тестирования обнаружилось, что при загрузке файла в МК часто теряется последний переданный пакет. В документации не могу найти ответ на вопрос о том, можно ли вызывать disconnect(), если в буфере сокета есть пришедшие, но не прочитанные данные? Пока грешу на это, но возможно, есть другая причина?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

По идее буфер TCP-соединения нужно ещё раз вычитывать уже после полного закрытия сокета.

У меня в одном проекте тоже похоже иногда наблюдается подобная проблема.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

29 minutes ago, jcxz said:

По идее буфер TCP-соединения нужно ещё раз вычитывать уже после полного закрытия сокета.

Подляна в том, что при попытке чтения мне возвращается 0. Тут суть вопроса не в теории работы ТСР, хотя за мысль с FIN благодарю, а именно в особенностях работы W5500. Возможно, перед закрытием сокета надо удостоверится в отсутствии данных и не переводить сокет в состояние "Соединение разорвано".

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

27 минут назад, tonyk_av сказал:

Возможно, перед закрытием сокета надо удостоверится в отсутствии данных и не переводить сокет в состояние "Соединение разорвано".

Ну так ведь данные могут прийти уже после закрытия сокета. Уже не говоря о том, что они могут прийти между тем как вы удостоверитесь и затем пошлёте команду закрытия. В этом случае только читать буфер после закрытия.

27 минут назад, tonyk_av сказал:

Подляна в том, что при попытке чтения мне возвращается 0.

А если подождать немного после закрытия и потом прочитать? Я вот у себя подумываю о таком решении в аналогичной ситуации.

 

И ещё вопрос: Закрытие соединения (в этих случаях, когда наблюдается проблема) происходит по чьей инициативе? Вашей или удалённой стороны?

У меня такое происходит только когда инициатива разрыва идёт от удалённой стороны. Насколько я заметил.

 

ЗЫ: Я не разбираюсь в API W5500, но мне кажется должен быть какой-то путь получения данных, пришедших во время закрытия TCP-соединения. Так как это - штатная ситуация. Если её не поддерживать, то с некоторыми серверами вообще невозможно будет работать, потому что они всегда присылают данные в составе последнего пакета с FIN. Сомневаюсь, что разработчики W5500 этого не знали и не учли.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 minute ago, jcxz said:

Закрытие соединения (в этих случаях, когда наблюдается проблема) происходит по чьей инициативе?

Клиента.

 

11 minutes ago, jcxz said:

мне кажется должен быть какой-то путь получения данных, пришедших во время закрытия TCP-соединения

Думаю, что мне надо ещё раз почитать даташит и проверить работу конечного автомата у W5500. В паре мест вижу странности в своём коде.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 минуту назад, tonyk_av сказал:

Клиента.

Получается, что у вас точно та же проблема, что и у меня в проекте. Только у меня не W5500, а ESP8266.

1 минуту назад, tonyk_av сказал:

Думаю, что мне надо ещё раз почитать даташит и проверить работу конечного автомата у W5500.

:good:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...