最近有点想法,有点像溜溜球的赶脚,来回总结了一下,发现以前忙忙碌碌地学习,学的也不少但是能记住不多,平时做的笔记也比较随意,很散,现在梳理感觉有点吃力,莫名的感觉到一味地瞎搞没有认真总结是一种非常不明智做法,效率真心不高。从此刻无论如何只要有新知识必须上了梳理一波,想变强必须严格要求,别懒死啊,兄弟~~~注意学习方法,积累知识,进入正题。。。
背景
最近和一个兄弟在搞可靠udp相关开发,具体实现仿照quic实现,不过做了化简,只取其可靠传输和拥塞控制相关东西,木有证书加密,木有流量控制,木有多流(这个应该是支持,没业务上啊,也算没有)。这位兄弟测试quic时发现网络切换的时候无法做连接迁移而是直接发包失败关闭链接(因为没有实现平台层网络监控,无法感知网络变化进行调整),引发了我的深思为啥我们的udp是私有协议可以做到网络切换仍然可以呢?(也没监控平台网络)比较发现quic 网络库创建socket后会去调 connect函数这样后面的发送就可以使用 send/recv 不需要sendto/recvfrom了,那到底为啥网络切换后发包会失败呢?第一步马上去百度,当也没搜出啥,只能自己玩了,直接上内核源码呗!
正题
直接跳过系统调用过程,来到 net/socket.c 文件,这里就有我们经常使用的socket编程所对应的函数,名字都一样,只不过套了宏:
SYSCALL_DEFINE3(connect, int, fd, struct sockaddr __user *, uservaddr,
int, addrlen)
{
struct socket *sock;
struct sockaddr_storage address;
int err, fput_needed;
// 根据fd获取socket函数创建的socket实例
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (!sock)
goto out;
// 将用户态的远端地址考到内核
err = move_addr_to_kernel(uservaddr, addrlen, &address);
if (err < 0)
goto out_put;
err =
security_socket_connect(sock, (struct sockaddr *)&address, addrlen);
if (err)
goto out_put;
// 调用inet_dgram_connect(ops赋值在socket创建的时候,这里不做展开)
err = sock->ops->connect(sock, (struct sockaddr *)&address, addrlen,
sock->file->f_flags);
out_put:
// 减小socket引用计数
fput_light(sock->file, fput_needed);
out:
return err;
}
// net/ipv4/af_inet.c
int inet_dgram_connect(struct socket *sock, struct sockaddr *uaddr,
int addr_len, int flags)
{
struct sock *sk = sock->sk;
if (addr_len < sizeof(uaddr->sa_family))
return -EINVAL;
if (uaddr->sa_family == AF_UNSPEC)
return sk->sk_prot->disconnect(sk, flags);
// 如果没有bind绑定端口,这里随机绑定一个,不展开
if (!inet_sk(sk)->inet_num && inet_autobind(sk))
return -EAGAIN;
// ip4_datagram_connect
return sk->sk_prot->connect(sk, uaddr, addr_len);
}
// net/ipv4/udp.c
// udp_prot->ip4_datagram_connect->__ip4_datagram_connect
int __ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
struct inet_sock *inet = inet_sk(sk);
struct sockaddr_in *usin = (struct sockaddr_in *) uaddr;
struct flowi4 *fl4;
struct rtable *rt;
__be32 saddr;
int oif;
int err;
if (addr_len < sizeof(*usin))
return -EINVAL;
if (usin->sin_family != AF_INET)
return -EAFNOSUPPORT;
// 重置路由缓存
sk_dst_reset(sk);
// 出口设备号,本地出口IP,即bind的地址
oif = sk->sk_bound_dev_if;
saddr = inet->inet_saddr;
// 绕过
if (ipv4_is_multicast(usin->sin_addr.s_addr)) {
if (!oif)
oif = inet->mc_index;
if (!saddr)
saddr = inet->mc_addr;
}
// 去路由表查找下一跳
fl4 = &inet->cork.fl.u.ip4;
rt = ip_route_connect(fl4, usin->sin_addr.s_addr, saddr,
RT_CONN_FLAGS(sk), oif,
sk->sk_protocol,
inet->inet_sport, usin->sin_port, sk);
if (IS_ERR(rt)) {
err = PTR_ERR(rt)