2009-03-24 31 views
6

Tôi đang làm việc trên một chương trình truyền tệp đáng tin cậy sử dụng UDP. (. Cho một khóa học về mạng máy tính)Cuộc gọi liên tiếp để recvfrom() mất dữ liệu?

Câu hỏi của tôi là thế này - tốt, xem xét tình huống này:

  1. Sender có (ví dụ) 12 byte dữ liệu để gửi. Vì vậy, người gửi thực hiện cuộc gọi này:

    sendto(fd, &buf, 12, 0, (struct sockaddr *)&cliaddr,sizeof(cliaddr)); 
    

    Điều này sẽ gửi 12 byte dữ liệu một cách không đáng tin cậy. 4 byte đầu tiên của dữ liệu này xảy ra là trường "độ dài tin nhắn". Trong trường hợp này, 4 byte đầu tiên có thể có giá trị 0x0000000C

  2. Người nhận muốn đọc 4 byte đầu tiên bằng cách sử dụng recvfrom(). Thấy rằng kích thước phân đoạn là 12 byte, nó muốn đọc 8 byte còn lại. Vì vậy, người nhận có thể trông như thế này:

    /* read the segment size */ 
    recvfrom(sockfd,&buf,4,0,(struct sockaddr *)&cliaddr,&len); 
    
    /* do some arithmetic, use bzero(), etc */ 
    
    /* read the rest of the data */ 
    recvfrom(sockfd,&buf,8,0,(struct sockaddr *)&cliaddr,&len); 
    

Khi tôi thực thi mã này, tôi có thể nhận được 4 byte đầu tiên mà không có một vấn đề. Nhưng khi tôi cố gắng lấy dữ liệu còn lại, dữ liệu đó dường như bị mất. Trong đầu ra của tôi, tôi nhận được rác - có vẻ như một số phần của tiếp theo 12 byte mà người gửi là sendto() - ing.

Hành vi này có được mong đợi không? Đó là để nói, nếu một cuộc gọi recvfrom() không đọc tất cả dữ liệu được gửi đi, có phải là nó không đảm bảo rằng dữ liệu đó (8 byte còn lại) có sẵn cho tôi không?

Dường như phương pháp gửi tiêu đề phân đoạn chuẩn (bao gồm kích thước của nó), tiếp theo là tải trọng, không hoạt động. Điều đó có nghĩa là tôi cần phải gửi 2 phân đoạn riêng biệt - một phân đoạn chỉ chứa thông tin tiêu đề và sau đó là phân khúc thứ 2 có trọng tải? Hay tôi chỉ sử dụng các syscalls sai (hoặc là có một lá cờ hoặc setsockopt() mà tôi đang thiếu?)

+1

Vấn đề là UDP là giao thức kiểu gói, không phải là giao thức kiểu đường ống. Vì vậy, nó cần phải cung cấp cho bạn toàn bộ gói trong một lần chụp, để bạn có thể biết nơi gói tin bắt đầu một kết thúc.Nếu không, bạn không có đầu mối những gì đã thực sự xảy ra. –

Trả lời

7

Từ recv (2) xem man page:

Nếu nhắn quá dài để vừa với bộ đệm được cung cấp , các byte dư thừa có thể là bị loại bỏ tùy thuộc vào loại ổ cắm nhận được.

Đây là những gì có vẻ như đang xảy ra với bạn.

Bạn nên có bộ đệm có kích thước thư tối đa và đọc số tiền đó. Bạn sẽ chỉ đọc một datagram và chiều dài sẽ được trả về. Sau đó bạn có thể phân tích độ dài từ phía trước của bộ đệm và xác nhận nó dựa vào những gì recvfrom (2) trả về.

+0

Tức là, kích thước phân đoạn tối đa phải được cố định và được cả khách hàng lẫn máy chủ biết trước? – poundifdef

+0

Kích thước tối đa của gói dữ liệu UDP là 64KiB. Bạn có thể không gửi được nhiều, tùy thuộc vào kích thước bộ đệm gửi. (MSS là một thuật ngữ TCP, BTW). – camh

+2

Vì bạn đang thiết kế giao thức này, vui lòng chọn kích thước tối đa. UNP Vol1, 3rd Edition đề xuất sử dụng bộ đệm lớn hơn một byte so với thông báo lớn nhất mà bạn từng mong đợi nhận được. Nếu recvfrom() trả về một giá trị bằng với chiều dài của bộ đệm của bạn, nó sẽ được coi là một lỗi. – sigjuice

2

Phương pháp khác là thực hiện thao tác giả với cờ MSG_PEEK. Trong khi kích thước trả về giống với kích thước bộ đệm của bạn (hoặc nhiều hơn), hãy lấy bộ đệm lớn hơn và thử lại. Sau đó thực hiện lại lần nữa (không có cờ MSG_PEEK) để xóa thư khỏi bộ đệm UDP.

Nhưng tất nhiên, điều này là không hiệu quả và không nên được thực hiện khi bạn chỉ có thể quyết định kích thước gói tối đa.

+0

Điều này không hoạt động. Giả sử bạn thực hiện recvfrom giả và sau đó trước khi bạn thực hiện recvfrom "thực", datagram đã bị loại bỏ. Sau đó, bạn sẽ nhận được datagram tiếp theo vào một bộ đệm có kích thước sai, có thể cắt ngắn nó. –

Các vấn đề liên quan