写点什么

UDP 连接要不要发起 connect

用户头像
kof11321
关注
发布于: 2021 年 01 月 04 日
UDP连接要不要发起connect

什么是 UDP

让我们先来看看 wiki 的定义

In computer networking, the User Datagram Protocol (UDP) is one of the core members of the Internet protocol suite. The protocol was designed by David P. Reed in 1980 and formally defined in RFC 768. With UDP, computer applications can send messages, in this case referred to as datagrams, to other hosts on an Internet Protocol (IP) network. Prior communications are not required in order to set up communication channels or data paths.

UDP uses a simple connectionless communication model with a minimum of protocol mechanisms. UDP provides checksums for data integrity, and port numbers for addressing different functions at the source and destination of the datagram. It has no handshaking dialogues, and thus exposes the user's program to any unreliability of the underlying network; there is no guarantee of delivery, ordering, or duplicate protection. If error-correction facilities are needed at the network interface level, an application may use Transmission Control Protocol (TCP) or Stream Control Transmission Protocol (SCTP) which are designed for this purpose.

UDP is suitable for purposes where error checking and correction are either not necessary or are performed in the application; UDP avoids the overhead of such processing in the protocol stack. Time-sensitive applications often use UDP because dropping packets is preferable to waiting for packets delayed due to retransmission, which may not be an option in a real-time system.[1]


划重点:

  1. UDP 是基于 IP 协议的

  2. 不需要预先建立通道来发送数据

  3. 开销很小,没有传输必达保证,没有排序,没有去重

  4. 应用层做错误监测

  5. 适用于对于丢包不敏感的场景

C 层面的 UDP 编程

问题:UDP 发送到底要不要首先发起 connect 调用

参考连接 1

https://www.geeksforgeeks.org/udp-server-client-implementation-c/


参考连接 2

https://www.geeksforgeeks.org/udp-client-server-using-connect-c-implementation/

非常有意思的是,大部分例子都分成两类:

一类不带 connect 函数,一类带 connect 函数,那么 UDP 发送,到底要不要首先发起 connect 调用呢?

让我们来看看 connect 函数的定义:

https://man7.org/linux/man-pages/man2/connect.2.html

The connect() system call connects the socket referred to by the       file descriptor sockfd to the address specified by addr.  The       addrlen argument specifies the size of addr. If the socket sockfd is of type SOCK_DGRAM, then addr is the       address to which datagrams are sent by default, and the only       address from which datagrams are received. 
复制代码

根据定义,还是不是很清楚,让我们再看看 stack 的答疑https://stackoverflow.com/questions/9741392/can-you-bind-and-connect-both-ends-of-a-udp-connection

An ordinary UDP socket doesn't know anything about its future destinations, so it performs a route lookup each time sendmsg() is called.

However, if connect() is called beforehand with a particular remote receiver's IP and port, the operating system kernel will be able to write down the reference to the route and assign it to the socket, making it significantly faster to send a message if subsequent sendmsg() calls do not specify a receiver (otherwise the previous setting would be ignored), choosing the default one instead.

connect() on a SOCK_DGRAM socket sets the default/send receive address, so then you can just use send and recv. I'm writing it to work over TCP as well so that ends up making some other code common to both protocols.

简单总结一下:

  1. 调用 connect,可以绑定接收发送地址,后续可以直接使用 send/receive 函数,使得 UDP/TCP 编程模型可以保持一致

  2. connect 函数绑定地址后,可以避免查询内核查询 route 表,更快,效率更高

所以要不要使用 connect 函数,还是取决于应用场景。

  1. 如果你的目标 UDP 服务器不变,那么 connect 之后发送接收,效率更高,并且编程模型,可以与 TCP 保持一致

  2. 如果你的 UDP 服务器地址不确定,那么不要 connect。

Go 层面的 UDP 编程

  // 建立连接	conn, err := net.DialUDP("udp", nil, udpAddr)  	// connection 超时时间	err = conn.SetDeadline(time.Now().Add(10 * time.Second))    out []byte	//发送数据	if _, err := conn.Write(out); err != nil {		logger.Errorf("send data failed, err : %v", err)		return err	}
// 接收数据 b := make([]byte, 4096) n, err := conn.Read(b)
复制代码

根据 go 的编程模型来看,write 函数并未指定目标服务器地址,我们可以推测,在之前的 net.DialUDP 操作中,做了之前说到的 connect 操作,将地址与 socket 进行了绑定,后续发送接收就不需要再携带服务器地址了。

查看下 net.DialUDP 的源码,socket_posix.go 里

if raddr != nil {		if rsa, err = raddr.sockaddr(fd.family); err != nil {			return err		}		if crsa, err = fd.connect(ctx, lsa, rsa); err != nil {			return err		}		fd.isConnected = true	}
复制代码

是不是看到了熟悉的 connect 方法 :)

Java 层面的 UDP 编程

待续。。。


发布于: 2021 年 01 月 04 日阅读数: 36
用户头像

kof11321

关注

还未添加个人签名 2018.08.14 加入

还未添加个人简介

评论

发布
暂无评论
UDP连接要不要发起connect