11. Header gói và các định dạng
11.1. Header gói cho một giao thức cụ thể
Các nhà phát triển giao thức thường muốn tạo một kiểu header để sử dụng trong các gói. Điều đó cho phép thực hiện một giao thức mới mà không chồng chéo lên các trường header đã có. Ta xem xét bản đơn giản của giao thức RTP. Header RTP yêu cầu các trường số thứ tự và một trường định danh nguồn. Các lớp sau đây tạo ra header cần thiết (xem trong ~ns/rtp.h và ~ns/rtp.cc):
Trong file rtp.h:
/* rtp packet. For now, just have srcid + seqno. */ struct hdr_rtp {
u_int32_t srcid_; int seqno_;
/* per-field member functions */
u_int32_t& srcid() { return (srcid_); } int& seqno() { return (seqno_); }
/* Packet header access functions */ static int offset_;
inline static int& offset() { return offset_; } inline static hdr_rtp* access(const Packet* p) { return (hdr_rtp*) p->access(offset_);
} };
Trong file rtp.cc:
class RTPHeaderClass : public PacketHeaderClass { public: RTPHeaderClass() : PacketHeaderClass("PacketHeader/RTP", sizeof(hdr_rtp)) { bind_offset(&hdr_rtp::offset_); } } class_rtphdr; void RTPAgent::sendpkt() { Packet* p = allocpkt(); hdr_rtp *rh = hdr_rtp::access(p); lastpkttime_ = Scheduler::instance().clock(); /* Fill in srcid_ and seqno */
rh->seqno() = seqno_++; rh->srcid() = session_->srcid(); target_->recv(p, 0); } RTPAgent::RTPAgent() : session_(0), lastpkttime_(-1e6) { type_ = PT_RTP; bind("seqno_", &seqno_); }
Cáu trúc đầu tiên, hdr_rtp, định nghĩa cách bố trí header gói của RTP (về khái niệm và chỗ đứng của nó): các trường cần thiết và kích thước của chúng. Định nghĩa cấu trúc này chỉ được trình biên dịch sử dụng để tính byte offset của trường; không có các đối tượng của cấu trúc này được định vị trực tiếp. Cấu trúc này cũng tạo ra các hàm bộ phận để tạo ra một lớp che dấu các các dữ liệu đối với các đối tượng muốn độc hoặc thay đổi trường header của các gói. Chú ý rằng các biến lớp tĩnh offset_ được dùng để tìm các byte offset tại đó header rtp được đặt trong một nspacket. Có 2 phương thức sử dụng biến này để truy nhập header này trong gói bất kỳ: offset() và access(). Phương thức trước được hầu hết người dùng lựa chọn để truy nhập header cụ thể này trong một gói; phương thức sau được sử dụng bởi lớp quản lý header gói nhưng cũng ít khi dùng. Ví dụ, để truy nhập header gói RTP trong một gói được trỏ bởi p, ta chỉ cần sử dụng hdr_rtp::access(p). Liên kết thực của offset_ đến vị trí của header này trong gói được thực hiện trong ~ns/tcl/lib/ns-packet.tcl và ~ns/packet.cc. Hằng số trong lệnh access() tạo một truy nhập read-only đến hằng Packet, mặc dù thuộc tính read-only bị bắt buộc do con trỏ trả về không phải là hằng số. Một cách chính xác để làm điều này là tạo ra 2 phương thức, một phương thức để truy nhập ghi, một phương thức để truy nhập read-only. Tuy nhiên, hiện nay chưa thực hiện điều này.
Đối tượng tĩnh class_rtphdr của lớp RTPHeaderClass được dùng để liên kết đến OTcl khi header RTP được cho phép tại thời điểm cấu hình. Khi bộ mô phỏng chạy, đối tượng tĩnh này gọi hàm khởi tạo PacketHeaderClass với lệnh "PacketHeader/RTP" và sizeof(hdr_rtp). Như vậy, kích thước của header RTP được lưu lại và sẵn sàng phục vụ đối tượng quản lý header gói tại thời điểm cấu hình (xem Section 12.2.4). Chú ý rằng bind_offset() phải được gọi trong hàm khởi tạo của lớp này, như vậy đối tượng quản lý header gói biết được nơi lưu giữ offset của header gói này.
Phương thức sendpkt() của hàm bộ phận ví dụ của RTPAgent tạo ra một gói mới để gửi đi bằng cách gọi allocpkt(), nó điều khiển việc ấn định tất cả các trường header gói lớp mạng (trong trường hợp này là IP). Các header khác ngoài IP được điều khiển riêng. Trong trường hợp này, agent sử dụng RTPHeader như đã định nghĩa ở trên. Hàm bộ phận Packet::access(void) trả về địa chỉ của byte đầu tiên trong bộ đệm được dùng để mang thông tin header. Giá trị trả về của nó là con trỏ đến header quan