日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【Socket网络编程】16.UDP 循环读取recvfrom() 与 循环发送 sendto()

發布時間:2025/3/21 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Socket网络编程】16.UDP 循环读取recvfrom() 与 循环发送 sendto() 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

@zhz:

疑問:有時候會看到某些代碼,sendto()時用了while循環, 而recvfrom()時沒使用while循環?

答:他們都可以使用循環語句,可參考TCP數據粘包的處理。

什么時候需要使用循環,什么時候不使用循環,可以看下面的分析:

以下其實是我根據自己項目使用的udp協議中的recvfrom()和sendto()進行測試沒問題后分析的。但是對于TCP粘包的問題,卻并非如此,并非recv每次只取一次完整發送的數據(UDP的recvfrom()為什么可以取這么準?),我目前還沒測試。
1. recvfrom()要使用與不使用循環的情況:

我們通常指定的接收端一次接收長度都會 >= 發送端一次發送的數據長度。通常情況下,我們發送端一次發送的數據長度都不會是固定的,所以就需要接收端設置一個合適的固定的接收長度,這個固定長度需要大于等于發送端一次發送的最大數據長度。

當recvfrom()函數指定buf的長度后,并且一次recvfrom()函數讀取到的數據小于指定長度max_length(這個是可以保證的),那么:

  • 如果能確定每次recvfrom()實際讀取到的數據是發送端一次發送的完整數據,那就不用循環recvfrom()。
  • 如果每次recvfrom()實際讀取到的數據不是發送端一次發送的完整數據,就需要循環recvfrom()。

2.sendto()要使用與不使用循環的情況:

sendto()一般情況下需要使用循環,因為假如一個數據包太大,如長度為10MB,一次sendto()發送到輸出緩沖區可能發不完整,此時就需要對sendto()使用循環發送,直到把10MB的數據都拷貝到輸出緩沖區。
sendto()函數中參數指定的數據長度,就是本次發送(就是寫入輸出緩沖區)的數據長度,都會提前計算好之后再填入,每次發送的數據長短可能不一樣,所以他就不是固定長度的。
而recvfrom()函數中指定的長度是固定的。


3.recvfrom()和sendto()例子:

recvfrom()和sendto()的第三個參數len都是指定第二個參數buf的長度。

  • 1.recvfrom()從輸入緩沖區中拷貝數據到應用程序緩沖區buf,在此需要指定buf的長度。他的長度一般在定義緩沖buf時就定下來了,如
constexpr std::size_t kBufferSize = 1024; ... uint8_t buf[kBufferSize] = {0}; //定義buf時,長度也定下來了 std::memset(buf, 0, kBufferSize); ... length = data_stream_->read(buf, kBufferSize, 0); 上面的read()函數會調用: ret = ::recvfrom(sockfd_, buffer, kBufferSize, 0)
  • 2.sendto()從輸出緩沖區中拷貝數據到應用程序緩沖區buf,在此需要指定buf的長度。他的長度都會提前計算好之后再填入,每次發送的數據長短可能不一樣,所以他就不是固定長度的:
constexpr std::size_t kBufferSize = 1024;char buf[kBufferSize] = "abcd"; // proto_msg_是一個已經賦值后的protobuf消息的變量int proto_msg_length = proto_msg_.ByteSize(); // 把 proto_msg_ 序列化進buf,從buf第四字節開始,前四個字節為"cidi"proto_msg_.SerializeToArray(&buf[4], proto_msg_length);int send_size = proto_msg_length + 4; // 加上"cidi"四個字節// 雖然 buf有1024字節,但是只將他的前 send_size 字節寫入輸出緩沖區std::size_t len = data_stream_->write(reinterpret_cast<uint8_t*>(buf),send_size, 0);上面的write()函數會調用: ret = ::sendto(sockfd_, buffer, kBufferSize, 0)

sendto()
size_t UdpStream::write(const uint8_t* data, size_t length, uint8_t flag) {size_t total_nsent = 0;// if (flag) {// peer_sockaddr_.sin_addr.s_addr = htonl(INADDR_BROADCAST);// }peer_sockaddr_.sin_addr.s_addr = peer_addr_;peer_sockaddr_.sin_port = peer_broad_port_;SDEBUG << "sendto addr: " << inet_ntoa(peer_sockaddr_.sin_addr)<< ", port: " << ntohs(peer_sockaddr_.sin_port);while (length > 0) {ssize_t nsent =::sendto(sockfd_, data, length, 0, (struct sockaddr*)&peer_sockaddr_,(socklen_t)sizeof(peer_sockaddr_));if (nsent < 0) { // errorif (errno == EINTR) {continue;} else {// errorif (errno == EPIPE || errno == ECONNRESET) {status_ = Stream::Status::DISCONNECTED;errno_ = errno;} else if (errno != EAGAIN) {status_ = Stream::Status::ERROR;errno_ = errno;}return total_nsent;}}total_nsent += nsent;length -= nsent;data += nsent;}return total_nsent; }
recvfrom()
size_t UdpStream::read(uint8_t* buffer, size_t max_length, uint8_t flag) {ssize_t ret = 0;struct sockaddr_in addrfrom;addrfrom.sin_addr.s_addr = htonl(INADDR_ANY);if (flag) {peer_sockaddr_.sin_addr.s_addr = htonl(INADDR_ANY);} else {addrfrom.sin_addr.s_addr = peer_sockaddr_.sin_addr.s_addr;}while ((ret = ::recvfrom(sockfd_, buffer, max_length, 0,(struct sockaddr*)&peer_sockaddr_,reinterpret_cast<socklen_t*>(&socklenth_))) < 0) {if (errno == EINTR) {continue;} else {// errorif (errno != EAGAIN) {status_ = Stream::Status::ERROR;errno_ = errno;}}return 0;}// 接收來自本車obu的數據包:0x63,0x69,0x64,0x69分別表示cidi的ASCII碼:99,105,100,105// 如果不是"cidi",1.如果是單播,就把ip保持為上一次成功單播的ip;// 2.如果是廣播,就把ip設為0.0.0.0(即htonl(INADDR_ANY)),即本機任意網卡的ipif (0x63 != buffer[0] && 0x69 != buffer[1] &&0x64 != buffer[2] && 0x69 != buffer[3]) {peer_sockaddr_.sin_addr.s_addr = addrfrom.sin_addr.s_addr;}// // 0x60,0x61 分別對應'`'和'a'的ASCII碼 96(`),97(a)// if (buffer[0] != 0x60 && buffer[1] != 0x61) {// peer_sockaddr_.sin_addr.s_addr = addrfrom.sin_addr.s_addr;// }SDEBUG << "Receive addr: " << inet_ntoa(peer_sockaddr_.sin_addr)<< ", port: " << ntohs(peer_sockaddr_.sin_port);return ret; }

總結

以上是生活随笔為你收集整理的【Socket网络编程】16.UDP 循环读取recvfrom() 与 循环发送 sendto()的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。