首页 > 其他分享 >[转帖]端口复用

[转帖]端口复用

时间:2024-06-17 13:21:36浏览次数:22  
标签:sk struct sock 端口 复用 转帖 SO 接字

https://cxd2014.github.io/2018/09/12/port-reuse/

 

 

SO_REUSEPORT套接字选项

从Linux 3.9内核版本之后Linux网络协议栈开始支持SO_REUSEPORT套接字选项,这个新的选项允许一个主机上的多个套接字绑定到同一个端口上,它的目的是提高运行在多核CPU上的多线程网络服务应用的处理性能。

他的使用也非常简单,如果多个进程或者线程都设置了下面这个选项,则他们可以同时绑定到同一个端口上:

int sfd = socket(domain, socktype, 0);

int optval = 1;
setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));

bind(sfd, (struct sockaddr *) &addr, addrlen);

只要第一个进程在绑定端口时设置了这个选项,则其他进程也可以通过设置这个选项来绑定到同一个端口上。 要求第一个进程必须设置SO_REUSEPORT这个选项的原因是防止端口劫持–一些流氓进程通过绑定正在被使用的端口上,来获取其进程接收到的连接请求和数据。为了防止其他不必要的进程通过SO_REUSEPORT选项劫持端口,所有之后绑定这个端口的进程都需要设置和第一个进程相同的user ID

TCP和UDP都可以使用SO_REUSEPORT选项。对于TCP它允许多个套接字监听同一个端口号,这样每个线程都可以调用accept()来处理连接, 避免了传统多线程服务中通常使用一个单一进程处理连接请求,而这个单一进程很可能会成为整个系统的瓶颈。 传统多线程服务中的另一种处理方法是多个线程或者进程对同一个套接字循环调用accept()函数处理连接请求,形式如下:

while (1) {
    new_fd = accept(...);
    process_connection(new_fd);
}

这种处理方式也会有一个问题:多个线程之间不能均衡的处理请求,有些线程处理了大量请求,有些线程处理了少量请求,这种不均衡会降低多核CPU的利用率。 而SO_REUSEPORT会更加均衡的分发请求到不同线程或者进程上。

SO_REUSEPORT选项分发数据包的方法是计算对端IP、端口加上本地IP、端口这四个值的哈希值,通过这个哈希值将数据包分发到不同进程上。 这样就可以保证同一个连接的数据包都被分发到同一个进程中去处理。

SO_REUSEPORT套接字选项在内核中的实现

这里只看UDP协议的实现, 当设置了SO_REUSEPORT套接字选项之后,绑定在同一个端口号的套接字在内核中会形成一个数组,保存在sock_reuseport结构体中, 在调用bind()函数时,会调用到/net/core/sock_reuseport.c文件中的reuseport_add_sock函数,此函数用来将当前套接字添加到数组中。

/* /include/net/sock_reuseport.h */
struct sock_reuseport {
	struct rcu_head		rcu;

	u16			max_socks;	/* length of socks */
	u16			num_socks;	/* elements in socks */
	struct bpf_prog __rcu	*prog;		/* optional BPF sock selector */
	struct sock		*socks[0];	/* 绑定在同一个端口号的套接字指针数组 */
};

在这篇文章中Linux协议栈–UDP协议的发送和接收我们说过当UDP数据到达IP层之后,会调用__udp4_lib_rcv函数将数据包存放到UDP的数据接收缓冲区中。在存放之前会调用__udp4_lib_lookup_skb函数找到这个数据包对应的sock。最终会调用__udp4_lib_lookup函数进行实际的查找工作:

struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr,
		__be16 sport, __be32 daddr, __be16 dport, int dif,
		int sdif, struct udp_table *udptable, struct sk_buff *skb)
{
    ...
begin:
	result = NULL;
	badness = 0;
    /* 遍历链表 */
	sk_for_each_rcu(sk, &hslot->head) {
        /* 根据五元组等信息来进行匹配 */
		score = compute_score(sk, net, saddr, sport,
				      daddr, hnum, dif, sdif, exact_dif);
		if (score > badness) {
            /* 匹配到之后,判断是否设置了 SO_REUSEPORT 选项 */
			if (sk->sk_reuseport) {
                /* 根据源端口、IP和接收端口、IP这四个值计算一个哈希值 */
				hash = udp_ehashfn(net, daddr, hnum,
						   saddr, sport);
                /* 根据这个哈希值,将数据包分发到对应的sock上 */
				result = reuseport_select_sock(sk, hash, skb,
							sizeof(struct udphdr));
				if (result)
					return result;
			}
			result = sk;
			badness = score;
		}
	}
	return result;
}

找到对应的sock之后,调用udp_queue_rcv_skb函数将数据包存放到此套接字的缓冲区中,之后调用sk->sk_data_ready(sk)函数指针,此函数指针在创建套接字的时候初始化为sock_def_readable函数。这个函数会将对应的进程唤醒,来接收数据包。

static void sock_def_readable(struct sock *sk)
{
	struct socket_wq *wq;

	rcu_read_lock();
	wq = rcu_dereference(sk->sk_wq);
	if (skwq_has_sleeper(wq))
		wake_up_interruptible_sync_poll(&wq->wait, EPOLLIN | EPOLLPRI |
						EPOLLRDNORM | EPOLLRDBAND);
	sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN);
	rcu_read_unlock();
}

标签:sk,struct,sock,端口,复用,转帖,SO,接字
From: https://www.cnblogs.com/jinanxiaolaohu/p/18252187

相关文章

  • 创建Docker容器与外部机通信(端口映射的方式)
    一、检查端口是否被占用1.1 查看正在使用中的TCP和UDP端口:ss-tulnLISTEN:表示端口正在监听连接,意味着这些端口已经被系统服务使用。比如,如果你看到"tcp000.0.0.0:80800.0.0.0:*LISTEN",这意味着8080端口被一个服务监听,并等待连接。 判断端口是否被占用:如果你想......
  • 端口服务
    端口服务信息扫描的思路一个服务一个端口本机端口​ Windows​ netstat-aon|findstr3306​ LInux​ netstat-an|grep3306远程机器端口​telnet192.168.142.13780​ wget192.168.142.13780​ nc-vz192.168.142.137445​ nc-vz192.168.1......
  • python IP 端口 socket tcp 介绍
    IP端口介绍1、IPIP地址是分配给网络设备上网使用的数字标签,它能够标识网络中唯一的一台设备windows环境可以使用ipconfig来查看自己的iplinux环境可以使用ifconfig来查看自己的ip2、端口端口是传输数据的通道,每个操作系统上都有65535个端口,通过对应的端口号把数......
  • 【iOS】自定义cell及其复用机制
    文章目录cell的复用注册非注册两者的区别自定义cellcell的复用当用户滚动UITableView或UICollectionView时,只有少量可见的cell会被实际创建和显示。对于那些暂时不可见的cell,系统会将它们缓存起来以备将来复用。这就是所谓的cell复用机制。为什么需要......
  • 基于交换机端口划分VLAN 千字解析
    实验目的及要求:(1)掌握交换机配置的几种模式及基本配置命令。(2)掌握VLAN的原理及基于交换机端口的VLAN划分方法。实验方法:基于单台交换机实现端口VLAN划分并验证;使用cicsopackettracer模拟器完成网络拓扑图如下所示:在三台PC机上设置IP地址如下:连通性测试:三台计算机两......
  • Apache配置多个项目公用80端口
    打开Apache的配置文件httpd.conf,通常位于/etc/httpd/conf/httpd.conf或/etc/apache2/httpd.conf。 确保NameVirtualHost*:80指令被解注释(移除#),以启用基于域名的虚拟主机。 为每个网站添加<VirtualHost>配置块:<VirtualHost*:80>ServerAdminwebmaster@e......
  • 基于Python+scopy实现的渗透测试工具对网站URL以及端口进行漏洞检测系统
    目录摘要2Abstract3第1章绪论51.1研究背景与意义51.2国内外研究现状和发展趋势51.3本论文主要工作及组织结构61.3.1论文主要研究工作61.3.2论文的组织结构6第2章web安全评估及测试的介绍82.1渗透测试82.2web安全评估8第3章渗透测试及安......
  • 阻塞IO、非阻塞IO、IO多路复用和信号驱动IO简介(简单易懂、纯小白)
    一、分类在UNIX或Liunx下主要有4中IO模型阻塞IO:最简单、最常用、效率最低阻塞IO简介和代码示例-CSDN博客当进程执行读操作的时候,如果缓冲区有内容,则继续读取内容向下执行。缓冲区没内容,进程进入休眠态,直到缓冲区中再次有内容,由内核唤醒进程,读取缓冲区的内容,然后继续向下执......
  • 端口占用多:UE4/UE5像素流送云推流时如何优化端口使用?
    许多用户反映,在使用UE4或UE5进行像素流送云推流时,端口的占用数量较多。这促使我们思考,是否有方案能够减少这种资源占用?目前,像素流送技术对于端口的要求是每个独立用户占用一个端口。然而,但在实际场景中,尤其在用户数量众多,访问人数大的情况下,往往无法提供足够的端口资源。这主......
  • Docker的通俗理解和通过宿主机端口访问Redis容器的实例
    前言本文解决的问题:入门docker理解镜像与容器、宿主机的概念理解Docker的常用指令创建redis容器,并通过宿主机端口访问默认读者的知识背景:使用过git初次使用Docker本文不会对Docker的定义作出解释,不会涉及Docker的实现原理,旨在帮助读者快速入门docker,理解......