Giúp giải thích code C phần socket

Trong 2 hàm sau:

bind(socket,(struct sockaddr *)&server , sizeof(server));
hoặc
connect(socket,(struct sockaddr *)&client , sizeof(client))

struct sockaddr_in server hoặc struct sockadd_in client khi cast về struct sockaddr * thì thông tin Port number được đưa vào 2 hàm trên như thế nào ?

Hay việc này diễn ra ở đoạn nào khác? Mình thấy trong 2 tiến trình bind hoặc connect , truct sockaddr_in` không được sử dụng trực tiếp vào hàm nào?

Trước hết mình xem qua các struct này được định nghĩa thế nào.

Đạt có đánh dấu số lượng byte theo sau mỗi member của struct.

struct sockaddr_in {
    short            sin_family;   // e.g. AF_INET - 2 bytes
    unsigned short   sin_port;     // e.g. htons(3490) - 2 bytes
    struct in_addr   sin_addr;     // see struct in_addr, below - 4 bytes
    char             sin_zero[8];  // zero this if you want to - 8 bytes
};

struct in_addr {
    unsigned long s_addr;  // load with inet_aton() - 4 bytes
};

Như vậy, mình thấy được struct sockaddr_in bao gồm 2 bytes đầu tiên là sin_family, có thể hiểu là header, và 14 bytes tiếp theo là data.

Bây giờ mình xem qua struct sockaddr

struct sockaddr {
    unsigned short    sa_family;    // address family, AF_xxx - 2 bytes
    char              sa_data[14];  // 14 bytes of protocol address
};

Ở đây mình thấy struct này cũng có 2 bytes đầu tiên là sa_family tương đương với 2 bytes đầu tiên của struct sockaddr_in là sin_family. Hai bytes này để phân loại socket thuộc về họ gì, ví dụ như:

  • IF_INET là IPv4
  • IF_INET6 là IPv6

Tiếp theo, ta thấy struct sockaddr có 14 bytes kiểu char là một mảng. Một kỹ thuật hay được dùng trong network programming là dùng một array bytes để chứa dữ liệu(data). Khi ta cast struct sockaddr_in, bất kể client hay server, hoặc struct sockaddr_in6, dành cho IPv6, về dạng struct sockaddr thì

  • 2 bytes đầu vẫn là family của socket đấy như đã nói ở trên.
  • 14 bytes tiếp theo sẽ nằm trong array char sa_data[14]. Khi muốn sử dụng chỉ cần gọi cái char sa_data[14] ra ép kiểu là lấy được mọi giá trị ta cần như sin_port, sin_addr. Để ý ở đây, struct sockaddr_in có char sin_zero[8], đây là phần thừa, không sử dụng. Người ta chỉ khai báo như vậy để phần data của struct này có độ dài giống như struct sockaddr_in6 mà thôi. Phần này không dùng trong IPv4.

Vậy làm sao có thể đọc được sin_port và sin_addr từ sa_data

Cách đơn giản nhất là trong hàm bindconnect, ta ép kiểu struct sockaddr ngược về struct sockaddr_in.

5 Likes

Vậy mấu chốt của ép kiểu không phải là cấu trúc dữ liệu, hay type ( vd: struct, int, char…) mà là length?

Rất cảm ơn bác.

Có thể hiểu như vậy :slight_smile: Ta có một chuỗi bytes. Đọc như thế nào, hiểu như thế nào là dựa vào struct mà mình ép xuống.

Ví dụ có chuỗi 4 bytes. Nếu ép về int, có nghĩa đó là 1 số nguyên 4 bytes. Nếu ép về char, thì đây là một chuỗi văn bản có 4 bytes (hoặc 3 bytes dữ liệu, 1 byte kết thúc chuỗi.)

Lý do cho sự tồn tại của struct sockaddr là gì? tại sao connectbind ko dùng luôn struct sockaddr_in?

Trong C, em chưa thấy struct sockaddr dùng với mục đích declaration mà chỉ dùng để cast, nếu thực sự cần nó tại sao không ẩn vào trong các hàm?

Lý do cho sự tồn tài của struct sockaddr là API. Một API phải có một interface rõ ràng, kiểu dữ liệu truyền vào cụ thể.

Hàm bindconnect có thể nhận vào IPv4 hoặc IPv6 hoặc kiểu Network Address khác. Vậy ta cần phải chọn một kiểu struct làm chuẩn. struct sockaddr là kiểu dữ liệu chuẩn cho API bindconnect.

Tuy nhiên, struct sockaddr có kích thước bằng với struct sockaddr_in, nhưng nhỏ hơn struct sockaddr_in6 và nhỏ hơn struct sockaddr_un. Thế nên ta cần thêm một giá trị nữa là length. Dựa vào length, ở đây là sizeof(server), ta sẽ biết là struct truyền vào có kích thước bao nhiêu. Hoặc có thể sử dụng member sa_family để biết loại network address là gì để ép kiểu trở lại kiểu ban đầu.

6 Likes

hay quá ^^ cám ơn ad ^^

e đang học lập trình mạng, cứ thắc mắc suốt tại sao cần phải có nhiều kiểu cấu trúc như vậy để làm gì, đọc dc bài viết và phần trả lời của bác Đạt thấy thông não quá, cảm ơn bác nhé

1 Like
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?