Giáo trình Kỹ thuật mô phỏng (Phần 2)

CHƢƠNG 7. QUẢN LÝ VÙNG ĐỆM VÀ ĐƢỜNG LIÊN KẾT  
Mục tiêu  
Liên kết (Link) là một đối tƣợng OTcl kết nối giữa hai nút để truyền các gói tin từ  
nút bắt đầu nguồn nút đích. Nội dung của chƣơng này chủ yếu tập trung vào lớp có tên là  
SimpleLink là Lớp đƣợc sử dụng rộng rãi nhất trong số các lớp thừa kế từ Lớp Link. Đối  
tƣợng SimpleLink mô hình hóa thời gian truyềứ gói tin, độ trễ của kênh truyền và bộ đệm  
gói tin để truyền các gói tin từ một nút này tới một nút khác, ở đây, thời gian truyền gói  
tin là thời gian cần thiết đề thiết bị truyền gửi đi một gói tin. Nó đƣợc xác định bởi băng  
thông của kênh truyền và kích cỡ gói tin. Thời gian trễ của kênh truyền là thời gian cần  
thiết để mang mỗi bit từ điểm đầu tới điểm cuối trên kênh truyền. Một thiết bị truyền có  
thể nhận đƣợc các gói tin trong khi nó đang truyền các gói tin khác. Các gói tin đi đến  
một thiết bị truyền đang bận có thể đƣợc đƣa vào một bộ đệm để truvền đi ở thời điểm  
sau. Không giống nhƣ trong thực tế, NS2 triển khai việc đệm gói tin trong một kênh  
truyền chứ không phải trong một nút.  
7.1. Giới thiệu về đối tƣợng SimpleLink  
NS2 mô hình hóa một kênh truyền bằng các sử dụng các Lớp đƣợc dẫn xuất từ  
Lớp Link của OTcl. Trong số các Lớp này, Lớp SimpleLink là lớp đơn giản nhất có thể  
đƣợc sử dụng để kết nối giữa hai nút.  
.
Các thành phần chính của một Lớp SimpleLink  
Hình 7.1 đƣa ra một hình ảnh kết hợp giữa các thành phần của Lớp SimpleLink  
với các đối tƣợng cơ bản và các đối tƣợng lƣu vết trong cây thông dịch nhƣ sau:  
Hình 7.1. Kiến trúc của một đối tƣợng SỉmpỉeLink  
Các đối tƣợng cơ bản  
head_: Điểm truy cập của đối tƣợng SỉmpleLink  
queue_: Là một đối tƣợng Queue, nó thực hiện mô hình hóa bộ đệm gói tin của  
một router thực (xem Mục 7.3).  
link_: Là đối tƣợng DelayLỉnk, thực hiện mô hình hóa thời gian truvền gói tin  
và thời gian trễ của kênh truyền (xem Mục 7.2)  
ttl_: Là một đối tƣợng kiểm tra thời gian sống (time to live) của lớp  
TTLChecker. Khi một gói tin tới, đối tƣợng này thực hiện việc giảm giá trị TTL của  
gói tin. Sau đó nó kiểm tra nếu giá trị TTL của gói tin vẫn là giá trị dƣơng, gói tin sẽ  
- 131 -  
đƣợc chuyển tiếp tới thành phần tiếp theo trên kênh truyền. Ngƣợc lại, nó sẽ hủy gói  
tin ra khỏi quá trình mô phỏng (xem file "ns/common/ttl.h.cc).  
đropheacL: Là điểm hủy gói tin của kênh truyền. Các gói tin bị hủy sẽ đƣợc  
chuyển tiếp tới đối tƣợng này. Nó thƣờng đƣợc kết nối với tác tử rỗng của đối tƣợng  
Simulator để mọi đối tƣợng SỉmpleLink có thể chia sẻ cùng một điểm hủy gói tin.  
Các đối tƣợng lƣu vết  
Các đối tƣợng này sẽ đƣợc chèn vào chỉ khi biến thành viên $traceAHFile_ đƣợc  
định nghĩa. Các đối tƣợng này bao gồm:  
enqT_: Lƣu vết các gói tin đi vào hàng đợi queue_ deqT_: Lƣu vết các gói tin đi ra  
từ hàng đợi queue_ drpT_: Lƣu vết các gói tin bị hủy từ hàng đợi queue_  
rcvT_: Lƣu vết các gói tin đi ra khỏi kênh truyền hoặc đƣợc các nút tiếp theo  
nhận.  
7.1.1. Các thủ tục instproc để cấu hình đối tƣợng SimpleLink  
Trong miền OTcl, một đối tƣợng SỉmpleLỉnk đƣợc tạo ra bằng cách sử dụng thủ  
tục instproc simplex-lỉnkL"} và duplex-lỉnkr---} của lớp Simulator với cú pháp nhƣ sau:  
Trong đó $ns là đối tƣợng Simulator và $nl, $n2 là các đối tƣợng Node. Thủ tục instproc  
simplex-Tink{”-} ở trên tạo một đối tƣợng SimpleLink kết nối 1 chiều giữa nút $nl với  
nút $n2 (Chƣong trình 7.1). Tốc độ và thời gian trễ của kênh truyền đƣợc đƣa vào tham  
số <bandwỉdth> (tính theo đon vị bps) và <delay> (tính theo đon vị giây). Khác với  
router thực, NS2 tích hợp hàng đợi trong đối tƣợng SimpleLỉnk chứ không phải trong  
đối tƣợng Node. Kiểu của hàng đợi trong kênh truyền đƣợc chỉ định bởi tham số  
<queue_type>.  
- 132 -  
Chƣơng trình 7.1 đƣa ra chi tiết của thủ tục Simulator-simplex- link{"}. Dòng 6  
tạo một đối tƣợng của lớp Queue/qtype. Dòng 12 khởi tạo một đối tƣợng SimpleLink  
kết nối giữa nút n1 và n2. Nó thiết lập độ trễ, băng thông và đối tƣợng Queue của kênh  
truyền tƣơng ứng là bw, delay và q. Đối tƣợng Simulator lƣu đối tƣợng SimpleLink  
đƣợc tạo ra vào mảng liên kết link_($sid:$did) của nó với sid là ID định danh của nút  
nguồn và did là ID định danh của nút đích (xem Chƣơng 4).  
Thủ tục duplex-link "-} tạo ra hai đối tƣợng SimpleLink. Một đối tƣợng kết nối  
từ nút $n1 tới nút $n2 và một đối tƣợng kết nối từ nút $n2 tới nút $n1. Bạn đọc có thể  
xem chi tiết của thủ tục này trong file ~ns/tcl/lib/ns-lib.tcl.  
7.1.2. Khỏi tạo của Lớp SimpleLink  
Chƣơng trình 7.2 đƣa ra chi tiết của thủ tục init{ —} của lớp SỉmpleLỉnk để khởi  
tạo và kết nối các đối tƣợng theo Hình 7.1. Trong chƣơng trình này các dòng 3, 5, 11, 12  
và 18 tạo ra các biến thành viên drophead_, head_ queue_ link_ và ttl_ là các đối tƣợng  
của các lớp OTcl tƣơng ứng là Connector Connector, Queue, DelayLink và TtlChecker.  
Chú ý rằng băng thông và độ trễ của biến link_ đƣợc cấu hình trong dòng 13-14  
Ngoài việc tạo ra các đối tƣợng trên, thủ tục khởi tạo này còn kết nối các đối  
tƣợng đƣợc tạo ra nhƣ Hình 7.1. Đƣợc dẫn xuất từ lớp Connector, mỗi một đối tƣợng  
- 133 -  
đƣợc tạo ra sử dụng lệnh target và drop-target để thiết lập đối tƣợng nhận gói và đối  
tƣợng hủy gói (xem Chƣơng 5). Dòng 9 thiết lập đối tƣợng nhận của head_ là q. Dòng 15  
thiết lập đối tƣợng nhận của queue_ là link_. Dòng 16 thiết lập đối tƣợng nhận của link_  
là điểm truy cập của nút kế tiếp. Dòng 19 và 21 chèn biến ttl_ vào giữa lỉnk_ và điểm  
truy cập của nút kế tiếp. Dòng 17 thiết lập điểm hủy gói tin của queue_ là dropheacL.  
Cuối cùng, dòng 4 thiết lập đích của drophead_ là tác tử rỗng của đối tƣợng Simulator.  
7.2. Mô hình truyền gói tin  
. Cơ chế truyền gói tín  
NS2 mô hình hóa cơ chế truyền gói tin bằng cách sử dụng lớp C++ LinkDelay  
(xem Chƣơng trinh 7.3). Lớp C++ này đƣợc liên kết với lớp DelayLink của OTcl. Lớp  
DelayLink đƣợc sử dụng để khởi tạo biến thành viên SimpleLink::Link_. Đây là biến  
dùng để mô hình hóa tiến trinh truyền gói tin.  
Một tiến trình truyền gói tin bao gồm thời gian truyền gói tin và thời gian trễ của  
kênh truyền. Khi một đối tƣợng LinkDelay nhận một gói tin, nó đƣa vào dòng thời gian  
mô phỏng hai sự kiện:  
- 134 -  
Sự kiện truyền gói tin đi từ đối tượng truyền: Thời gian truyền gói tin  
đƣợc định nghĩa bằng (kích thƣớc gói tin / băng thông). Đây là thời gian cần thiết để  
truyền một gói tin qua một kênh truyền. Sau một chu kỳ của thời gian truyền gói tin, gói  
tin hoàn toàn đi ra khỏi thiết bị truyền và thiết bị truyền đƣợc phép tiếp tục truyền gói tin  
tiếp theo. Nhờ vào sự kiện nhận gói tin một đối tƣợng LinkDelay sẽ đợi một chu kỳ thời  
gian truyền gói tin và báo cho đối tƣợng gửi biết rằng nó đã sẵn sàng để nhận một gói tin  
tiếp theo.  
Sự kiện nhận gói tin về tại đối tượng nhận: Thời gian trễ khi truyền  
gói tin đƣợc định nghĩa bằng thời gian cần thiết để truyền một bit từ điểm bắt đầu tới  
điểm kết thúc trên một kênh truyền. Nhƣ vậy, toàn bộ gói tin cần một khoảng thời gian  
bằng ―thời gian truyền + thời gian trễ‖ để đi tới đích. Do đó, đối tƣợng LinkDelay sẽ lập  
lịch cho sự kiện nhận gói tin tại nút nhận sau khoảng thời gian trên.  
. Lớp LinkDelay trong C++  
Chƣơng trình 7.3 đƣa ra khai báo của Lớp C++ LinkDelay. Lớp này có bốn biến  
chính. Biến bandwidth_ (dòng 15) lƣu băng thông của kênh truyền. Biến delay_ (dòng  
16) lƣu thời gian trễ khi truyền. Dòng 4 và 5 liên kết hai biến này với các biến có cùng  
tên trong OTcl. Lớp LỉnkDelay lƣu mọi gói tin sẽ đƣợc truyền trong bộ đệm itq_ của nó  
(dòng 17). Biến itq_ là một con trỏ trỏ tới đối tƣợng PacketQueue (xem Mục 7.3.1).  
Cuối cùng, biến ỉntr_là một đối tƣợng Event giả biểu diễn sự kiện gói tin rời khỏi nút  
truyền. Sự kiện này đƣơc lập lịch bằng cách sử dụng biến ỉntr_ mà không cần đến cơ chế  
thực thi sự kiện (xem Mục 4.3.6).  
Các hàm chính của Lớp LinkDelay là hàm recv(p,h), hàm send(p,h), hàm  
handle(e) và hàm txttime(p). Hàm txttỉme(p) tính thời gian truyền của gói tin *p (dòng  
10-12 trong Chƣơng trình 7.3). Hàm send(p,h) gửi gói tin *p tới đối tƣợng nhận (dòng  
12 trong Chƣơng trinh 7.3). Hàm handle(e) đƣợc gọi khi bộ lập lịch thực thi sự kiện  
tƣơng ứng với đối tƣợng LinkDelay (xem Chƣơng 4). Hàm recv(p.h) lập lịch cho các sự  
kiện gói tin rời khỏi nút truyền và gói tin tới nút nhận từ hai đối số đầu vào là gói tin *p  
và thẻ quản lý *h.  
- 135 -  
Sự kiện gói tin rời khỏi nút truyền: Vì một gói tin cần ‗thời gian truyền‖ (biến txt  
trong dòng 3) tại đối tƣợng truyền nên hàm recv(p,h) sẽ lập lịch cho sự kiện gói tin rời  
khỏi nút truyền tại thời điểm txt giây sau khi đối tƣợng LỉnkDelay nhận đƣợc gói tin. Để  
thực hiện việc này, dòng 10 gọi hàm scheđule(h,Sántr,txt) của lớp Scỉieđuler với đối số  
thứ nhất là thẻ quản lý, đối số thứ hai là sự kiện giả và đối số thứ ba là thời gian trễ. Sau  
khoảng thời gian txt giây, bộ lập lịch thực thi sự kiện này bằng cách gọi hàm handle(e)  
liên kết với thẻ quản lý *h để báo cho đối tƣợng gửi biết sự kiện một gói tin đã rời đi.  
Trong hầu hết mọi trƣờng hợp, đối tƣợng gửi đáp ứng lại bằng cách truyền một gói tin  
tiếp theo nếu có (xem Mục 7.3.3 về cơ chế gọi ngƣợc).  
Sự kiện gói tin đến: Lớp LinkDelay cũng thực hiện việc truyền gói tin tới đối  
tượng nhận (*target). Dòng 8 thực hiện việc lập lịch cho một sự kiện lấy từ gói tin đầu  
vào *p với thời gian trễ txt+đelay_ giây. Trong đó, txt là thời gian truyền và đelay_ là  
thời gian trễ trên kênh truyền. Ở đây, *target_ được truyền từ hàm schedule( - ) dưới  
dạng một con trỏ Handler. Sau khoảng thời gian “txt+delay_” giây, hàm h.handle(p) sẽ  
gọi hàm recv(p) (xem Chương trình 4.2) và gói tin *p sẽ được truyền tới đối tượng  
*target_ sau khoảng thời gian txt+delay_ giây.  
Sự khác biệt chính giữa việc lập lịch cho sự kiện gói tin rời đi từ nguồn và gói tin  
đến đích là nhƣ sau: Trong khi một nút chỉ có thể giữ một gói tin thì một kênh truyền có  
thể chứa nhiều hơn một gói tin. Vì thế, một đối tƣợng Link chỉ có thể lập lịch cho một sự  
kiện gói tin rời đi từ nguồn (sử dụng biến intr_) và nhiều hơn một sự kiện gói tin đến đích  
(sử dụng *p để biểu diễn một gói tin). Bất kỳ lúc nào đối tƣợng LinkDelay nhận đƣợc  
một gói tin, nó sẽ lập lịch cho sự kiện gói tin rời đi từ nguồn bằng cách sử dụng biến sự  
kiện giả ỉntr_. Nếu biến intr_ không thực thi đƣợc, bộ lập lịch sẽ gây ra lỗi trong thời gian  
chạy bởi vì nó cố đƣa một gói tin vào đầu bộ đệm mà bộ đệm này lại đang bị chiếm giữ  
bởi gói tin khác. Ngƣợc lại, đối với sự kiện gói tin đến đích, đối tƣợng LinkDelay lập  
lịch cho sự kiện gói tin tới nút đích cho mọi gói tin nó nhận đƣợc (xem dòng 8 trong  
Chƣơng trình 7.4). Do đó, một liên kết có thể lập lịch cho một sự kiện gói tin đến đích  
trong khi sự kiện gói tin tới đích trƣớc đó vẫn chƣa đƣợc thực thi. Đây là trƣờng hợp rất  
phổ biến đối với các kênh truyền có thời gian trễ lớn có thể chứa nhiều gói tin đồng thời.  
7.3. Quản lý vùng đệm  
Một thành phần chính khác của đối tƣợng SimpleLink là lớp Queue. Lớp Queue  
mô hình hoá cơ chế bộ đệm trong router. Nó lƣu gói tin nhận đƣợc trong bộ đệm, chuyển  
tiếp lần lƣợt từng gói tin ở đầu bộ đệm tới đối tƣợng nhận.  
- 136 -  
Nhƣ ta thấy trong Chƣong trình 7.5, lớp Queue là lớp dẫn xuất từ Lớp Connector  
và nó đƣợc sử dụng để kết nối giữa hai đối tƣợng NsObject. Nó khai báo đối tƣợng *pq_  
của Lớp PacketQueue (dòng 20) dừng làm bộ đệm gói tin. Kích thƣớc bộ đệm chứa  
trong biến qlim_ (dòng 16). Các biến blockecL (dòng 17), unblock_on_resume_ (dòng  
18) và qh_ (dòng 19) có liên quan tới cơ chế gọi ngƣợc đƣợc trinh bày trong Mục 7.3.3.  
Có một số hàm quan trọng sau trong Lớp Queue: Hàm enque(p) và deque() (dòng  
3-4) thực hiện các công việc tƣơng ứng là đƣa gói tin vào và lấy gói tin ra từ đối tƣợng  
*pq_ của Lớp PầcketQueue. Các hàm này đều là các hàm ảo nguyên thuỷ và chúng cần  
đƣợc xây dựng lại trong các Lớp dẫn xuất từ Lớp Queue/Hàm recv(p,h) (dòng 5) thừa kế  
từ Lớp NsObject là hàm thực hiện việc nhận gói tin. Hàm blocked) (dòng 7) là hàm kiểm  
tra trạng thái kho á của đối tƣợng Queue. Các hàm resume() (dòng 6), unblock() (dòng 8)  
và block() (dòng 9) đƣợc sử dụng trong cơ chế gọi ngƣợc sẽ đƣợc đề cập chi tiết trong  
Mục 7.3.3. Cuối cùng, hàm lỉmito và lengthO thực hiện các công việc tƣơng ứng là lấy  
giá trị kích cỡ tối đa và kích cỡ hiện tại của bộ đệm.  
. Lớp PacketQueue  
Đƣợc khai báo trong Chƣơng trình 7.6, lớp PacketQueue mô hình hoá các hoạt  
động cấp thấp của bộ đệm bao gồm lƣu trữ, đƣa gói vào hàng đợi và xuất gói ra khỏi  
hàng đợi.  
- 137 -  
Đối tƣợng PacketQueue là một danh sách liên kết của các con trỏ đối tƣợng  
Packets. Nó bao gồm các biến và các hàm thành viên sau:  
7. Biến head_ (dòng 11): là một con trỏ trỏ vào phần tử đầu danh sách liên  
kết.  
8. Biến tail_ (dòng 12): là một con trỏ trỏ vào phần tử cuối danh sách liên kết  
9. Biến len_ (dòng 13); chứa số lƣợng gói tin trong bộ đệm  
10.Hàm enque(p) (dòng 5): đƣa gói tin *p vào cuối bộ đệm.  
11.Hàm deque() (dòng 6): trả về phần tử con trỏ đầu bộ đệm trong trƣờng hợp  
bộ đệm không rỗng. Ngƣợc lại nó sẽ trả về giá trị NULL.  
12.Hàm remove(p) (dòng 7): tìm kiếm gói tin *p và xoá nó khỏi bộ đệm nếu  
tìm thấy.  
. Lớp QueueHandler  
Lớp QueueHandler là Lớp dẫn xuất từ Lớp Handler (dòng 1 trong Chƣơng trình  
7.7). Nó có mối quan hệ chặt chẽ với bộ lập lịch sự kiện. Lớp này định nghĩa hành động  
mặc định của mình trong hàm handle(e). Các hành động mặc định này sẽ đƣợc thực thi  
khi sự kiện liên kết với nó đƣợc thực thi.  
- 138 -  
Trong các dòng từ 8 tới 11 của Chƣơng trình 7.7, hành động mặc định của i đối  
tƣợng QueueHandler là thực hiện lệnh resume của đối tƣợng queue_ (lớp Queue) liên  
kết với nó. Ta sẽ thảo luận chi tiết về hàm resume trong Mục 7.3.3. Trong phần này ta  
chỉ tập trung vào nội dung cách tạo ra kết nối giữa các đối tƣợng QueueHandler và đối  
tƣợng Queue.  
Để liên kết một đối tƣợng Queue với một đối tƣợng QueueHandler, Lớp Queue  
khai báo biến thành viên qh_ (dòng 19 trong Chƣong trình 7.5) và Lớp QueueHandler  
khai báo biến thành viên queue_ (dòng 12 trong Chƣơng trình 7.7). Hai biến này đƣợc  
khởi tạo khi một đối tƣợng Queue đƣợc tạo ra (dòng 12 trong.Chƣơng trình 7.7). Sau đó,  
hàm khởi tạo của qh_ thiết lập biến thành viên queue_ để chia sẻ chung địa chỉ của đối  
tƣợng đầu vào Queue (đối tƣợng queue_(q) trong dòng 3 của Chƣơng trình 7.7). Vỉ vậy  
kết nối hai chiều giữa đối tƣợng Queue và QueueHandler đã đƣợc tạo ra. Kể từ điểm này,  
các đối tƣợng Queue và QueueHandler tham chiếu tới nhau bằng các biến qh_ và queue_.  
. Cơ chế khoá hàng đợi và cơ chế gọi ngƣợc  
Cơ chế khoá hàng đợi  
NS2 sử dụng khái niệm ―khoá hàng đợi‖ để chỉ trạng thái hàng đợi đang truyền  
gói tin. Mặc định, một hàng đợi có thể truyền một gói tin tại một thời điểm. Nó không  
cho phép (khoá) truyền các gói tin khác cho tới khi hoàn thành việc truyền gói tin hiện  
tại. Một hàng đƣợc gọi là đang bị khoá (blocked_=l) khi nó đang truyền gói tin và nó có  
trạng thái mở khoá (blocked_=0) khi nó không truyền gói tin.  
Hình 7.2. Sơ đồ trạng thái của cơ chế khoá hàng đợi  
Hình 7.2 cho thấy sơ đồ trạng thái của cơ chế khoá hàng đợi. Khi hàng đợi ở trạng  
thái ―không khoá‖, nó cho phép truyền gói tin đi bằng cách thực hiệr lệnh ―target_-  
>recv(p,&qh_)‖. Sau đó nó chuyển sang trạng thái ―khoá‖. ( đây hàng đợi sẽ phải đợi cho  
đến khi nào việc truyền gói tin ra ngoài hoàj thành. Khi đó hàm resumeO sẽ đƣợc gọi.  
Sau điểm này, hàng đợi chuyển sanj trạng thái ―không khoá‖ và tiến trình trên lại đƣợc  
lặp lại.  
Cơ chế gọi ngƣợc  
Nhƣ đã đề cập trong Chƣơng 5, một nút trong NS2 truyền các gói tin tc Nút nhận  
bằng cách thực thi hàm recv(p,h) với *p là gói tin truyền đi và *h là Thẻ quản lý. Cơ chế  
gọi ngƣợc là tiến trình ở đó đối tƣợng nhận gọi ngƣợc cho đối tƣợng gửi để thực hiện một  
mục đích nào đó. Trong tiến trình khóa hàng đợi cơ chế gọi ngƣợc đƣợc sử dụng để mở  
- 139 -  
khóa cho một đối tƣợng Queue bằng cách gọi hàm resume) của đối tƣợng Queue ở nút  
gửi.  
Bây giờ ta sẽ giải thích chi tiết cơ chế gọi ngƣợc để mở khóa hàng đợi qua ị một ví  
dụ trong Hình 7.3  
Hình 7.3. Sơ đồ của cơ chế gọi ngƣợc dành cho tiến trình mở khóa hàng đợi  
Trong hình vẽ trên ta giả sử rằng các đối tƣợng sau đã đƣợc kết nối theo thứ tự  
sau: Đối tƣợng NsObject gửi, đối tƣợng Queue, đối tƣợng LinkDelay và I Đối tƣợng  
NsObject nhận. Ta cũng nhắc lại rằng, một đối tƣợng NsObject gửi ! gói tin *p bằng cách  
gọi hàm recv(p,h) của đối tƣợng nhận gói tin, trong đó *h I là Thẻ quản lý. Trong hầu hết  
mọi trƣờng hợp, Thẻ quản lý đầu vào *I1 đƣợc ; truyền cùng với gói tin *p nhƣ là đối số  
đầu vào của hàm recv(p,h). Tuy nhiên I cơ chế này lại có sự khác biệt đối với đối tƣợng  
Queue.  
Xét hàm recv(p,h) của Lớp Queue trong Chƣơng trình 7.8. Khi đối tƣợng I Queue  
nhận đƣợc gói tin *p, thay vì việc truyền ngay lập tức gói tin này tới đối I tƣợng nhận của  
nó, đối tƣợng Queue thực hiện việc đƣa gói tin vào bộ đệm I (dòng 3). Nhắc lại rằng, một  
đối tƣợng Queue chỉ cho phép truyền một gói tin chỉ khi nó không ở trạng thái khóa  
(dòng 4). Trong trƣờng hợp này, dòng 5 sẽ lấy về gói tin từ đầu bộ đệm. Nếu gói tin này  
hợp lệ (dòng 6), dòng 7 sẽ thiết lập trạng thái của đối tƣợng Queue là ―khóa‖ và dòng 8  
sẽ chuyển tiếp gói tin này ; tới đối tƣợng nhận kế tiếp của đối tƣợng Queue (chứa trong  
biến target_). Đối tƣợng Queue truyền con trỏ QueueHandler qh_ của nó (thay vì con trỏ  
Thẻ quản lý đi tới nó) tới đối tƣợng nhận kế tiếp. Con trỏ QueueHandler này đóng vai trò  
nhƣ một điểm tham chiếu cho cơ chế gọi ngƣợc mở khóa hàng đợi.  
- 140 -  
Từ Hình 7.3 ta thấy, đối tƣợng nhận kế tiếp của đối tƣợng Queue là đối tƣợng  
LỉnkDelay. Khi nhận đƣợc một gói tin, đối tƣợng LinkDelay sẽ lập lịch hai sự kiện: sự  
kiện gói tin rời đi và sự kiện gói tin đi đến (dòng 10 và 8 trong Chƣơng trình 7.4). Sự  
kiện gói tin đi đến đƣợc liên kết với đối tƣợng nhận kế tiếp (đối tƣợng *target_). Tại thời  
điểm thực thi, hàm handle(p) của đối tƣợng nhận kế tiếp sẽ gọi hàm recv(p) để nhận gói  
tin *p (xem Chƣơng trình 4.2).  
Hàm recv(p) của Lớp LinkDelay cũng đƣợc lập lịch cho sự kiện gói tin rời đi. Vì  
con trỏ Handler đầu vào là một con trỏ QueueHandler, sự kiện gói tin rời đi đƣợc liên kết  
với đối tƣợng qh_ kiểu QueueHandler. Tại thời điểm thực thi, bộ lập lịch gọi hàm  
handle(p) của đối tƣợng QueueHandler qh_.  
Trong Chƣơng trình 7.7, hàm này sau đó sẽ gọi đến hàm resume() để mở khóa  
cho đối tƣợng Queue liên kết với nó. Nhƣ vậy có thể thấy đối tƣợng LinkDelay lập lịch  
cho một sự kiện mà sự kiện này sẽ gọi ngƣợc cho đối tƣợng Queue đã gọi nó để mở khóa  
hàng đợi.  
Chƣơng trình 7.9 là mã nguồn của hàm resume(). Đầu tiên hàm này lấy gói tin ở  
đầu bộ đệm (dòng 3). Nếu bộ đệm không rỗng (dòng 4) thì dòng 5 sẽ gửi gói tin tới đối  
tƣợng nhận kế tiếp của hàng đợi mà không cần quan tâm tới trạng thái của hàng đợi là  
khóa hay mở. Trong trƣờng hợp này, biến blocked_ vẫn không có sự thay đổi. Nếu đối  
- 141 -  
tƣợng Queue đang ở trạng thái ―khóa‖, nó sẽ vẫn ở trạng thái khóa sau khi truyền gói tin.  
Điều này là phù hợp với sơ đồ trạng thái trong Hình 7.2. Nếu hàng đợi rỗi (bộ đệm rỗng),  
biến blocked. sẽ đƣợc ' thiết lập giá trị bằng 0 nếu cờ unblock_on_resume_ có giá trị  
bằng 0 và biến blocked_ sẽ đƣợc thiết lập giá trị bằng 1 trong trƣờng hợp cờ ,  
unblock_on_resume_ có giá tri bằng 1.  
. Lớp DropTail  
Lớp DropTail là một Lớp con của Lớp Queue. Nó đƣợc liên kết với Lớp  
Queue/DropTail của OTcl với mã nguồn đƣợc đƣa ra trong Chƣơng trình 7.10. Hàm  
khởi tạo của Lớp DropTaỉl tạo một con trỏ q_ (dòng 13) trỏ tới một đối tƣợng  
PacketQueue và thiết lập biến pq_thừa kế từ Lớp Queue bằng giá trị biến q_ (dòng 5).  
Trong quá trình thực hiện các hàm, Lớp DropTail tham chiếu tới bộ đệm của nó bằng  
biến con trỏ q_ thay cho biến con trỏ pq_ của Lớp cha. Nó cũng cho phép việc hủy gói tin  
tại đầu bộ đệm nếu cờ drop_front_ (dòng 14) đƣợc thiết lập giá trị bằng 1. Lớp  
DropTail không xây dựng lại hàm recv(p,h) từ lớp cha. Do đó, nó nhận gói tin qua hàm  
tecvíp.h) của Lớp Queue.  
Trong Chƣơng trình 7.11, hàm enque(p) đầu tiên sẽ kiểm tra gói tin đến có ; làm  
tràn bộ đệm hay không (dòng 3). Nếu có, hoặc là nó sẽ hủy gỏi tin đang I đứng đầu hàng  
đợi (dòng 5-7) hoặc là nó sẽ hủy gói tin ở cuối hàng đợi (dòng ! 9). ở đây, hàm drop(p)  
(dòng 7 và 9) là hàm thành viên của Lớp Connector ; (xem Chƣơng trình 5.4). Nếu bộ  
đệm còn đủ chỗ, dòng 10 sẽ đƣa gói tin (p) vào I cuối bộ đệm (q_).  
- 142 -  
7.4. Một ví dụ mạng Two-Node  
Ta đã giới thiệu về hai thành phần cơ bản của NS2 là nút và kênh truyền. Trên cơ  
sở của hai thành phần này, bây giờ ta sẽ tạo một mạng gồm hai nút với kênh truyền một  
chiều và cơ chế chuyển gói tin trong mạng nhƣ Hình 7.4  
Hình 7.4. Một mạng hai nút với kênh truyền một chiều và các hàm của lớp  
Simulator  
. Khỏi tạo mạng  
Mạng trong Hình 7.4 bao gồm một nút bắt đầu (n1), một nút kết thúc (n2) một  
kênh SimpleLink kết nối giữa nl và n2, một tác tử nguồn lớp giao vận(udp) và một tác tử  
nhận lớp giao vận(null). Mạng này đƣợc tạo ra bởi đoạn mã Tcl sau:  
Ở đây, lệnh $ns node tạo ra một đối tƣợng Node. Cơ chế bên trong của tiến trình  
khởi tạo đối tƣợng này đã đƣợc trình bày trong Mục 6.6. Lệnh $ns simplex-link $nl $n2  
<bw><đelay> DropTail tạo một đối tƣợng SimpleLink kết nối một chiều giữa nút nl và  
nút n2. Băng thông và độ trễ của kênh tƣơng ứng sẽ là <bw> bps và <delay> giây. Bộ  
đệm của kênh kiểu DropTail. Nhƣ đã trinh bày trong Mục 6.6.3, lệnh $ns attach-agent $nl  
$udp thiết lập đích của tác tử udp là đỉểm truy cập của nút n1. Lệnh $n2 attach-agent $n2  
$null cài đặt tác tử null vào bộ phân kênh của nút n2.  
. Cơ chế luồng gói tin  
- 143 -  
Các bƣớc để vận chuyển gói tin ―*p‖ từ tác tử udp tới tác tử null bao gồm:  
-
-
Tác tử udp gửi gói tin *p tới điểm truy cập của nút nl  
Gói tin *p đƣợc gửi tới bộ phân loại đứng đầu classifỉer_ (là đối tƣợng của  
Lớp DestHastClassifier) của nút nl  
-
Đối tƣợng classifỉer_ lớp DestHashClassỉfier kiểm tra header của gói tin *p.  
Trong trƣờng hợp này, đích của gói tin là nút n2. Do đó, bộ phân loại này sẽ chuyển tiếp  
gói tin tới đối tƣợng SimpleLink đứng đầu chuỗi liên kết.  
-
-
Đối tƣợng SimpleLink chuyển tiếp gói tin tới đối tƣợng Queue  
Đối tƣợng Queue đƣa gói tin vào hàng đợi. Nếu hàng đợi ở trạng thái không  
khóa, nó sẽ chuyển tiếp gói tin đầu hàng đợi tới đối tƣợng LinkDelay kêt nối với nó và  
thiết lập trạng thái của hàng đợi là trạng thái khóa.  
-
-
Khi nhận đƣợc gói tin, đối tƣợng LinkDelay lập lịch hai sự kiện  
Sự kiện gói tin rời đi chỉ ra rằng việc truyền gói tin đã hoàn thành. Sự kiện này  
sẽ mở khóa cho hàng đợi Queue liên kết với đối tƣợng LỉnkDelay.  
Sự kiện gói tin đi đến chỉ ra rằng gói tin đi đến đối tƣợng TTLChecker kết nối  
với đối tƣợng LinkDelay  
-
-
Đối tƣợng TTLChecker nhận gói tin và giảm giá trị của trƣờng TTL I  
Trong phần header của gói tin. Sau khi giảm giá trị trƣờng TTL, nếu trƣờng I này  
có giá trị bằng không thì gói tin sẽ bị đối tƣợng này hủy bỏ. Ngƣợc lại, đối tƣợng này  
chuyển tiếp gói tin tới điểm truy cập của nút n2. Nút n2 chuyển tiếp gói tin tới bộ phân  
loại đứng đầu classỉfỉer_. Vì ; nút n2 chính là đích của gói tin nên gói tin sẽ đƣợc chuyển  
tiếp tới bộ phân kênh dmux_i  
Bộ phân kênh dmux_ chuyển tiếp gói tin tới tác tử null đƣợc cài đặt trong nó.  
- 144 -  
CHƢƠNG 8. PACKETS, PACKETS HEADERS, VÀ HEADERS FORMAT  
Mục tiêu  
Một gói tin thông thƣờng bao gồm hai phần là phần header và phần dữ liệu.  
Header của gói tin lƣu trữ các thông tin thuộc tính của gói cần thiết cho quá trình truyền  
gói qua mạng trong khi phần dữ liệu lƣu trữ dữ liệu của ngƣời dùng cần truyền qua mạng.  
Tuy nhiên trong NS2, mô hình gói tin có những sự khác biệt nhất định so với thực tế.  
Trong hầu hết mọi trƣờng hợp, NS2 trích thông tin từ phần dữ liệu và lƣu thông  
tin này vào phần header. Ý tƣởng này làm giảm bớt sự cần thiết phải xử lý thông tin trong  
phần dữ liệu trong thời gian chạy. Ví dụ, thay vì việc phải đếm số lƣợng bit trong gói tin,  
NS2 lƣu giá trị kích cỡ gói tin trong biến hdr_cmn::size_ (xem Mục 8.3.5) và truy cập  
vào biến này tại thời gian chạy.  
Nội dung của chƣơng này tập trung vào vấn đề mô hình hóa gói tin trong NS2.  
Mục 8.1 đƣa ra một cách nhìn tổng quan về mô hình hóa gói tin trong NS2. Mục 8.2 thảo  
luận về tiến trình cấp phát và thu hồi bộ nhớ dành cho gói tin. Mục 8.3 và 8.4 đƣa ra nội  
dung chi tiết về tổ chức phần header và phần dữ liệu của gói tin. Các hƣớng dẫn về cách  
tùy biến các gói tin đƣợc đƣa ra trong Mục 8.5  
8.1. Tổng quan về nguyên tắc tạo gói tin  
.
Kiến trúc gói tin  
Kiến trúc của một mô hình gói tin trong NS2 đƣợc biểu diễn bởi Hình 8.1. Trong  
hình vẽ này ta thấy một mô hình gói tin bao gồm bốn thành phần chính: gói tin thực, lớp  
Packet, header theo giao thức và bộ quản lý header. Ý nghĩa của các thành phần này nhƣ  
sau:  
Gói tin thực: Một gói tin thực đƣợc xem nhƣ là một phần của bộ nhớ lƣu trữ phần  
header và phần dữ liệu của gói tin. NS2 không truy cập trực tiếp tới phần header hoặc  
phần dữ liệu mà nó sử dụng con trỏ bỉts_ và đata_ để truy cập tƣơng ứng vào phần header  
và phần dữ liệu của gói tin. Chi tiết về phần header và phần dữ liệu có trong Mục 8.3 và  
8.4 của chƣơng này.  
Lớp Packet: Đƣợc khai báo trong Chƣơng trình 8.1, Lớp Packet là một lớp C++  
chính đƣợc dùng để biểu diễn các gói tin. Nó chứa các biến và các hàm thành viên sau:  
- 145 -  
Hình 8.1. Mô hình gói tin trong NS2  
Các biến thành viên của lớp Packet  
.
bits_: Chuỗi chứa phần header của gói tin  
data_: Con trỏ trỏ tới một đối tƣợng AppData chứa phần dữ liệu của gói tin  
fflag_: Đƣợc thiết lập là true nếu gói tin hiện tại đang đƣợc các đối tƣợng khác  
tham chiếu tới và là false trong trƣờng hợp ngƣợc lại  
tree_: Con trỏ trỏ tới phần tử đầu danh sách gói tin tự do ref_count_: số lƣợng  
đối tƣợng đang tham chiếu tới gói tin next_: Con trỏ trỏ tới gói tin tiếp theo trong  
danh sách liên kết các gói tin. Mr_len_: Kích thƣớc phần header của gói tin.  
.
Các hàm thành viên của lớp Packet  
- refcopy(): Tăng số lƣợng đối tƣợng tham chiếu vào gói tin lên một đơn vị  
- alloc(): Tạo một gói tin mới và trạ về một con trỏ trỏ tới gói tin đã đƣợc tạo  
- alloc(n); Tạo một gói tin mới có ―n‖ byte phần dữ liệu và trả về một con trỏ  
trỏ tới gói tin đã đƣợc tạo.  
- 146 -  
- allocdata(n): cấp phát ―n‖ byte cho .phần dữ liệu của biến data_ f ree(p):  
Giải phóng bộ nhớ dành cho gói tin p  
- access(off): Lấy tham chiếu tới một điểm nào đó (đƣợc xác định bởi  
khoảng dịch ―off‖) của biến bits_  
.
Header theo giao thức (Protocol Specifỉc Header)  
Từ Hình 8.1, ta thấy phần header của gói tin chứa một số header theo giao thức.  
Mỗi header theo giao thức sử dụng một vùng liên tục trong header của gói tin để lƣu các  
thuộc tính của gói tin. Thông thƣờng với hầu hết các đối tƣợng TclObject, có ba lớp liên  
quan với mỗi header theo giao thức đó là một lớp C++, một lớp OTcl và một lớp ánh xạ.  
-Lớp C++ (ví dụ nhƣ hdr_cmn hoặc hdr_ip): cung cấp một cấu trúc để lƣu trữ  
các thuộc tính của gói tin  
-Lớp OTcl (ví dụ nhƣ PacketHeader/Common hoặc PacketHeader/IP); đóng  
vai trò nhƣ một giao tiếp tới miền OTcl. NS2 sử dụng lớp này để cấu hình header của  
gói tin từ miền OTcl.  
-Lớp ánh xạ (ví dụ nhƣ CommonHeaderClass hoặc IPHeaderClass); liên kết  
lớp C++ với lớp OTcl.  
.
Bộ quản lý header (Packet Header Manager)  
Duy tri một danh sách các giao thức đang hoạt động và cấu hình mọi header theo  
giao thức đang hoạt động đề thiết lập header của gói tin. Nó có một biến hdrlen_ dùng để  
- 147 -  
lƣu kích thƣớc của phần header của gói tin chứa các header theo giao thức. Biến hdrlen_  
đƣợc liên kết với biến hdrlen_ của Lớp Packet. Mọi sự thay đổi của một biến sẽ đƣợc tự  
động cập nhật sang biến liên kết.  
.
Sự kiện nhận gói tin  
Đƣợc dẫn xuất từ Lớp Event (dòng 1 trong Chƣơng trình 8.1), Lớp Packet có thể  
đƣợc đƣa vào dòng thời gian mô phỏng (xem chi tiết trong Chƣơng 4). Trong Mục 4.2, ta  
đã biết rằng một đối tƣợng Event là một đối tƣợng đƣợc tạo ra bởi ngƣời dùng từ mã Tcl.  
Chƣơng này sẽ thảo luận chi tiết về một Lớp khác dẫn xuất từ Lớp Event. Đó là Lớp  
Packet.  
Nhƣ đã nói đến trong Mục 5.2.2, NS2 mô hình hóa sự kiện làm trễ quá trình  
truyền gói tin bằng cách đƣa một sự kiện tƣơng ứng vào dòng thời gian mô phỏng với  
một khoảng thời gian trễ xác định trƣớc. Đƣợc dẫn xuất từ Lớp Event, lớp Packet có thể  
đƣợc đƣa vào dòng thời gian mô phỏng để biểu diễn việc nhận một gói tin bị trễ. Ví dụ,  
câu lệnh sau (xem dòng 8 trong Chƣơng trinh 7.4) lập lịch một sự kiện nhận gói tin để  
đối tƣợng *target_ kiểu NsObject nhận gói tin *p tại thời điểm txt+delay_ giây trong  
tƣơng lai: s.schedule(target_, p, txt + delay_)  
Hàm schedule(-) của lớp Scheduler đƣợc định nghĩa trong Chƣơng trinh 4.7 có  
đối số đầu vào thứ hai là một con trỏ Event. Con trỏ Packet có thể đƣợc ép kiểu thành  
con trỏ Event trƣớc khi đƣa vào làm tham số thứ hai của hàm trên.  
Tại thời điểm thực thi sự kiện, bộ lập lịch sẽ thực thi sự kiện đƣợc lập lịch (*p) và  
gọi hàm target―>handle(p) đề thực thi hàm ―target->recv(p)‖ để chuyển tiếp gói tin *p tới  
con trỏ đối tƣợng *target_ lớp NsObject.  
-
Danh sách lỉên kết gói tin  
Là một trong bốn thành phần chính của gói tin trong NS2, một đối tƣợng Packet  
chứa một con trỏ next_ (dòng 11 trong Chƣong trinh 8.1) để trợ giúp cho việc hình thành  
một danh sách các đối tƣợng Packet (ví dụ nhƣ Packet List trong Hình 8.2).  
Hình 8.2. Danh sách liên kết các gói tin và danh sách gói tin tự do  
- 148 -  
Chƣơng trình 8.2 đƣa ra mã thực hiện của các hàm enque(p) và deque(p) cửa lớp  
PacketQueue. Hàm enque(p) (dòng 3-13) thêm một đối tƣợng *p kiểu Packet vào cuối  
hàng đợi. Nếu hàng đợi PacketQueue rỗng, NS2 sẽ thiết lập các con trỏ head_, tail_ và p  
trỏ tới cùng một điểm (dòng 5). Ngƣợc lại, dòng 7-8 thiết lập con trỏ p trỏ vào gói tin ở  
cuối hàng đợi PacketQueue và dịch chuyển con trỏ tail_ trỏ vào cùng vị trí với con trỏ p.  
Vì con trỏ tail_ là con trỏ cuối cùng của hàng đợi PacketQueue nên dòng 10 thiết lập con  
trỏ tail_- >next_ bằng 0 (trỏ đến NULL).  
Hàm deque (dòng 14-21) lấy về một con trỏ trỏ tới gói tin đầu bộ đệm. Nếu không  
có gói tin nào trong bộ đệm, hàm deque() sẽ trả lại con trỏ NULL (dòng 15). Nếu bộ đệm  
không rỗng, dòng 17 sẽ dịch chuyển con trỏ head_ tới gói tin tiếp theo, dòng 19 sẽ trả về  
con trỏ gói tin p đã đƣợc thiết lập là con trỏ head_ trong dòng 16.  
Danh sách gói tin tự do  
Không nhƣ hầu hết các đối tƣợng NS2 khác, đối tƣợng Packet mỗi khi đƣợc tạo ra  
sẽ không bị hủy cho đến khi kết thúc mô phỏng. NS2 sẽ lƣu giữ các đối tƣợng Packet  
không còn sử dụng trong một danh sách gói tin tự do (xem Hình 8.2). Khi NS2 cần một  
gói tin mới, đầu tiên nó sẽ kiểm tra xem danh sách gói tin tự do có rỗng hay không. Nếu  
không rỗng, nó sẽ lấy một đối tƣợng Packet từ danh sách này. Nếu danh sách này rỗng,  
nó sẽ tạo một đối tƣợng Packet mới. Ta sẽ thảo luận chi tiết về quá trình cấp phát và thu  
hồi bộ nhớ cho đối tƣợng Packet trong Mục 8.2.  
Có hai biến có mối quan hệ rất chặt chẽ với tiến trinh cấp phát và thu hồi bộ nhớ là  
biến fflag_ và free_. Ngƣởi ta sử dụng biến fflag_ (dòng 6 trong Chƣơng trinh 8.1) của  
một đối tƣợng Packet để xác định xem đối tƣợng này có đang đƣợc sử dụng hay không.  
Nếu biến fflag_ có giá tộ true thì hiện tại nó đang đƣợc sử dụng và ngƣợc lại biến này sẽ  
có giá trị false. Con trỏ tĩnh free_ (dòng 8 trong Chƣơng trình 8.1) đƣợc mọi đối tƣợng  
- 149 -  
Packet sử dụng chung là một con trỏ trỏ vào gói tin đầu tiên của danh sách gói tin tự do.  
Mỗi một gói tin trong danh sách gói tin tự do sử dụng biến next_ của mình để trợ giúp  
cho quá trình thiết lập danh sách liên kết các đối tƣợng gói tin tự do. Mặc dù NS2 không  
trả về bộ nhớ đƣợc cấp phát cho một đối tƣợng Packet cho hệ thống, nó vẫn thực hiện  
việc trả về bộ nhớ đƣợc phần header và phần dữ liệu của gói tin sử dụng (biến bits_ và  
data_) cho hệ thống (xem Mục 8.2.2). Vì yêu cầu lƣu trữ một đối tƣợng gói tin trong bộ  
nhớ là tổng họp của phần header và phần dữ liệu cho nên việc duy trì danh sách gói tin tự  
do sẽ tiết kiệm đƣợc nhiều không gian bộ nhớ.  
8.2. Sắp xếp và phân bổ gói tin  
Không giống nhƣ hầu hết các đối tƣợng NS2 khác, một đối tƣợng Packet đƣợc  
NS2 cấp phát và thu hồi bộ nhớ bằng cách sử dụng các hàm tĩnh tƣơng ứng là alloc() và  
free(). Nếu có thể, hàm alloc() sẽ lấy một đối tƣợng Packet từ danh sách các gói tin tự  
do. Chỉ khi nào danh sách này rỗng, nó mới tạo ra một đối tƣợng Packet mới bằng cách  
sử dụng hàm new. Hàm free(p) thu hồi bộ nhớ đã cấp phát cho một đối tƣợng Packet  
bằng cách trả về vị trí bộ nhớ đã đƣợc cấp phát cho phần header và phần dữ liệu của đối  
tƣợng này cho hệ thống và lƣu trữ con trỏ p trỏ tới gói tin không đƣợc sử dụng vào danh  
sách gói tin tự do để có thể sử dụng lại sau đó. Chi tiết của tiến trình cấp phát và thu hồi  
bộ nhở cho đối tƣợng Packet đƣợc trình bày ở hai mục tiếp theo là 8.2.1 và 8.2.2.  
8.1.1. Cấp phát gói tin  
Chƣơng trình 8.3 đƣa ra chi tiết của hàm alloc() thuộc lớp Packet. Hàm alloc() trả  
về một con trỏ trỏ tới một đối tƣợng Packet đƣợc cấp phát cho đối tƣợng gọi hàm. Nó  
bao gồm hai phần: cấp phát gói tin trong các dòng 3-15 và khởi tạo gói tin trong các dòng  
16-24.  
Xét tiến trình cấp phát gói tin trong các dòng 3-15. Dòng 3 khai báo p là con trỏ  
trỏ tới đối tƣợng Packet và thiết lập con trỏ p trỏ tới gói tin đầu tiên trong danh sách gói  
tin tự do. Nếu danh sách này rỗng (p=0), NS2 sẽ tạo một đối tƣợng Packet mới (dòng 11),  
và cấp phát không gian nhớ có kích thƣớc ―ỉiđrlen^‘ byte cho phần header của gói tin  
trong dòng 12. Biến hdrlen_ không đƣợc cấu hình trong quá trình khởi tạo đối tƣợng  
Packet. Thay vào đó, nó đƣợc thiết lập trong Pha cấu hình mạng (xem Mục 8.3.8) và  
đƣợc hàm alloc() sử dụng để tạo header của gói tin.  
Hàm alloc() không cấp phát bộ nhớ cho phần dữ liệu của gói tin. Nếu cần thì NS2  
sẽ tạo phần này bằng hàm allocdata(n) (dòng 8-14 trong Chƣơng trình 8.4).  
- 150 -  
Tải về để xem bản đầy đủ
pdf 102 trang Thùy Anh 14540 Free
Bạn đang xem 20 trang mẫu của tài liệu "Giáo trình Kỹ thuật mô phỏng (Phần 2)", để tải tài liệu gốc về máy hãy click vào nút Download ở trên

File đính kèm:

  • pdfgiao_trinh_ky_thuat_mo_phong_phan_2.pdf