第4章 使用UDP套接字

UDP只执行两种功能:

1.向IP层添加了另一个寻址(端口)层;

2.检测传输中可能发生的数据损坏,并丢弃任何损坏的数据报。

UDP套接字与TCP套接字之间的另一个区别在于它们处理消息边界的方式:UDP套接字会保留它们。

UDP提供的端到端传输服务时一种“尽力而为”的服务:不保证通过UDP套接字发送的消息将会到达其目的地。

 

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)

When a call to send() on a TCP socket returns, all the caller knows is that the data has been copied into a buffer for transmission; the data may or may not have actually been transmitted yet. However, UDP does not buffer data for
possible retransmission because it does not recover from errors. This means that by the time a call to sendto() on a UDP socket returns, the message has been passed to the underlying channel for transmission and is (or soon will be) on its way out the door.

Between the time a message arrives from the network and the time its data is returned via recv() or recvfrom(), the data is stored in a first-in, first-out (FIFO) receive buffer. With a connected TCP socket, all received-but-not-yet-delivered bytes are treated as one continuous sequence (see Section 7.1). For a UDP socket, however, the bytes from different messages may have come from different senders. Therefore, the boundaries between them need to be preserved so that the data from each message can be returned with the proper address. The buffer really contains a FIFO sequence of “chunks” of data, each with an associated source address. A call to recvfrom() will never return more than one of these chunks. However, if recvfrom() is called with size parameter n, and the size of the first chunk in the receive FIFO is bigger than n, only the first n bytes of the chunk are returned. The remaining bytes are quietly discarded, with no indication to the receiving program.
For this reason, a receiver should always supply a buffer big enough to hold the largest message allowed by its application protocol at the time it calls recvfrom(). This technique will guarantee that no data will be lost. The maximum amount of data that can ever be returned by recvfrom() on a UDP socket is 65,507 bytes—the largest payload that can be carried in a UDP datagram.
Alternatively, the receiver can use the msg_peek flag with recvfrom() to “peek” at the first chunk waiting to be received. This flag causes the received data to remain in the socket’s receive FIFO so it can be received more than once. This strategy can be useful if memory is scarce, application messages vary widely in size, and each message carries information about its size in the first few bytes. The receiver first calls recvfrom() with msg_peek and a small buffer, examines the first few bytes of the message to determine its size, and then calls recvfrom() again (without msg_peek) with a buffer big enough to hold the entire message. In the usual case where memory is not scarce, using a buffer big enough for the largest possible message is simpler.

 

Connecting a UDP Socket

UDP套接字可以调用connect()来固定将要发送的数据报的地址。一旦连接,可以使用send()来发送数据报,不需要再指定目的地址。相似的,使用recv()来接收数据报,因为一个连接的UDP套接字只能接收关联的外部地址发送的数据报,调用connect()后就知道接收到得数据报的源地址。

也就是说,连接后,只能向connect()指定的地址发送数据报,或从它接收数据报。连接之后使用send()和recv()并不改变UDP的行为。

通过调用AF_UNSPEC地址族参数的connect()可以取消连接。

 

UDP套接字使用connect()的另一个微妙的优点是:是你能够接收到套接字上先前动作产生的错误指示。

最简单的例子,发送一个数据报给不存在的服务器或端口,调用send()发送该数据报将最终导致错误,但该调用并没有错误指示就返回。过了一段时间之后,主机会收到一个错误消息指示发送的数据报遇到问题。由于该数据报是控制消息而不是常规的UDP数据报,如果套接字是非连接的,系统没法指示该消息发送到哪,因为非连接的套接字没有相关联的外部地址和端口。然而,如果端口是连接的,系统能将错误数据报的信息与套接字的关联外部IP地址和端口进行匹配。

需要注意的是,控制错误消息被交付给套接字将导致后续的系统调用返回错误,而不是导致之前导致错误的send()返回错误。

posted on 2010-09-12 21:09  龍蝦  阅读(348)  评论(0编辑  收藏  举报