[Źródło: http://www.st.com/en/evaluation-tools/stm32f4discovery.html]
Protokół TCP jest zorientowany połączeniowo. Oznacza to, umożliwia on zestawienie połączenia z którym nastąpi wymiana danych. Dużym plusem tej metody jest duża efektywność oraz niezawodność w przesyłaniu informacji.
W tym połączeniu stosuje się sterowanie przepływem informacji, potwierdzanie odbioru, kontroli błędów czy wykonanie retransmisji.
Protokół TCP jest zorientowany połączeniowo. Oznacza to, umożliwia on zestawienie połączenia z którym nastąpi wymiana danych. Dużym plusem tej metody jest duża efektywność oraz niezawodność w przesyłaniu informacji.
W tym połączeniu stosuje się sterowanie przepływem informacji, potwierdzanie odbioru, kontroli błędów czy wykonanie retransmisji.
Na samym początku opiszę jak wygląda segment w protokole TCP. W jego skład wchodzi:
Port źródłowy - 16 bitów (2 bajty) zawiera numer portu źródłowego dla procesu aplikacyjnego, który korzysta z usługi TCP.
Port docelowy - 16 bitów (2 bajty) zawiera numer portu docelowego. Połączenie portu docelowego oraz portu źródłowego wraz z adresem IP pozwala na określenie pary gniazd połączeniowych dla protokołu TCP.
Numer sekwencyjny - 32 bity (4 bajty) zawiera on numer sekwencyjny dla pierwszego bajtu danych w segmencie. W czasie ustanawiania połączenia w polu wprowadzany jest inicjujący numer sekwencyjny. Od niego rozpoczyna się numerację bajtów. Dodatkowo bit SYN w polu znaczniki musi być ustawiony na 1.
Numer potwierdzenia - 32 bitowy (4 bajty) jest to numer potwierdzający otrzymanie pakietu przez odbiorcę. Pozwala to na synchronizację nadawania i odbierania między klientem a serwerem.
Długość nagłówka - 4 bitowa liczba oznaczająca 32 bitowy wiersz nagłówka. Pozwala na określenie kiedy zaczynają się dane. Nagłówek może posiadać długość, która jest wielokrotnością 32 bitów.
Rezerwa - 3 bity zarezerwowane do przyszłego użycia. Muszą być wpisane jako 0.
Znaczniki - to pole składa się z sześciu bitów sterujących:
NS - pojedynczy byt określający sumę wartości flag ENC;
CWR - potwierdza odebranie powiadomienia od nadawcy;
ECE - ustawiana przez odbiorcę pakietu w momencie otrzymania pakietu w którym flaga CE jest ustawiona;
UGR - pozwala na określenie ważności dla pola Prioryter
ACK - określa ważność dla pola Numer potwierdzenia
PSH - wymuszenie przesłania pakietu
RST - resetuje połączenie
SYN - synchronizacja kolejnych numerów sekwencyjnych
FIN - zakończenie przesyłania danych
Szerokość okna - 16 bitów informacja o tym ile danych może aktualnie przyjąć odbiorca.
Suma kontrolna - 16 bitowa liczba, będzie wynikiem działań na całym pakiecie. Obliczany jest ona z całego nagłówka TCP. Pola sumy kontrolnego są wyzerowane. Ostatnie osiem pól nagłówka IP stanowią adres nadawcy oraz odbiorcy pakietu.
Wskaźnik pilności - w przypadku gdy flaga URG jest włączona, informuje o ważności pakietu.
Opcje - dodatkowe informacje oraz polecenia. Wartość 0 oznacza koniec listy opcji. 1 braz działania natomiast 2 ustawia maksymalną długość segmentu.
Port źródłowy - 16 bitów (2 bajty) zawiera numer portu źródłowego dla procesu aplikacyjnego, który korzysta z usługi TCP.
Port docelowy - 16 bitów (2 bajty) zawiera numer portu docelowego. Połączenie portu docelowego oraz portu źródłowego wraz z adresem IP pozwala na określenie pary gniazd połączeniowych dla protokołu TCP.
Numer sekwencyjny - 32 bity (4 bajty) zawiera on numer sekwencyjny dla pierwszego bajtu danych w segmencie. W czasie ustanawiania połączenia w polu wprowadzany jest inicjujący numer sekwencyjny. Od niego rozpoczyna się numerację bajtów. Dodatkowo bit SYN w polu znaczniki musi być ustawiony na 1.
Numer potwierdzenia - 32 bitowy (4 bajty) jest to numer potwierdzający otrzymanie pakietu przez odbiorcę. Pozwala to na synchronizację nadawania i odbierania między klientem a serwerem.
Długość nagłówka - 4 bitowa liczba oznaczająca 32 bitowy wiersz nagłówka. Pozwala na określenie kiedy zaczynają się dane. Nagłówek może posiadać długość, która jest wielokrotnością 32 bitów.
Rezerwa - 3 bity zarezerwowane do przyszłego użycia. Muszą być wpisane jako 0.
Znaczniki - to pole składa się z sześciu bitów sterujących:
NS - pojedynczy byt określający sumę wartości flag ENC;
CWR - potwierdza odebranie powiadomienia od nadawcy;
ECE - ustawiana przez odbiorcę pakietu w momencie otrzymania pakietu w którym flaga CE jest ustawiona;
UGR - pozwala na określenie ważności dla pola Prioryter
ACK - określa ważność dla pola Numer potwierdzenia
PSH - wymuszenie przesłania pakietu
RST - resetuje połączenie
SYN - synchronizacja kolejnych numerów sekwencyjnych
FIN - zakończenie przesyłania danych
Szerokość okna - 16 bitów informacja o tym ile danych może aktualnie przyjąć odbiorca.
Suma kontrolna - 16 bitowa liczba, będzie wynikiem działań na całym pakiecie. Obliczany jest ona z całego nagłówka TCP. Pola sumy kontrolnego są wyzerowane. Ostatnie osiem pól nagłówka IP stanowią adres nadawcy oraz odbiorcy pakietu.
Wskaźnik pilności - w przypadku gdy flaga URG jest włączona, informuje o ważności pakietu.
Opcje - dodatkowe informacje oraz polecenia. Wartość 0 oznacza koniec listy opcji. 1 braz działania natomiast 2 ustawia maksymalną długość segmentu.
Można wyróżnić kilka rodzajów połączeń TCP:
LISTEN - serwer jest gotowy do przyjęcia połączenie na podanym porcie
SYN-SENT - początek nawiązywania połączenia. Klient wysyła do serwera pakiet SYN. Po czym przechodzi on do oczekiwania na ramkę SYN wraz z ACK.
SYN-RECEIVED - Serwer otrzymał pakiet SYN. Wysyła SYN+ACK. Serwer oczekuje teraz na ramkę ACK od klienta. Połączenie jest w połowie otwarte.
ESTABLISHED - połączenie zostało nawiązane pomiędzy klientem a serwerem
FIN-WAIT-1 - przesłano pakiet FIN kończący transmisję
FIN-WAIT-2 - oczekiwania na wysłanie FIN do serwera
CLOSE-WAIT - oczekiwanie na przesłanie własnego pakietu FIN.
CLOSING - zamykanie połączenia
LAST-ACK - ramki FIN zostały przesłane. Oczekiwanie na odebranie ostatniego pakietu ACK
TIME-WAIT - oczekiwanie na otrzymanie potwierdzenia rozłączenia
CLOSED - połączenie zamknięte
LISTEN - serwer jest gotowy do przyjęcia połączenie na podanym porcie
SYN-SENT - początek nawiązywania połączenia. Klient wysyła do serwera pakiet SYN. Po czym przechodzi on do oczekiwania na ramkę SYN wraz z ACK.
SYN-RECEIVED - Serwer otrzymał pakiet SYN. Wysyła SYN+ACK. Serwer oczekuje teraz na ramkę ACK od klienta. Połączenie jest w połowie otwarte.
ESTABLISHED - połączenie zostało nawiązane pomiędzy klientem a serwerem
FIN-WAIT-1 - przesłano pakiet FIN kończący transmisję
FIN-WAIT-2 - oczekiwania na wysłanie FIN do serwera
CLOSE-WAIT - oczekiwanie na przesłanie własnego pakietu FIN.
CLOSING - zamykanie połączenia
LAST-ACK - ramki FIN zostały przesłane. Oczekiwanie na odebranie ostatniego pakietu ACK
TIME-WAIT - oczekiwanie na otrzymanie potwierdzenia rozłączenia
CLOSED - połączenie zamknięte
Kod:
Tutaj w zadzie wykorzystałem prawie cały kod z poprzednich dwóch przykładów. Uległy tylko rodzaje ramek z UDP na TCP. Wobec tego poniżej przedstawię tylko zmienione funkcje.
Gdy przyjdą dane do układ to następuje wygenerowanie przerwania. W obsłudze przerwania dekodowany jest pakiet danych na jego podstawie sprawdzana jest ramka danych:
Przy odebraniu ramki danych ETH_IP funkcja wywołuje ipRead która odpowiada za określenie typu ramki danych:
Gdy przyjdzie ramka TCP to wchodzimy do funkcji określającej dokładne parametry ramki. I odsyłające odpowiedź.
Nawiązanie połączenia odbywa się poprzez procedurę three-way handshake. Gdy klient chce nawiązać połączenie z serwerem postawionym na STM32 przesyła on segment SYN. Po jej odebraniu następuje przesłanie ramki SYN od serwera oraz ACK. Po odebraniu obu ramek w odpowiedniej konfiguracji klient przechodzi w stan ESTABLISHED i odsyła ramkę ACK. Po jej odebraniu serwer przechodzi w stan ESTABLISHED.
Funkcja odpowiedzialna za przesłanie wiadomości SYN + ACK:
Gdy zostaną przesłane dane od klienta to funkcja wchodzi do odebrania danych i przesłania odpowiedzi do klienta.
Dla weryfikacji nadawania oraz odbierania stosowane są sumy kontrolne oraz sekwencyjne numery pakietów. Odbiorca potwierdza otrzymanie pakietów przez ustawienia flagi ACK.
Zakończenie transmisji odbywa się za pomocą następującej funkcji:
Zakończenie połączenia może być wykonane przez klienta jak i przez serwer. Przesyłany pakiet ma ustawioną flagę FIN. Jego przesłanie wymaga odpowiedzi w postaci ramki ACK.
Działanie programu polega na przesłaniu zapytania przez klienta do serwera. On wtedy odpowiada przykładowymi danymi z odpowiedzią:
Cały projekt można pobrać z dysku Google pod tym linkiem w folderze STM32 a następnie STM32F4.
Gdy przyjdą dane do układ to następuje wygenerowanie przerwania. W obsłudze przerwania dekodowany jest pakiet danych na jego podstawie sprawdzana jest ramka danych:
- void ethReadFrame(enc28j60_frame_t *frame, uint16_t length)
- {
- uint8_t status=0;
- char bufferString[50] = {0};
- if(length > sizeof(enc28j60_frame_t))
- {
- if(frame->frameType==ETH_ARP)
- {
- #ifdef DEBUG_NET_FIL
- sprintf(bufferString,"ETH_ARP:%02X:%02X:%02X:%02X:%02X:%02X-%02X:%02X:%02X:%02X:%02X:%02X; %d; arp\r\n",
- frame->sourceMacAddress[0],frame->sourceMacAddress[1],frame->sourceMacAddress[2],
- frame->sourceMacAddress[3],frame->sourceMacAddress[4],frame->sourceMacAddress[5],
- frame->destMacAddress[0],frame->destMacAddress[1],frame->destMacAddress[2],
- frame->destMacAddress[3],frame->destMacAddress[4],frame->destMacAddress[5],
- length);
- HAL_UART_Transmit(&huart2,(uint8_t*)bufferString,strlen(bufferString),0x1000);
- #endif
- status = arpRead(frame, length-sizeof(enc28j60_frame_t));
- if(status==1)
- {
- arpSend(frame);
- }
- else if(status==2)
- {
- arpTableFill(frame);
- }
- }
- else if(frame->frameType==ETH_IP)
- {
- #ifdef DEBUG_NET_FIL
- sprintf(bufferString,"ETH_IP_FUNCT:%02X:%02X:%02X:%02X:%02X:%02X-%02X:%02X:%02X:%02X:%02X:%02X; %d; ip\r\n",
- frame->sourceMacAddress[0],frame->sourceMacAddress[1],frame->sourceMacAddress[2],
- frame->sourceMacAddress[3],frame->sourceMacAddress[4],frame->sourceMacAddress[5],
- frame->destMacAddress[0],frame->destMacAddress[1],frame->destMacAddress[2],
- frame->destMacAddress[3],frame->destMacAddress[4],frame->destMacAddress[5],
- length);
- HAL_UART_Transmit(&huart2,(uint8_t*)bufferString,strlen(bufferString),0x1000);
- #endif
- ipRead(frame,length-sizeof(ip_frame_t));
- }
- else
- {
- #ifdef DEBUG_NET_FIL
- sprintf(bufferString,"ETH_ELSE:%02X:%02X:%02X:%02X:%02X:%02X-%02X:%02X:%02X:%02X:%02X:%02X; %d; %04X",
- frame->sourceMacAddress[0],frame->sourceMacAddress[1],frame->sourceMacAddress[2],
- frame->sourceMacAddress[3],frame->sourceMacAddress[4],frame->sourceMacAddress[5],
- frame->destMacAddress[0],frame->destMacAddress[1],frame->destMacAddress[2],
- frame->destMacAddress[3],frame->destMacAddress[4],frame->destMacAddress[5],
- length, CONVERT_16BIT(frame->frameType));
- HAL_UART_Transmit(&huart2,(uint8_t*)bufferString,strlen(bufferString),0x1000);
- HAL_UART_Transmit(&huart2,(uint8_t*)"\r\n",2,0x1000);
- #endif
- }
- }
- }
Przy odebraniu ramki danych ETH_IP funkcja wywołuje ipRead która odpowiada za określenie typu ramki danych:
- static void ipRead(enc28j60_frame_t *frame, uint16_t length)
- {
- ip_frame_t *ip_frame = (void*)(frame->dataBuffer);
- if((ip_frame->protVerion==0x45) && (!memcmp(ip_frame->destinIpAdress,ipaddr,4)))
- {
- /* Data Length */
- length = CONVERT_16BIT(ip_frame->packetLength) - sizeof(ip_frame_t);
- if(ip_frame->protocolType==IP_ICMP)
- {
- ICMPRead(frame, length);
- }
- else if(ip_frame->protocolType==IP_TCP)
- {
- tcpRead(frame, length);
- }
- }
- free(ip_frame);
- }
Gdy przyjdzie ramka TCP to wchodzimy do funkcji określającej dokładne parametry ramki. I odsyłające odpowiedź.
- void tcpRead(enc28j60_frame_t *frame, uint16_t len)
- {
- ip_frame_t *ipFrame = (void*)(frame->dataBuffer);
- tcp_frame_t *tcpFrame = (void*)(ipFrame->dataBuffer);
- uint16_t dataLength = CONVERT_16BIT(ipFrame->packetLength) - 20 - (tcpFrame->dataOffset>>2);
- char uartBuffer[50] = {0};
- #ifdef DEBUG_TCP_FIL
- sprintf(uartBuffer,"Data receive from: %d.%d.%d.%d Data length: %d\r\n",
- ipFrame->destinIpAdress[0],ipFrame->destinIpAdress[1],
- ipFrame->destinIpAdress[2],ipFrame->destinIpAdress[3], dataLength);
- HAL_UART_Transmit(&huart2,(uint8_t*)uartBuffer,strlen(uartBuffer),0x1000);
- #endif
- /* If there was send some data */
- if (CHECK_TCP_DATA_RECEIVE(dataLength))
- {
- #ifdef DEBUG_TCP_FIL
- sprintf(uartBuffer,"DataReceive %s\r\n", tcpFrame->dataBuffer);
- HAL_UART_Transmit(&huart2,(uint8_t*)uartBuffer,strlen(uartBuffer),0x1000);
- #endif
- tcpSendData(ipFrame->sourceIpAdress, CONVERT_16BIT(tcpFrame->sourcePort));
- }
- if (CHECK_TCP_SYN)
- {
- tcpSynAck(ipFrame->sourceIpAdress, CONVERT_16BIT(tcpFrame->sourcePort));
- }
- else if (CHECK_TCP_FIN)
- {
- tcpFin(ipFrame->sourceIpAdress, CONVERT_16BIT(tcpFrame->sourcePort));
- }
- else if (CHECK_TCP_ACK)
- {
- HAL_UART_Transmit(&huart2,(uint8_t*)"Transmit Finish\r\n",17,0x1000);
- }
- free(ipFrame);
- free(tcpFrame);
- }
Nawiązanie połączenia odbywa się poprzez procedurę three-way handshake. Gdy klient chce nawiązać połączenie z serwerem postawionym na STM32 przesyła on segment SYN. Po jej odebraniu następuje przesłanie ramki SYN od serwera oraz ACK. Po odebraniu obu ramek w odpowiedniej konfiguracji klient przechodzi w stan ESTABLISHED i odsyła ramkę ACK. Po jej odebraniu serwer przechodzi w stan ESTABLISHED.
Funkcja odpowiedzialna za przesłanie wiadomości SYN + ACK:
- static void tcpSynAck(uint8_t *ip_addr, uint16_t port)
- {
- uint16_t bufferLength = 0;
- enc28j60_frame_t *encFrame = (void*)net_buf;
- ip_frame_t *ipFrame = (void*)(encFrame->dataBuffer);
- tcp_frame_t *tcpFrame = (void*)(ipFrame->dataBuffer);
- char uartBuffer[50] = {0};
- tcpFrame->destPort = CONVERT_16BIT(port); /* Prepare dest port */
- tcpFrame->sourcePort = CONVERT_16BIT(PORT_TCP); /* Prepare source port */
- /* Ack number need to add 1 for computing ACK flag */
- tcpFrame->ackNumber = CONVERT_32BIT(CONVERT_32BIT(tcpFrame->sequNumber) + 1);
- tcpFrame->sequNumber = 3472621397U; /* Set sequence number */
- tcpFrame->flags = TCP_SYN | TCP_ACK; /* Set SYN and ACK Flags */
- tcpFrame->windowSize = WINDOW_SIZE; /* Set window size */
- tcpFrame->urgentPointer = 0; /* URG is not set so data to 0 */
- bufferLength = sizeof(tcp_frame_t)+4;
- tcpFrame->dataOffset = bufferLength << 2;
- /* Add MMS option */
- tcpFrame->dataBuffer[0] = 2;
- tcpFrame->dataBuffer[1] = 4;
- tcpFrame->dataBuffer[2] = 0x05;
- tcpFrame->dataBuffer[3] = 0x80;
- /* Clear checksum */
- tcpFrame->checkSum = 0;
- /* Calculate checksum */
- tcpFrame->checkSum=checksum((uint8_t*)tcpFrame-8, bufferLength+8, Prot_TCP);
- bufferLength += sizeof(ip_frame_t);
- ipFrame->packetLength = CONVERT_16BIT(bufferLength);
- ipFrame->packetId = 0;
- ipFrame->serviceType = 0;
- ipFrame->protVerion = 0x45;
- ipFrame->flags = 0;
- ipFrame->timeToLive = 64;
- ipFrame->controlType = 0;
- ipFrame->protocolType = IP_TCP; /* Protocol number */
- memcpy(ipFrame->destinIpAdress,ip_addr,4);
- memcpy(ipFrame->sourceIpAdress,ipaddr,4);
- ipFrame->controlType = checksum((void*)ipFrame,sizeof(ip_frame_t),0);
- memcpy(encFrame->destMacAddress, encFrame->sourceMacAddress, 6);
- memcpy(encFrame->sourceMacAddress, macaddr, 6);
- encFrame->frameType = ETH_IP;
- bufferLength += sizeof(enc28j60_frame_t);
- enc28j60PacketSend((void*)encFrame,bufferLength);
- #ifdef DEBUG_TCP_FIL
- sprintf(uartBuffer,"Syn Ack Buffer Length:%d\r\n", bufferLength);
- HAL_UART_Transmit(&huart2,(uint8_t*)uartBuffer,strlen(uartBuffer),0x1000);
- #endif
- }
Gdy zostaną przesłane dane od klienta to funkcja wchodzi do odebrania danych i przesłania odpowiedzi do klienta.
- static void tcpSendData(uint8_t *ip_addr, uint16_t port)
- {
- uint16_t receiveDataLength = 0;
- uint16_t bufferLength = 0;
- uint32_t segmentNumber = 0;
- char uartBuffer[50] = {0};
- enc28j60_frame_t *frame = (void*)net_buf;
- ip_frame_t *ipFrame = (void*)(frame->dataBuffer);
- tcp_frame_t *tcpFrame = (void*)(ipFrame->dataBuffer);
- receiveDataLength = CONVERT_16BIT(ipFrame->packetLength) - 20 - (tcpFrame->dataOffset>>2);
- tcpFrame->destPort = CONVERT_16BIT(port);
- tcpFrame->sourcePort = CONVERT_16BIT(PORT_TCP);
- segmentNumber = tcpFrame->ackNumber;
- tcpFrame->ackNumber = CONVERT_32BIT(CONVERT_32BIT(tcpFrame->sequNumber) + receiveDataLength);
- #ifdef DEBUG_TCP_FIL
- sprintf(uartBuffer,"receiveDataLength:%u\r\n", receiveDataLength);
- HAL_UART_Transmit(&huart2,(uint8_t*)uartBuffer,strlen(uartBuffer),0x1000);
- #endif
- /* Prepare ACK frame */
- tcpFrame->sequNumber = segmentNumber;
- tcpFrame->flags = TCP_ACK;
- tcpFrame->windowSize = WINDOW_SIZE;
- tcpFrame->urgentPointer = 0;
- bufferLength = sizeof(tcp_frame_t);
- tcpFrame->dataOffset = bufferLength << 2;
- tcpFrame->checkSum = 0;
- tcpFrame->checkSum = checksum((uint8_t*)tcpFrame-8, bufferLength+8, Prot_TCP);
- bufferLength+=sizeof(ip_frame_t);
- /* prepare IP*/
- ipFrame->packetLength = CONVERT_16BIT(bufferLength);
- ipFrame->packetId = 0;
- ipFrame->serviceType = 0;
- ipFrame->protVerion = 0x45;
- ipFrame->flags = 0;
- ipFrame->timeToLive = 64;
- ipFrame->controlType = 0;
- ipFrame->protocolType = IP_TCP;
- memcpy(ipFrame->destinIpAdress,ip_addr,4);
- memcpy(ipFrame->sourceIpAdress,ipaddr,4);
- ipFrame->controlType = checksum((void*)ipFrame,sizeof(ip_frame_t),0);
- memcpy(frame->destMacAddress, frame->sourceMacAddress, 6);
- memcpy(frame->sourceMacAddress, macaddr, 6);
- frame->frameType = ETH_IP;
- bufferLength += sizeof(enc28j60_frame_t);
- enc28j60PacketSend((void*)frame,bufferLength);
- //--------------------------------------------------------------------------
- /* Send data to client */
- strcpy((char*)tcpFrame->dataBuffer,"Data Received\r\n");
- tcpFrame->flags = TCP_ACK|TCP_PSH;
- bufferLength = sizeof(tcp_frame_t);
- tcpFrame->dataOffset = bufferLength << 2;
- bufferLength += strlen((char*)tcpFrame->dataBuffer);
- /* Clear checksum */
- tcpFrame->checkSum = 0;
- tcpFrame->checkSum = checksum((uint8_t*)tcpFrame-8, bufferLength+8, Prot_TCP);
- bufferLength += sizeof(ip_frame_t);
- ipFrame->packetLength = CONVERT_16BIT(bufferLength);
- ipFrame->controlType = 0;
- ipFrame->controlType = checksum((void*)ipFrame,sizeof(ip_frame_t),0);
- bufferLength+=sizeof(enc28j60_frame_t);
- enc28j60PacketSend((void*)frame,bufferLength);
- }
Dla weryfikacji nadawania oraz odbierania stosowane są sumy kontrolne oraz sekwencyjne numery pakietów. Odbiorca potwierdza otrzymanie pakietów przez ustawienia flagi ACK.
Zakończenie transmisji odbywa się za pomocą następującej funkcji:
- static void tcpFin(uint8_t *ip_addr, uint16_t port)
- {
- uint16_t bufferLength = 0;
- uint32_t segmentNumber = 0;
- enc28j60_frame_t *frame = (void*)net_buf;
- ip_frame_t *ipFrame = (void*)(frame->dataBuffer);
- tcp_frame_t *tcpFrame = (void*)(ipFrame->dataBuffer);
- tcpFrame->destPort = CONVERT_16BIT(port);
- tcpFrame->sourcePort = CONVERT_16BIT(PORT_TCP);
- segmentNumber = tcpFrame->ackNumber;
- tcpFrame->ackNumber = CONVERT_32BIT(CONVERT_32BIT(tcpFrame->sequNumber) + 1);
- tcpFrame->sequNumber = segmentNumber;
- tcpFrame->flags = TCP_ACK; /* Set ACK flag */
- tcpFrame->windowSize = WINDOW_SIZE;
- tcpFrame->urgentPointer = 0;
- bufferLength = sizeof(tcp_frame_t);
- tcpFrame->dataOffset = bufferLength << 2;
- tcpFrame->checkSum = 0;
- tcpFrame->checkSum = checksum((uint8_t*)tcpFrame - 8, bufferLength+8, Prot_TCP);
- bufferLength += sizeof(ip_frame_t);
- /* IP part */
- ipFrame->packetLength = CONVERT_16BIT(bufferLength);
- ipFrame->packetId = 0;
- ipFrame->serviceType = 0;
- ipFrame->protVerion = 0x45;
- ipFrame->flags = 0;
- ipFrame->timeToLive = 64;
- ipFrame->protocolType = IP_TCP;
- memcpy(ipFrame->destinIpAdress, ip_addr, 4);
- memcpy(ipFrame->sourceIpAdress, ipaddr, 4);
- ipFrame->controlType = 0;
- ipFrame->controlType = checksum((void*)ipFrame,sizeof(ip_frame_t),0);
- memcpy(frame->destMacAddress, frame->sourceMacAddress, 6);
- memcpy(frame->sourceMacAddress, macaddr, 6);
- frame->frameType = ETH_IP;
- bufferLength+=sizeof(enc28j60_frame_t);
- enc28j60PacketSend((void*)frame,bufferLength);
- tcpFrame->flags = TCP_FIN|TCP_ACK; /* Set ACK and FIN flag */
- bufferLength = sizeof(tcp_frame_t);
- tcpFrame->checkSum = 0;
- tcpFrame->checkSum = checksum((uint8_t*)tcpFrame-8, bufferLength+8, Prot_TCP);
- bufferLength += sizeof(ip_frame_t);
- bufferLength += sizeof(enc28j60_frame_t);
- enc28j60PacketSend((void*)frame,bufferLength);
- #ifdef DEBUG_TCP_FIL
- HAL_UART_Transmit(&huart2,(uint8_t*)"FIN Transmit End\r\n",5,0x1000);
- #endif
- }
Zakończenie połączenia może być wykonane przez klienta jak i przez serwer. Przesyłany pakiet ma ustawioną flagę FIN. Jego przesłanie wymaga odpowiedzi w postaci ramki ACK.
Działanie programu polega na przesłaniu zapytania przez klienta do serwera. On wtedy odpowiada przykładowymi danymi z odpowiedzią:
Cały projekt można pobrać z dysku Google pod tym linkiem w folderze STM32 a następnie STM32F4.