Sending and Receiving with UDP Sockets

ssize_t sendto(int socket, const void* msg, size_t msgLength, int flags,
               const struct sockaddr* destAddr, socklen_t addrLen);
ssize_t recvfrom(int socket, void* msg, size_t msgLength, int flags,
                 struct sockaddr* srcAddr, socklen_t* addrLen);

sendto()的前四个参数与send()的完全相同,只是在后面添加了目标地址及其长度大小, 因为UDP没有connect()操作,所以要在发送时指明目标地址
recvfrom()的前四个参数与recv()的相同,并在之后添加了两个参数用于接受消息的来源地址。 注意recvfrom()中的addrLen参数是一个输入/输出参数,在输入时表示srcAddr地址缓冲区的大小, 在输出时表示实际复制到地址缓冲区中的地址的大小

TCPUDP的一个区别就是UDP会保留边界信息,这就意味着一次recvfrom()调用最多只会接受来自 一个sendto()发送的数据。而不同次调用的recvfrom()接受的数据永远不可能来自同一次sendto()发送 (除非使用MSG_PEEK标识)。
TCP中一个recv()可能接受的是多个send()发送的数据,或多个recv()接受来自一个send()发送的数据。

send()返回时,调用者只知道数据被放入的发送缓冲区,数据可能会也可能不会实际传输到目标地址。 由于UDP没有错误处理机制,所以不需要缓冲区对发送失败数据进行重传,这就是说当sendto()返回时, 数据已经传输或正在传输到目标地址

在数据被发送到目标服务器但是在recv()recvfrom()被调用前,数据被保存在一个FIFO的接受缓冲区队列。 所有接受到的数据在这个缓冲区中是一个连续的序列。由于recvfrom()要返回数据的源地址,所以UDP发送的消息 需要保留边界信息。如果调用recvfrom()时使用的接受数据缓冲区大小n小于FIFO队列中第一数据块的大小,则 recvfrom()只会接受到该块数据的前n个字节,而剩余的字节则会被自动舍弃且调用着无法感知。
所以在使用recvfrom()时要使用比接受数据大的缓冲区才不会导致数据丢失。 UDPrecvfrom()可以返回的最大字节数为65507 bytes

如果在recvfrom()调用时使用MSG_PEEK标识,则在调用后数据必然会保存在FIFO接受缓冲区队列中, 这样就可以多次重复获取同一sendto()发送的数据。如果在发送数据的头部写入数据块的大小信息, 则可以使用这用方法提前探测数据块的真实大小,然后使用合适的缓冲区大小接受数据,从而保证没有数据丢失

Connecting a UDP Socket

也可使用connect()函数连接UDP Socket然后使用send()recv()通信