[ENC28J60] Bài 10: Giao thức UDP

Tiếp tục bài trước trong bài này, chúng ta sẽ tìm hiểu về giao thức UDP. UDP (User Datagram Protocol) là một trong những giao thức cốt lõi của giao thức TCP/IP. Dùng UDP, chương trình trên mạng máy tính có thể gửi những dữ liệu ngắn được gọi là datagram tới máy khác.

Giao thức UDP không cung cấp sự tin cậy và thứ tự truyền nhận mà TCP làm, các gói dữ liệu có thể đến không đúng thứ tự hoặc bị mất mà không có thông báo. Tuy nhiên UDP nhanh và hiệu quả hơn đối với các mục tiêu như kích thước nhỏ và yêu cầu khắt khe về thời gian.

Do bản chất không trạng thái của nó nên nó hữu dụng đối với việc trả lời các truy vấn nhỏ với số lượng lớn người yêu cầu.

Cổng

UDP dùng cổng để cho phép các giao tiếp giữa các ứng dụng diễn ra.

Cổng dùng 16 bit để đánh địa chỉ, vì vậy số của cổng nằm trong khoản 0 đến 65.535. Cổng 0 được để dành và không nên sử dụng.

Cổng từ 1 đến 1023 được gọi là cổng “well-known” và trên các hệ điều hành tựa Unix, việc gắn kết tới một trong những cổng này đòi hỏi quyền root.

Cổng 1024 đến 49.151 là cổng đã đăng ký.

Cổng từ 49.152 đến 65.535 là các cổng tạm, được dùng chủ yếu bởi client khi liên lạc với server.

Cấu trúc chung

Nhận dạng

UDP có mã Protocol (thuộc gói IP) là 0x11

Cấu trúc gói UDP

UDP là giao thức hướng thông điệp nhỏ nhất của tầng giao vận

Vị tí của UDP trong các tầng

UDP không đảm bảo cho các tầng phía trên thông điệp đã được gửi đi và người gửi cũng không có trạng thái thông điệp UDP một khi đã được gửi

Cấu trúc của gói UDP

Không có gì để giải thích, cấu trúc của các gói UDP rất đơn giản

Chúng ta sẽ bắt tay vào viết thư viện udp luôn. Tiếp tục tạo các fule udp.hudp.c

Sao chép mã khởi tạo cho file .h

Tiếp tục mã khởi tạo cho file .c

Khởi tạo cấu trúc cho frame ở file .h

Trong thư viện net.c tại hàm NET_ipRead chúng ta kiểm tra trường Protocol để xem có phải gói UDP, nếu đúng thì ném gói này cho thư viện udp.c xử lí

(nhớ thêm nguyên mẫu hàm vào file udp.h và add thư viện udp.h vào file net.h)

Bây giờ mình sẽ tạo hàm UDP_read trong thư viện udp.c

Mình sẽ chạy thử mô phỏng và cho phần mềm Hercules gửi thử các gói UDP đến ENC28J60 xem chúng ta đã nhận được gói UDP nào chưa

giao thức udp enc28j60

Toẹt vời ! Đã bắt được các gói UDP

Ngoài IP , UDP còn sử dụng thêm Port (cổng) để xác định nguồn và đích nhận dữ liệu ! Mình sẽ cho ENC28J60 cho phép nhận tin nhắn từ tất cả các cổng ! Do vậy chúng ta chỉ cần quan tâm Source Port ( với tin gửi đến ENC28J60 ) và Dest Port ( với tin mà ENC28J60 gửi đi)

Chúng ta sẽ đọc trường Length để xác định độ dài của tin nhắn gửi đến và in ra màn hình debug nhé.

Các bạn mở file net.h rồi thêm vào 2 #define này nhé

Do các biến kiểu 16 và 32 trong struct có byte thấp đứng trước byte cao nên mình #define thêm macro để đảo ngược nó lại

Tiếp tục với hàm UDP_read, mình sẽ in nội dung của tin nhắn ra

giao thức udp
giao thức udp

Mình sẽ vẽ thêm 4 con LED vào để test thử chức năng điều khiển LED nhé

Mình sẽ xài hàm strstr để check

Và test gửi tin nhắn điều khiển bằng phần mềm Hercules

Xây dựng hàm gửi tin nhắn UDP

Để gửi gói tin UDP tới 1 địa chỉ IP, chúng ta cần biết MAC tương ứng của IP đó. Do vậy, mình sẽ check IP trong bản ARP_table, nếu có rồi thì lấy xài, nếu chưa có thì phát hành bản tin ARP_request để lấy IP của nó

Mình sẽ khai bảo 1 cổng mặc định để cho ENC28j60 gửi đi là cổng 20798. Các bạn thêm #define cho nó vào file udp.h

Trong thư viện arp.c mình sẽ viết thêm API để lấy MAC trong bảng

Đừng quên thêm nguyên mẫu hàm cho nó vào file arp.h . Chúng ta sẽ bắt tay vào viết hàm UDP_send

Giải thích: Để gửi các gói tin UDP đi, mình sẽ phát hành một boardcast (ARP request) tới địa chỉ IP nhận, chờ nó phản hồi để lấy MAC, nếu không thấy phản hồi (hết time oụt) thì không gửi. Sau khi có phản hồi MAC. Chúng ta make gói tin và gửi đi

Trong hàm gửi đi, mình có viết thêm 2 hàm tính checksum cho gói IP và gói UDP. Giao thức UDP không quan trọng checksum, nếu không cần checksum, các bạn có thể để checksum mặc định = 0x00, nhưng vì đang học nên chúng ta cứ viết hàm tính cho nó nhé

Tính checksum

Trước tiên là hàm checksum cho gói IP, các bạn quay trở lại file net.c để tạo thêm hàm checksum cho gói IP. Nội dung các hàm checksum nó ý chang nhau, chẳng qua địa chỉ bắt đầu và độ dài của mỗi loại checksum khác nhau 1 tí thôi, nên mình không giải thích lại hàm checksum nữa !

Nhớ khai báo nguyên mẫu hàm vào file net.h nhé nhé

Tiếp tục viết thêm hàm checksum cho gói UDP ở file udp.c

Checksum của gói UDP gồm: IP Protocol (0x11) + IP source + IP dest + toàn bộ UDP packet + UDP packet length

checksum udp
tính checksum cho udp

OK rồi ! Giờ trong hàm nhận udp điều khiển LED mình sẽ phản hồi lại cho máy tính nhé

Các bạn có thể tải source cho bài này tại đây

Như vậy, chúng ta đã hoàn thành các giao thức cơ bản, nếu hiểu hết các bài học tới thời điểm này, các bạn hoàn toàn có thể tự nghiên cứu để viết thêm TCP – giao thức chính, cũng là giao thức chúng ta cần nhất. Thực tế, giao thức TCP khó hơn các giao thức chúng ta đã học rất nhiều. Trong thời gian tới, mình sẽ cố gắng làm thêm về TCP cho các bạn tham khảo !

Lộ trình sắp tới sẽ là TCP Server -> TCP Client -> Socket -> MQTT

Các bạn giúp mình chia sẻ rộng rãi tutorial để mình có động lực viết thêm, và nếu thấy khóa học giúp ích ít nhiều cho các bạn, các bạn có thể donate ít mì tôm cho mình tại thông tin ở cuối website ! Rất cảm ơn sự ủng hộ của các bạn !

 

Từ tác giả:

Nếu có bất kì thắc mắc nào trong bài viết, vui lòng để lại comment dưới mỗi bài ! Mình sẽ không trả lời thắc mắc của các bạn ở facebook hay email !

Nếu trong phần code bạn nhìn thấy nhưng thứ kiểu như &amp; thì đó là lỗi hiển thị, cụ thể 3 kí tự < > & bị biến đổi thành như thế
&amp; là &
&lt;  là <
&gt; là >

Giới thiệu Đào Nguyện 80 bài viết
DIY,chế cháo, viết blog chia sẽ kiến thức về lập trình,điện tử - IoT. Rất mong được giao lưu, kết bạn với các bạn cùng đam mê. Địa chỉ Facebook: https://www.facebook.com/nguyendao207

5 bình luận

  1. Mình có theo dõi khóa học của bạn, mình có làm theo, gửi các gói ICMP, ARP thì đều ok, đến gói UDP và TCP thì mình gửi packet lên mạng LAN không được, đặc biệt là UDP, mình làm giao thức UDP lần đầu tiên thì gửi phản hồi “Da ON LED 1” lên máy tính được nhưng sang hôm sau ko làm gì mà lại không gửi được nữa, mình cũng ko hiểu tại sao, mọi thứ mình đều làm giống bạn, ko biết bạn có gặp trường hợp như mình không, xin hồi đáp ạ

  2. Hi anh, em có theo dõi serie về ENC28J60 của anh ạ, em làm theo anh y hệt mọi thứ, nhưng đến phần UDP thì em nhận được packet từ máy tính gửi xuống nhưng không gửi phản hồi lại cho máy tính được, không biết anh có từng bị giống em chưa ạ và fix thế nào ạ, mong anh trả lời, tks anh

      • Em có dùng WireShark, nhưng WireShark không bắt được gói tin em gửi đi ạ, mọi thứ đều đúng hết anh, em làm theo y hệt anh, lúc gửi gói tin UDP em cũng in ra màn hình giá trị các trường thì cũng đều đúng hết, nhưng không hiểu sao lại ko gửi được

        • Bạn thử đổi ip của 28j60 trong proteus trùng với ip của máy tính thử xem có bắt được không. Mình nghĩ do mô phỏng nên lúc đầu proteus sẽ mượn phần cứng thật của máy tính để giả lập.
          Khi chạy mô phỏng thì ip 28j60 mô phỏng sẽ nhận ip tĩnh mà mình thiết lập trong code.

Đã đóng bình luận.