---
title: "STM Lesson 95. LAN. W5500. FTP Server. Part 3"
url: "https://maker.wiznet.io/teddy/projects/stm-lesson-95-lan-w5500-ftp-server-part-3/"
markdown_url: "https://maker.wiznet.io/teddy/projects/stm-lesson-95-lan-w5500-ftp-server-part-3/md"
type: "UCC: User Created Content"
author: "narod stream"
author_url: "https://narodstream.ru/stm-urok-95-lan-w5500-ftp-server-chast-3/"
editor: "WIZnet"
editor_url: "https://maker.wiznet.io/"
original_author: "narod stream"
original_url: "https://narodstream.ru/stm-urok-95-lan-w5500-ftp-server-chast-3/"
published: "2023-09-04"
language: "en"
tags: ["W5500"]
hardware: ["WIZnet W5500", "STMicroelectronics STM32F411RE"]
likes: 0
views: 312
comments: 0
source: "WIZnet Makers (https://maker.wiznet.io/)"
---

# STM Lesson 95. LAN. W5500. FTP Server. Part 3

> FTP 3rd Lecture Using STM32F401RET6 and W5500 - Get Directory Information

Original author: narod stream (source: https://narodstream.ru/stm-urok-95-lan-w5500-ftp-server-chast-3/)

## Components

- **WIZnet W5500** x 1 ([docs](https://docs.wiznet.io/Product/Chip/Ethernet/W5500))
- **STMicroelectronics STM32F411RE** x 1 ([docs](https://www.st.com/en/microcontrollers-microprocessors/stm32f411re.html))
- Software: **ARM KEIL5** ([docs](https://www2.keil.com/mdk5))

WIZnet parts: W5500 ([Datasheet](https://docs.wiznet.io/Product/Chip/Ethernet/W5500/datasheet?utm_source=maker&utm_medium=project&utm_campaign=w5500), [product hub](https://maker.wiznet.io/products/w5500/))

## Article

In [**the previous part**](https://narodstream.ru/stm-urok-95-lan-w5500-ftp-server-chast-2/) of the lesson, we wrote a function to parse the FTP command line, and also processed several commands to adequately respond to them to the client.

Let's now respond to the client's **PASV** command . Or rather, for now, let's start answering, because it's not quite simple and we will do this procedure gradually. To do this, first connect the global variable

`extern``uint8_t``ipaddr[4];`

`**extern**````**uint16_t**``** local_port_ftp_data_passiv;**`

Then let's go to the **w5500.c** file and write the socket close function after the **SocketClosedWait function**

`**//-----------------------------------------------**`

`**void**``** CloseSocket(**``**uint8_t**``** sock_num)**`

`**{**`

`** uint8_t**``** opcode;**`

`** opcode = (((sock_num<<2)|BSB_S0)<<3)|OM_FDM1;**`

`** w5500_writeReg(opcode, Sn_CR, 0x10); **``**//CLOSE SOCKET**`

`**}**`

`**//-----------------------------------------------**`

Let's add a prototype for this function, and at the same time some more in the header file **w5500.h**

`void``SetReadPointer(``uint8_t``sock_num, ``uint16_t``point);`

`**void**``** CloseSocket(**``**uint8_t**``** sock_num);**`

`**uint8_t**``** GetSocketStatus(**``**uint8_t**``** sock_num);**`

`**void**``** SetSockPort(**``**uint8_t**``** sock_num, **``**uint16_t**``** port);**`

Let's return to our switch in the **ftp_cmd_parse function of the ftpd.c** file and start writing the server's reaction to the PASV client command

`break``;`

`**case**````***PASV_CMD***``**:**`

`** local_port_ftp_data_passiv++;**`

`** if**``**(local_port_ftp_data_passiv > 50000) local_port_ftp_data_passiv=35000;**`

`** break**``**;**`

`default``: ``// Invalid`

Let's assign a server port address for the data connection. We will increment this value with each next connection, which will occur on the same client command, so that the port addresses are different. If the port address reaches 50000, the process will resume.

Next, we will respond to the client with a message with the IP address and port address. first we transmit the high byte of the port address, and then the low byte

`if``(local_port_ftp_data_passiv > 50000) local_port_ftp_data_passiv=35000;`

`**len = **``**sprintf**``**((**``**char**``***)datasect->**``**data**``**, **``**"227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)rn"**``**,**`

`** ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3], local_port_ftp_data_passiv >> 8, local_port_ftp_data_passiv & 0x00ff);**`

`**tcp_send_ftp_one(sn, (**``**uint8_t**``** *)buf_send, len);**`

`break``;`

But it is not enough for us to simply transmit the command. We must ensure that the data connection works.

So that's what we're going to do

`tcp_send_ftp_one(sn, (``uint8_t``*)buf_send, len);`

`**//Разъединим сокет для данных и заново его подключим**`

`**if**``**(GetSocketStatus(FTP_SOCKET_DATA)==SOCK_ESTABLISHED)**`

`** DisconnectSocket(FTP_SOCKET_DATA); **``**//Разъединяемся**`

`**CloseSocket(FTP_SOCKET_DATA);**`

`**SocketClosedWait(FTP_SOCKET_DATA);**`

`**HAL_UART_Transmit(&huart2,(**``**uint8_t**``***)**``**"654321rn"**``**,8,0x1000);**`

`**sprintf**``**(str1,**``**"S%d closedrn"**``**,FTP_SOCKET_DATA);**`

`**HAL_UART_Transmit(&huart2,(**``**uint8_t**``***)str1,**``**strlen**``**(str1),0x1000);**`

`**SetSockPort(FTP_SOCKET_DATA, local_port_ftp_data_passiv);**`

`**OpenSocket(FTP_SOCKET_DATA,Mode_TCP);**`

`**//Ждём инициализации сокета (статус SOCK_INIT)**`

`**SocketInitWait(FTP_SOCKET_DATA);**`

`**//Продолжаем слушать сокет**`

`**ListenSocket(FTP_SOCKET_DATA);**`

`**SocketListenWait(FTP_SOCKET_DATA);**`

`**ftpprop.**``**datasock_mode**``** = PASSIVE_MODE;**`

`**ftpprop.**``**datasock_state**``** = DATASOCK_READY;**`

`**sprintf**``**(str1,**``**"PASV port: %drn"**``**, local_port_ftp_data_passiv);**`

`**HAL_UART_Transmit(&huart2,(**``**uint8_t**``***)str1,**``**strlen**``**(str1),0x1000);**`

`break``;`

Here I think everything is clear. If the connection then we break it and connect again using the new port address.

Everything seems to be fine with response processing.

But now we need to somehow handle the data packets on the data connection. But, in principle, we will still have time to do this.

Let's collect the code, flash the controller and try to connect again

![Image16](https://narodstream.ru/wp-content/uploads/2017/10/Image16.png)

Here is the next picture. Our client received a response, the connection was created, the client sends us the following LIST command, which means the client's request to send detailed information about the current directory. We are not yet able to transmit such information, but it is transmitted over the connection already for data transmission. Therefore, the server sent a message to the client that it does not know such a command, for which the client asked to close the connection.

So the next task is to process the command from the **LIST** client .

But before we start executing it, let's also process the **QUIT** command before we forget about it. This team, like many others, speaks for itself. This is the desire of the client to terminate the connection with the server

`break``;`

`**case**````***QUIT_CMD***``** :**`

`** len = **``**sprintf**``**((**``**char**``***)datasect->**``**data**``**, **``**"221 Goodbye!rn"**``**);**`

`** tcp_send_ftp_one(sn, (**``**uint8_t**``** *)buf_send, len);**`

`** DisconnectSocket(sn); **``**//Разъединяемся**`

`** SocketClosedWait(sn);**`

`** sprintf**``**(str1,**``**"S%d closedrn"**``**,sn);**`

`** HAL_UART_Transmit(&huart2,(**``**uint8_t**``***)str1,**``**strlen**``**(str1),0x1000);**`

`** OpenSocket(sn,Mode_TCP);**`

`** //Ждём инициализации сокета (статус SOCK_INIT)**`

`** SocketInitWait(sn);**`

`** //Продолжаем слушать сокет**`

`** ListenSocket(sn);**`

`** SocketListenWait(sn);**`

`** ftpprop.**``**connect_stat**``** = FTP_DISCONNECT;**`

`** break**``**;**`

`default``: ``// Invalid`

We send the desired message to the client and disconnect from it, then reopen the socket and listen to it, so that we are always ready to create a new connection at the request of the client, not necessarily the same one.

Now let's start working with the **LIST** command , along with it there is also a similar **MLSD** command , which the server can also request, so we will also process it

`break``;`

`**case**````***MLSD_CMD***``**:**`

`** len = **``**sprintf**``**((**``**char**``***)datasect->**``**data**``**, **``**"150 Opening data channel for directory listing of "%s"rn"**``**, ftpprop.**``**work_dir**``**);**`

`** tcp_send_ftp_one(sn, (**``**uint8_t**``** *)buf_send, len);**`

`** ftpprop.**``**current_cmd**``** = **``***MLSD_CMD***``**;**`

`** break**``**;**`

`**case**````***LIST_CMD***``**:**`

`** sprintf**``**(str1,**``**"LIST_CMDrn"**``**);**`

`** HAL_UART_Transmit(&huart2,(**``**uint8_t**``***)str1,**``**strlen**``**(str1),0x1000);**`

`** len = **``**sprintf**``**((**``**char**``***)datasect->**``**data**``**, **``**"150 Opening data channel for directory listing of "%s"rn"**``**, ftpprop.**``**work_dir**``**);**`

`** tcp_send_ftp_one(sn, (**``**uint8_t**``** *)buf_send, len);**`

`** ftpprop.**``**current_cmd**``** = **``***LIST_CMD***``**;**`

`** break**``**;**`

`case``*QUIT_CMD*``:`

Here we send the appropriate messages to the client. They are exactly the same. We will also enter the index of the current command in the corresponding field of the structure.

Only now, after all this, we should start sending information about the directory over the data connection. The transfer of this information is not an easy task and you will not form it immediately. Since there are certain formats for information about directories (name, type, date, size, and other attributes).

But whether we want it or not, we must somehow provide this information. At the moment, our current directory is the root. Yes, it does not matter what our current directory is. We could request any from the client by writing the path to it in the connection properties.

Let's start slowly working with the information that we will transmit.

For now, let's go to the ftp_receive function in the body of the condition for the presence of a packet in the data socket, and if we do not have a connection flag in the structure field, then set it

`else``if``(sn == FTP_SOCKET_DATA)`

`{`

`** if**``**(!ftpprop.**``**connect_stat_data**``**)**`

`** {**`

`** sprintf**``**(str1,**``**"FTP Data socket Connectedrn"**``**);**`

`** HAL_UART_Transmit(&huart2,(**``**uint8_t**``***)str1,**``**strlen**``**(str1),0x1000);**`

`** ftpprop.**``**connect_stat_data**``** = 1;**`

`** }**`

`}`

We then add the type specifier of the current command, leaving only the body of one condition

`ftpprop.``connect_stat_data``= 1;`

`}`

`** switch**``**(ftpprop.**``**current_cmd**``**)**`

`** {**`

`** case**````***LIST_CMD***``**:**`

`** case**````***MLSD_CMD***``**:**`

`** case**````***RETR_CMD***``**:**`

`** if**``**(ftpprop.**``**data_stat**``**==DATA_COMPLETED)**`

`** {**`

`** ftpprop.**``**data_stat**``**=DATA_FIRST;**`

`** }**`

`** break**``**;**`

`** default**``**:**`

`** break**``**;**`

`** }**`

`}`

`}`

We processed several commands at once, since the handler is the same. In the handler we found out the transmission status, and then, if we have no untransmitted data, set the data transmission status to the transmission status of the first packet. Unlike HTTP, here we will not differ whether it is the first or the only one. We will already determine this as we process the package. Also in this condition we will have a command to call a certain function, which we do not yet have. So let's add it. This will be the function to send data to the client over the data socket. We will add it after the **ftp_cmd_parse function**

`**//-----------------------------------------------**`

`**void**``** ftp_data_send(**``**uint8_t**``** sn)**`

`**{**`

`**}**`

`**//-----------------------------------------------**`
Now we will call it in our data packet handler in the data socket that we just wrote, in the same branch where we determined that we have no untransmitted data.

`ftpprop.``data_stat``=DATA_FIRST;`

`** ftp_data_send(sn);**`

`}`

`break``;`

In the header file **w5500.h,** we will add the window size macro

`#define``SS_DESELECT() HAL_GPIO_WritePin(CS_GPIO_PORT, CS_PIN, GPIO_PIN_SET)`

`//--------------------------------------------------`

`**#define**``** SIZE_WND 2048**`

Let's return to our function of transferring data to the client **ftp_data_send** to the file **ftpd.c** and start writing its body

`void``ftp_data_send(``uint8_t``sn)`

`{`

`** static**````**char**``** buf_send[SIZE_WND+3];**`

`** FRESULT**``** res; **``**//результат выполнения**`

`** buf_send[0] = **``**''**``**;**`

`** switch**``**(ftpprop.**``**current_cmd**``**)**`

`** {**`

`** case**````***LIST_CMD***``**:**`

`** case**````***MLSD_CMD***``**:**`

`** break**``**;**`

`** default**``**:**`

`** break**``**;**`

`** }**`

`}`

For now, we will only process current commands for requesting information in the directory.

Let's add some global variables to work with the FATFS library

`extern``volatile``uint16_t``tcp_size_wnd;`

`**static**````**FILINFO**``** fileInfo;**`

`**static**````**DIR**``** dir;**`

`**extern**````**uint32_t**``** bytesread;**`

Let's return to our function and initialize some pointers in the command handler for transmitting information about the current directory

`case``*LIST_CMD*``:`

`case``*MLSD_CMD*``:`

`** fileInfo.**``**lfname**``** = (**``**char**``***)sect;**`

`** fileInfo.**``**lfsize**``** = **``**sizeof**``**(sect);**`

`break``;`

Above this function, we will write another separate function to transfer information about the directory to the client, in which we will create a number of global variables and initialize pointers.

`**//-----------------------------------------------**`

`**//функция чтения и передачи позиций каталога**`

`**void**``** ftp_send_dirinfo(**``**uint8_t**``** sn, **``**char**``** *buf)**`

`**{**`

`** FRESULT**``** res; **``**//результат выполнения**`

`** uint16_t**``** len;**`

`** char**``** str_date[15];**`

`** int**``** str_date_ptr = 0, buf_ptr = 0;**`

`** uint16_t**``** buf1_ptr = 0;**`

`** char**``** *fn;**`

`** fileInfo.**``**lfname**``** = (**``**char**``***)sect;**`

`** fileInfo.**``**lfsize**``** = **``**sizeof**``**(sect);**`

`**}**`

`**//-----------------------------------------------**`

This function will write to the transmission buffer information about the positions of the catalog by one position, and if we suddenly start to exceed the window size, then the transmission will stop and resume already in the transmission of the next packet. We will also encounter a situation almost always when we reach the maximum buffer size, but the current position is not completely transferred. Therefore, we will have to remember the position of the pointer in the line with information about the current position and return to this place in the next packet transmission. Therefore, let's create another global buffer

`extern``uint8_t``sect[515];`

`**static**````**char**``** buf1[256];**`

I think this size is enough for information about the position. Also, in order for our buffer not to fall apart by our next arrival to the function, we will add the **static** attribute to it .

Let's continue writing the body of our function

`fileInfo.``lfsize``= ``sizeof``(sect);`

`**buf_ptr+=3;**``**//оставим место под ардес и опкод**`

`**//если есть оставшаяся информация о позиции в каталоге, то передадим её**`

`**if**``**(ftpprop.**``**rem_bytes_dirinfo**``**)**`

`**{**`

`** memcpy**``**((**``**uint8_t**``***)(buf+buf_ptr),(**``**uint8_t**``***)(buf1+**``**strlen**``**(buf1)-ftpprop.**``**rem_bytes_dirinfo**``**),ftpprop.**``**rem_bytes_dirinfo**``**);**`

`** buf_ptr+=ftpprop.**``**rem_bytes_dirinfo**``**;**`

`**}**`

We left space for the address and opcode, then if we have untransmitted bytes from the previous position, we add them to the main buffer, which we will then transfer.

Next cycle

`buf_ptr+=ftpprop.``rem_bytes_dirinfo``;`

`}`

`**while**``**(1)**`

`**{**`

`** buf1_ptr = 0;**`

`** res = f_readdir(&dir, &fileInfo);**`

`**}**`

In the loop, we set the buffer pointer for the current position to zero and read information about the current position. We worked with this function and we know very well that it switches the pointer to the next position after it is called again.

We continue to work with the cycle

`res = f_readdir(&dir, &fileInfo);`

`**if**``** (res==**``***FR_OK***``** && fileInfo.**``**fname**``**[0])**`

`**{**`

`** fn = fileInfo.**``**lfname**``**;**`

`** if**``**(!**``**strlen**``**(fn)) fn=fileInfo.**``**fname**``**;**`

`**}**`

`**else**`

`**{**`

`** break**``**;**`

`**}**`

We first determine the name of the file (directory). If it is long, then it is already defined in a slightly different way, we also know how it is done, so we also took this into account here.

We do not exit the loop and write the code further

`break``;`

`}`

`**switch**``**((fileInfo.**``**fdate**``** >> 5) & 0x0f)**`

`**{**`

`** case**``** 1:**`

`** len = **``**sprintf**``**(str_date, **``**"JAN "**``**);**`

`** break**``**;**`

`** case**``** 2:**`

`** len = **``**sprintf**``**(str_date, **``**"FEB "**``**);**`

`** break**``**;**`

`** case**``** 3:**`

`** len = **``**sprintf**``**(str_date, **``**"MAR "**``**);**`

`** break**``**;**`

`** case**``** 4:**`

`** len = **``**sprintf**``**(str_date, **``**"APR "**``**);**`

`** break**``**;**`

`** case**``** 5:**`

`** len = **``**sprintf**``**(str_date, **``**"MAY "**``**);**`

`** break**``**;**`

`** case**``** 6:**`

`** len = **``**sprintf**``**(str_date, **``**"JUN "**``**);**`

`** break**``**;**`

`** case**``** 7:**`

`** len = **``**sprintf**``**(str_date, **``**"JUL "**``**);**`

`** break**``**;**`

`** case**``** 8:**`

`** len = **``**sprintf**``**(str_date, **``**"AUG "**``**);**`

`** break**``**;**`

`** case**``** 9:**`

`** len = **``**sprintf**``**(str_date, **``**"SEP "**``**);**`

`** break**``**;**`

`** case**``** 10:**`

`** len = **``**sprintf**``**(str_date, **``**"OCT "**``**);**`

`** break**``**;**`

`** case**``** 11:**`

`** len = **``**sprintf**``**(str_date, **``**"NOV "**``**);**`

`** break**``**;**`

`** case**``** 12:**`

`** len = **``**sprintf**``**(str_date, **``**"DEC "**``**);**`

`** break**``**;**`

`**}**`

This part of the code, I think, is understandable. We take information from the file (directory) information structure about the month the file (directory) was created and put it into a string in a format understandable to the client.

We write further code in the loop without leaving it

`break``;`

`}`

`**str_date_ptr += len;**`

`**len = **``**sprintf**``**(str_date + str_date_ptr, **``**"%d "**``**, (fileInfo.**``**fdate**``** & 0x1f));**`

`**str_date_ptr += len;**`

`**len = **``**sprintf**``**(str_date + str_date_ptr, **``**"%d"**``**, (((fileInfo.**``**fdate**``** >> 9) & 0x7f) + 1980));**`

`**str_date_ptr = 0;**`

`**if**``**(fileInfo.**``**fattrib**``** & AM_DIR)**`

`**{**`

`** sprintf**``**(buf1, **``**"d"**``**);**`

`**}**`

`**else**`

`**{**`

`** sprintf**``**(buf1, **``**"-"**``**);**`

`**}**`

Here we move through a buffer intended solely for the string representation of the date, then we enter the day of the month itself into this buffer. Next, we reset the buffer counter to zero, and then begin to fill the buffer of the position itself. We begin to fill it in with information about the position type - is it a file or a directory.

We write further the body of our cycle

`sprintf``(buf1, ``"-"``);`

`}`

`**buf1_ptr++;**`

`**len = **``**sprintf**``**(buf1 + buf1_ptr, **``**"rwxr-xr-x 1 ftp ftp %lu %s %srn"**``**, fileInfo.**``**fsize**``**, str_date, fn);**`

`**buf1_ptr += len;**`

We move up the buffer one position and begin to fill it according to the standard, determining the client’s rights to the file. Let's pass the standard rights, then we'll pass the string "ftp ftp", which is also mandatory, and then information about the file size and the string we prepared earlier with a string representation of the date. Well, let's move the pointer to the size of the data transferred to the buffer.

Continue with the body of the loop

`buf1_ptr += len;`

`**if**``**((buf_ptr+buf1_ptr)>(SIZE_WND+3))**`

`**{**`

`** memcpy**``**((**``**uint8_t**``***)(buf+buf_ptr),(**``**uint8_t**``***)buf1,SIZE_WND+3-buf_ptr);**`

`**}**`

`**else**`

`**{**`

`** memcpy**``**((**``**uint8_t**``***)(buf+buf_ptr),(**``**uint8_t**``***)buf1,buf1_ptr);**`

`**}**`

`**buf_ptr += buf1_ptr;**`

Accordingly, here we are already copying the buffer with the line for the position into a large buffer, which we are preparing for writing to a buffer for sending to the client, equal to the size of the window. And here we determine whether we have reached the maximum size of the main socket buffer. If we have achieved it, then we copy into it only that part of the position that will fit into it, as they say, to capacity. And if we haven’t achieved it, then we copy the whole thing. Then we move along the main buffer again.

Next we write our cycle. This will be the final code of our loop

`buf_ptr += buf1_ptr;`

`** if**``**(buf_ptr>(SIZE_WND+3))**`

`** {**`

`** ftpprop.**``**rem_bytes_dirinfo**``** = buf_ptr-SIZE_WND-3;**`

`** ftpprop.**``**data_stat**``**=DATA_MIDDLE;**`

`** break**``**;**`

`** }**`

`** else**`

`** {**`

`** ftpprop.**``**rem_bytes_dirinfo**``** = 0;**`

`** }**`

`}`

Here, when we reach the end of the buffer, we determine and set the value of the remaining bytes field for transmitting the current position, and also change the transmission status to the transmission status of the middle part of the entire amount of data. We don’t yet know whether it will really be average or will be the last. Also, don’t forget about three information bytes. We will determine this later. Well, if we haven’t reached the end of the buffer, then we reset the counter.

Now we exit the endless loop and write the code further. All we have to do now is to transfer our filled buffer to the client

`ftpprop.``rem_bytes_dirinfo``= 0;`

`}`

`}`

`** if**``**(buf_ptr>(SIZE_WND+3))**`

`** {**`

`** buf_ptr=SIZE_WND+3;**`

`** }**`

`** else**`

`** {**`

`** ftpprop.**``**data_stat**``**=DATA_END;**`

`** f_closedir(&dir);**`

`** }**`

`** tcp_send_ftp_one(sn, (**``**uint8_t**``** *)buf, buf_ptr-3);**``**//отнимаем из общей длины служебные байты**`

`}`

Here we move the pointer in the buffer to its very end when the maximum size is reached. Otherwise, we do not touch its value, since it is not exceeded and close the directory. Also, otherwise we set the data transfer status to ending. And then we pass the buffer to the client.

We will process all these statuses later, but for now we will return to our function of transferring data to the client **ftp_data_send** and continue to write there the client command handler to request the contents of the directory

`fileInfo.``lfsize``= ``sizeof``(sect);`

`**if**``**(ftpprop.**``**data_stat**``**==DATA_FIRST)**`

`**{**`

`** res = f_opendir(&dir, ftpprop.**``**work_dir**``**);**`

`** if**``**(res == **``***FR_OK***``**)**`

`** {**`

`** ftp_send_dirinfo(sn,buf_send);**`

`** if**``**(ftpprop.**``**data_stat**``**==DATA_END)**`

`** {**`

`** f_closedir(&dir);**`

`** }**`

`** }**`

`**}**`

`**else**````**if**``**(ftpprop.**``**data_stat**``**==DATA_MIDDLE)**`

`**{**`

`** ftp_send_dirinfo(sn,buf_send);**`

`**}**`

`break``;`

Here, provided that this is our first package, we open the directory. If the directory opened successfully, then we call our function to pass information about the contents of the directory, and then, if we have the end of data transfer status, we close the directory. Otherwise, if we have the data transfer status of the middle package, we no longer open the catalog, it is assumed that we have already opened it earlier and continue to transfer data with information about the next positions of the catalog, for which we will call the appropriate function. That's all for now with this data transfer function.

Now let's return to the function of receiving and processing FTP packets **ftp_receive** and in the body of the condition for receiving a packet over a data connection above the current command switch, add another data transfer status switch and process two statuses in it

`ftpprop.``connect_stat_data``= 1;`

`}`

`**switch**``**(ftpprop.**``**data_stat**``**)**`

`**{**`

`** case**``** DATA_END:**`

`** ftpprop.**``**current_cmd**``** = **``***NO_CMD***``**;**`

`** len = **``**sprintf**``**((**``**char**``** *)(sect+3), **``**"226 Successfully transferred "%s"rn"**``**, ftpprop.**``**work_dir**``**);**`

`** tcp_send_ftp_one(FTP_SOCKET_CTRL, sect, len);**`

`** ftpprop.**``**data_stat**``**=DATA_COMPLETED;**`

`** DisconnectSocket(FTP_SOCKET_DATA); **``**//Разъединяемся**`

`** SocketClosedWait(FTP_SOCKET_DATA);**`

`** sprintf**``**(str1,**``**"S%d closedrn"**``**,FTP_SOCKET_DATA);**`

`** HAL_UART_Transmit(&huart2,(**``**uint8_t**``***)str1,**``**strlen**``**(str1),0x1000);**`

`** OpenSocket(FTP_SOCKET_DATA,Mode_TCP);**`

`** //Ждём инициализации сокета (статус SOCK_INIT)**`

`** SocketInitWait(FTP_SOCKET_DATA);**`

`** //Продолжаем слушать сокет**`

`** ListenSocket(FTP_SOCKET_DATA);**`

`** SocketListenWait(FTP_SOCKET_DATA);**`

`** ftpprop.**``**connect_stat_data**``** = 0;**`

`** break**``**;**`

`** case**``** DATA_MIDDLE:**`

`** ftp_data_send(sn);**`

`** break**``**;**`

`**}**`

`switch``(ftpprop.``current_cmd``)`

In the case of the transfer end status, we close the socket, reinitialize certain fields of the structure, send a specific message to the client that the data has been transferred so that the client knows that we have transferred all the data to it, open the socket and start listening to it again. And in the case of the transfer status of the middle part, we call the data transfer function again.

Let's collect the code and flash the controller. First, let's fill the flash card with the expectation that information about the directory does not exceed the size of the buffer.

Let's connect to the server and see the result

![Image17](https://narodstream.ru/wp-content/uploads/2017/10/Image17.png)

Here is the result. We see the entire file system of the flash card.

And here is the information from WireShark

![Image18](https://narodstream.ru/wp-content/uploads/2017/10/Image18.png)

We see that everything is transferred.

Now let's fill the map with more information and see the result

![Image20](https://narodstream.ru/wp-content/uploads/2017/10/Image20.png)

Also, all information is displayed and comes. Here is the result in WireShark, where we see the next packet being sent that did not fit into the buffer

![Image21](https://narodstream.ru/wp-content/uploads/2017/10/Image21.png)

Let's fill our drive with more information.

And here is the result

![Image22](https://narodstream.ru/wp-content/uploads/2017/10/Image22.png)

We work further with FTP. Let's try to open some directory by double clicking the mouse

![Image23](https://narodstream.ru/wp-content/uploads/2017/10/Image23.png)

The catalog opened normally. Let's look at the information in the traffic analyzer

![Image24](https://narodstream.ru/wp-content/uploads/2017/10/Image24.png)

Everything is fine here too. The client and server communicate normally and respond adequately to messages and commands.

Now let's go further. Let's try to return from our directory to a higher level, that is, exit it

![Image25](https://narodstream.ru/wp-content/uploads/2017/10/Image25.png)

In this case, the connection is lost. We will find the reason for this gap in [**the next part**](https://narodstream.ru/stm-urok-95-lan-w5500-ftp-server-chast-4/) of our lesson, in which we will also learn how to create and delete files and directories, as well as transfer files to and from the server.

---

Source: https://maker.wiznet.io/teddy/projects/stm-lesson-95-lan-w5500-ftp-server-part-3/
