61-面向连接的 UDP

前面我们已经知道,UDP 编程中的调用 sendto 和 recvfrom 产生的错误是不会返回的。主要原因在于套接字是无连接的。

要想让它们返回错误,只有让套接字变成有连接的。

1. 程序路径

代码托管在 gitos 上,请使用下面的命令获取:

git clone https://git.oschina.net/ivan_allen/unp.git

如果你已经 clone 过这个代码了,请使用 git pull 更新一下。本节程序所使用的程序路径是 unp/program/echo_udp/conn

2. UDP 也可以是有连接的

我们可以在 UDP 套接字上调用 connect,但它于 TCP 连接却不同,在 UDP 套接字上调用 connect 没有三次握手的过程。本质上,connect 函数是一个重载的函数,针对不同的套接字,它会做不同的事情。

  • 无连接的 UDP 套接字(unconnected UDP socket),新创建的 UDP 套接字默认如此;
  • 有连接的 UDP 套接字(connected UDP socket),对 UDP 套接字调用 connect.

3. 有连接 VS 无连接

借助这篇文章,我们再次讨论,什么是有连接,什么是无连接。

3.1 sockname 与 peername

在网络编程中,对于有连接的协议来说,有两个基本的概念,即 sockname 与 peername,这是什么意思呢?

sockname 表示本地(local)套接字地址,而 peername 表示外地(foreign)套接字地址。有两个函数 getsockname 与 getpeername 可以用来获取有连接协议的本地套接字地址与外地套接字地址。

连接,是一种抽象的概念,它相当于 sockname + peername. 因此,我们说:

有连接 = {sockname, peername} = {(ip1:port1), (ip2:port2)}

3.2 再看 bind 与 connect

很遗憾,存在 getsockname 与 getpeername 这两个函数,却不见 setsockname 与 setpeername 这样的函数,没错,看小标题你已经猜到了。

bind 函数本质上就是 setsockname,而 connect 函数本质上就是 setpeername 函数。unp 在介绍有连接 UDP 的时候就提到过:

bind 函数更好的名字是 setsockname,而 connect 函数更好的名字是 setpeername

3.3 有连接与无连接

现在,我们可以大体上已经把握了有连接与无连接的区别,即有连接就是 sockname + peername,而无连接,总是缺少其中的 peername。

4. 让 UDP 套接字成为有连接

作为 UDP 客户端来说,默认情况下它是无连接的,要想让其变成有连接,只要给它指定 peername 即可,使用 setpeername 函数,哦不!应该是 connect 函数。

// 伪代码 int sockfd; struct sockaddr_in servaddr;  servaddr = resolve(ip, port); sockfd = socket(AF_INET, SOCK_DGRAM, 0);  // 调用 connect,设置 peername connect(sockfd, servaddr);

5. 注意事项

有连接的 UDP 套接字相比无连接的 UDP 发生了三点变化:

  • 1) 不能再使用 sendto 函数指定目的 IP 和 port,这个参数需要指定成 NULL,或者干脆使用 write 函数。
  • 2) 不需要再使用 recvfrom 来获取数据报的发送者了,应该改用 read 或 recv 等函数。
  • 3) 有连接的 UDP 套接字引发错误,会返回给它所在的进程。无连接的 UDP 套接字不会。

对于第三点,内核只是检查是否存在立即可知的错误(例如一个显然不可达的目的地)。

6. 实验

  • 正常情况


这里写图片描述
图1 正常情况

  • 主机存在,服务未启动


这里写图片描述
图2 主机存在,服务未启动

  • 主机不存在


这里写图片描述
图3 主机不存在(未开机),阻塞在 recvfrom 上了

6. 总结

  • 掌握有连接和无连接的概念
  • 知道如何让 UDP 变成有连接

说明:本文转自blog.csdn.net,用于学习交流分享,仅代表原文作者观点。如有疑问,请联系我们删除~