UDP:用户数据报协议。无连接不可靠的数据报协议。
与TCP相比,UDP更注重数据的收发速度且因为不用维护连接信息,系统与网络开销要小的多。TCP是端到端的一对一,UDP却支持一对一,一对多以及多对多。
但是UDP的缺点也是很明显的,UDP无法做到像TCP那样对于连接的维护,UDP无重传机制,拥塞控制以及流量控制等,因此在使用UDP的时候,会出现丢包,重复包,包乱序等现象。
TCP主要在对连接可靠性要求高的场合使用,使用TCP的协议有:telnet,ftp,smtp。
UDP主要在对高速传输和实时性要求高或者广播组播的场合使用,支持的协议有:DNS,NFS,SNMP。
UNIX中UDP的基本套接字有两个:
#include<sys/socket.h>sockfd:套接字。
ssize_t recvfrom(int sockfd,void* buff,size_t nbytes,int flags,\
struct sockaddr* from,socklen_t* addrlen);
//成功返回从sockfd读得的字节数,出错返回-1
ssize_t sendto(int sockfd,const void* buff,size_t nbytes,int flages,\
const struct sockaddr*to ,socklen_t addrlen);
//成功返回写入sockfd的字节数,出错返回-1
nbytes:读写字节数。
buff:读入或写出缓冲区。
flags:指明recvfrom或sendto的具体操作方式。
from:发送方源地址;to:接收方目的地址。
addrlen:from或to的地址长度。
使用UDP基本套接字常见问题及解决办法:
1.数据报丢失。
前面提到UDP是不可靠的,因此若是数据报没有到达另一端,或者另一端的应答没有回到本端,都会造成在调用recvfrom的时候永远的阻塞,为防止永久阻塞,一般方法是给recvfrom设置一个超时。
2.验证接收到的响应。
UDP协议不保证主机接收到的应答是之前发送数据报的应答,因此需要程序员在写代码的时候进行验证该响应是否是相应的应答。常见一种方法是通过recvfrom函数获得发送响应一方的源地址,再与传入sendto函数的目的地址to做对比,看是否一致。
但是这种方法不适合多接口的弱端系统模型。强、弱端系统模型定义如下:
弱端系统模型:大多数IP实现接受目的地址为本主机任一IP地址的数据报,而不管数据报到达的接口。
强端系统模型:只有目的地址与到达的接口相一致的数据报才会被接受。
有了上述概念很容易知道,如果对弱端系统模型采用上述方法,那么作为应答方,很有可能选择某个外出接口发送响应,并且响应的源IP地址与之前应答方接收到的数据报目的地址不同。
为了弥补这个不足有两个办法:
(1)如果发送响应的是服务器,则在得到由recvfrom返回的IP地址后,可以通过在DNS中查找服务器主机的名字来验证主机的域名。
(2)UDP服务器给服务器主机上配置的每个IP地址创建一个套接字,用bind函数捆绑每个IP地址到各自的套接字,再在这些套接字上使用select,这么做可以保证服务器每个套接字接收到的数据报目的地址与它们自身绑定的地址相同,这样服务器在响应的时候发出的应答源地址就不会改变。
3.对端进程未启动。
假设在服务器进程没有启动的情况下,启动客户端,并由客户端通过sendto函数向服务器发送数据报,服务器接收这个数据报后,会响应一个"port unreachable"的ICMP消息,如果客户端采用recvfrom接收响应,那么它返回的信息仅有errno值,没有办法返回出错数据报的目的IP地址和目的UDP端口号。在上面客户端采用sendto发送数据报和服务器响应ICMP错误的过程中会产生异步错误:sendto函数成功返回发送的字节数,但是ICMP错误却在后来才返回。
为了使客户端具有区分出错数据报的能力,应该遵循一个基本规则:对于一个UDP套接字,由它引发的异步错误却并不返回给它,除非它已连接。
反映在代码上就是用connect、read、write函数代替recvfrom与sendto函数。
新建的UDP套接字默认是“未连接”的,而调用connect函数并成功返回后的UDP套接字是“已连接”的。由于connect函数记录对端的IP地址和端口号,所以不必再使用recvfrom和sendto函数转而使用read和write函数。由已连接的UDP套接字引发的异步错误会返回给它们所在的进程,而未连接的UDP的套接字不接收任何异步错误(这里的“未连接”和“已连接”是指是否调用connect函数来使内核认为对话是已连接的,而非真正的形成连接,正是因为调用connect后,UDP客户端可以使用read来获取ICMP的错误)。
UDP使用connect函数是为了确保服务器可达并且开放了相应UDP端口,与TCP使用connect不同:
a.UDP使用connect没有发生三次握手过程。
b.内核只是检查是否存在立即可知的错误,记录对端的IP地址和端口号,然后立即返回到调用进程。
c.TCP中connect可以通过三次握手过程中服务器响应的RST来获取ICMP错误,而UDP则是通过调用read函数。
而且一般给一个TCP套接字只能调用一次connect函数,但是给一个UDP套接字却能多次调用connect函数。目的:
a.指定新的IP地址和端口号。
b.断开套接字。