《TCP/IP Sockets 编程》笔记4

来源:互联网 发布:linux 自旋锁 编辑:程序博客网 时间:2024/04/28 15:34

第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()。

It is possible to call connect() on a UDP socket to fix the destination address of future datagrams sent over the socket. Once connected, you may use send() instead of sendto() to transmit datagrams because you no longer need to specify the destination address. In a similar way, you may use recv() instead of recvfrom() because a connected UDP socket can only receive datagrams from the associated foreign address and port, so after calling connect() you know the source address of any incoming datagrams. In fact, after connecting, you may only send and receive to/from the address specified to connect(). Note that connecting and then using send() and recv() with UDP does not change how UDP behaves. Message boundaries are still preserved, datagrams can be lost, and so on. You can “disconnect” by calling connect() with an address family of AF_UNSPEC.

 

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

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

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

Another subtle advantage to calling connect() on a UDP socket is that it enables you to receive error indications that result from earlier actions on the socket. The canonical exampleis sending a datagram to a nonexistent server or port. When this happens, the send() that eventually leads to the error returns with no indication of error. Some time later, an error message is delivered to your host, indicating that the sent datagram encountered a problem. Because this datagram is a control message and not a regular UDP datagram, the system can’t always tell where to send it if your socket is unconnected, because an unconnected socket has no associated foreign address and port. However, if your socket is connected, the system is able to match the information in the error datagram with your socket’s associated foreign IP address and port. Note such a control error message being delivered to your socket will result in an error return from a subsequent system call (for example, the recv() that was intended to get the reply), not the offending send().

 

 

 

 

 

 

 

 

 

 

 

 

 

 

原创粉丝点击