【Socket网络编程】16.UDP 循环读取recvfrom() 与 循环发送 sendto()
@zhz:
疑問(wèn):有時(shí)候會(huì)看到某些代碼,sendto()時(shí)用了while循環(huán), 而recvfrom()時(shí)沒(méi)使用while循環(huán)?
答:他們都可以使用循環(huán)語(yǔ)句,可參考TCP數(shù)據(jù)粘包的處理。
什么時(shí)候需要使用循環(huán),什么時(shí)候不使用循環(huán),可以看下面的分析:
以下其實(shí)是我根據(jù)自己項(xiàng)目使用的udp協(xié)議中的recvfrom()和sendto()進(jìn)行測(cè)試沒(méi)問(wèn)題后分析的。但是對(duì)于TCP粘包的問(wèn)題,卻并非如此,并非recv每次只取一次完整發(fā)送的數(shù)據(jù)(UDP的recvfrom()為什么可以取這么準(zhǔn)?),我目前還沒(méi)測(cè)試。
1. recvfrom()要使用與不使用循環(huán)的情況:
我們通常指定的接收端一次接收長(zhǎng)度都會(huì) >= 發(fā)送端一次發(fā)送的數(shù)據(jù)長(zhǎng)度。通常情況下,我們發(fā)送端一次發(fā)送的數(shù)據(jù)長(zhǎng)度都不會(huì)是固定的,所以就需要接收端設(shè)置一個(gè)合適的固定的接收長(zhǎng)度,這個(gè)固定長(zhǎng)度需要大于等于發(fā)送端一次發(fā)送的最大數(shù)據(jù)長(zhǎng)度。
當(dāng)recvfrom()函數(shù)指定buf的長(zhǎng)度后,并且一次recvfrom()函數(shù)讀取到的數(shù)據(jù)小于指定長(zhǎng)度max_length(這個(gè)是可以保證的),那么:
- 如果能確定每次recvfrom()實(shí)際讀取到的數(shù)據(jù)是發(fā)送端一次發(fā)送的完整數(shù)據(jù),那就不用循環(huán)recvfrom()。
- 如果每次recvfrom()實(shí)際讀取到的數(shù)據(jù)不是發(fā)送端一次發(fā)送的完整數(shù)據(jù),就需要循環(huán)recvfrom()。
2.sendto()要使用與不使用循環(huán)的情況:
sendto()一般情況下需要使用循環(huán),因?yàn)榧偃缫粋€(gè)數(shù)據(jù)包太大,如長(zhǎng)度為10MB,一次sendto()發(fā)送到輸出緩沖區(qū)可能發(fā)不完整,此時(shí)就需要對(duì)sendto()使用循環(huán)發(fā)送,直到把10MB的數(shù)據(jù)都拷貝到輸出緩沖區(qū)。
sendto()函數(shù)中參數(shù)指定的數(shù)據(jù)長(zhǎng)度,就是本次發(fā)送(就是寫入輸出緩沖區(qū))的數(shù)據(jù)長(zhǎng)度,都會(huì)提前計(jì)算好之后再填入,每次發(fā)送的數(shù)據(jù)長(zhǎng)短可能不一樣,所以他就不是固定長(zhǎng)度的。
而recvfrom()函數(shù)中指定的長(zhǎng)度是固定的。
3.recvfrom()和sendto()例子:
recvfrom()和sendto()的第三個(gè)參數(shù)len都是指定第二個(gè)參數(shù)buf的長(zhǎng)度。
- 1.recvfrom()從輸入緩沖區(qū)中拷貝數(shù)據(jù)到應(yīng)用程序緩沖區(qū)buf,在此需要指定buf的長(zhǎng)度。他的長(zhǎng)度一般在定義緩沖buf時(shí)就定下來(lái)了,如
- 2.sendto()從輸出緩沖區(qū)中拷貝數(shù)據(jù)到應(yīng)用程序緩沖區(qū)buf,在此需要指定buf的長(zhǎng)度。他的長(zhǎng)度都會(huì)提前計(jì)算好之后再填入,每次發(fā)送的數(shù)據(jù)長(zhǎng)短可能不一樣,所以他就不是固定長(zhǎng)度的:
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;}// 接收來(lái)自本車obu的數(shù)據(jù)包:0x63,0x69,0x64,0x69分別表示cidi的ASCII碼:99,105,100,105// 如果不是"cidi",1.如果是單播,就把ip保持為上一次成功單播的ip;// 2.如果是廣播,就把ip設(shè)為0.0.0.0(即htonl(INADDR_ANY)),即本機(jī)任意網(wǎng)卡的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 分別對(duì)應(yīng)'`'和'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; }總結(jié)
以上是生活随笔為你收集整理的【Socket网络编程】16.UDP 循环读取recvfrom() 与 循环发送 sendto()的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【Socket网络编程】15. 发送端和
- 下一篇: 【Socket网络编程】17. recv