很久之前写过以上:套接字socket的底层来龙去脉、sockfs文件系统的实现,可以作为本文的前置知识进行学习浏览。
先来一张本文中核心的一张图,具体可以看后面文章的解释:
本文从socket的bind系统调用进行分析,主要是了解一下bind背后,Linux内核是如何进行端口绑定、如何管理本地众多的端口号。
先直观感受bind系统调用背后的端口管理、端口复用
#include < stdio.h >
#include < stdlib.h >
#include < string.h >
#include < unistd.h >
#include < sys/socket.h >
#include < netinet/in.h >
#include < arpa/inet.h >
int main(int argc, char *argv[])
{
int sockfd_one;
int err_log;
sockfd_one = socket(AF_INET, SOCK_STREAM, 0); //创建TCP套接字one
if(sockfd_one < 0)
{
perror("sockfd_one");
exit(-1);
}
// 设置本地网络信息
struct sockaddr_in my_addr;
bzero(&my_addr, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(8000); // 端口为8000
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
// 绑定,端口为8000
err_log = bind(sockfd_one, (struct sockaddr*)&my_addr, sizeof(my_addr));
if(err_log != 0)
{
perror("bind sockfd_one");
close(sockfd_one);
exit(-1);
}
int sockfd_two;
sockfd_two = socket(AF_INET, SOCK_STREAM, 0); //创建TCP套接字two
if(sockfd_two < 0)
{
perror("sockfd_two");
exit(-1);
}
// 新套接字sockfd_two,继续绑定8000端口,绑定失败
// 因为8000端口已被占用,默认情况下,端口没有释放,无法绑定
err_log = bind(sockfd_two, (struct sockaddr*)&my_addr, sizeof(my_addr));
if(err_log != 0)
{
perror("bind sockfd_two");
close(sockfd_two);
exit(-1);
}
close(sockfd_one);
close(sockfd_two);
return 0;
}
可以看到端口重复绑定导致了第二个套接字创建失败,我们通过setsockopt系统调用在创建socket后设置端口可复用:
int opt = 1;
// sockfd为需要端口复用的套接字
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&opt, sizeof(opt));
具体如下:
#include < stdio.h >
#include < stdlib.h >
#include < string.h >
#include < unistd.h >
#include < sys/socket.h >
#include < netinet/in.h >
#include < arpa/inet.h >
int main(int argc, char *argv[])
{
int sockfd_one;
int err_log;
sockfd_one = socket(AF_INET, SOCK_STREAM, 0); //创建UDP套接字one
if(sockfd_one < 0)
{
perror("sockfd_one");
exit(-1);
}
// 设置本地网络信息
struct sockaddr_in my_addr;
bzero(&my_addr, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(8000); // 端口为8000
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
// 在sockfd_one绑定bind之前,设置其端口复用
int opt = 1;
setsockopt( sockfd_one, SOL_SOCKET,SO_REUSEADDR,
(const void *)&opt, sizeof(opt) );
// 绑定,端口为8000
err_log = bind(sockfd_one, (struct sockaddr*)&my_addr, sizeof(my_addr));
if(err_log != 0)
{
perror("bind sockfd_one");
close(sockfd_one);
exit(-1);
}
int sockfd_two;
sockfd_two = socket(AF_INET, SOCK_STREAM, 0); //创建UDP套接字two
if(sockfd_two < 0)
{
perror("sockfd_two");
exit(-1);
}
// 在sockfd_two绑定bind之前,设置其端口复用
opt = 1;
setsockopt( sockfd_two, SOL_SOCKET,SO_REUSEADDR,
(const void *)&opt, sizeof(opt) );
// 新套接字sockfd_two,继续绑定8000端口,成功
err_log = bind(sockfd_two, (struct sockaddr*)&my_addr, sizeof(my_addr));
if(err_log != 0)
{
perror("bind sockfd_two");
close(sockfd_two);
exit(-1);
}
printf("two socket create success!n");
close(sockfd_one);
close(sockfd_two);
return 0;
}
如上,两个套接字绑定同一个端口都创建成功。下面将从bind出发分析bind是如何端口管理、复用的。
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。
举报投诉
-
内核
+关注
关注
3文章
1362浏览量
40221 -
Linux
+关注
关注
87文章
11222浏览量
208885 -
系统
+关注
关注
1文章
1006浏览量
21310
发布评论请先 登录
相关推荐
udp_bind这个绑定的端口怎么解除?
请教下,udp_bind 这个绑定的端口,刚开始是可以的,但是重新绑定时返回错误,有什么方法可以在 重新绑定前解除之前的绑定 ?
发表于 04-22 07:41
TCP server 不能 bind 80 端口?
后程序只开一个 AP 模式下的 TCP server ,测试。 但是发现 80 端口没法 bind ,错误码是 -98 ,意思是端口已被占用? 换一个其它端口号(比如 12345)就
发表于 05-14 00:33
端口复用概念
记录一下,方便以后翻阅~主要内容:1)端口复用;2)端口重映射;官方资料:《STM32中文参考手册V10》第8章 通用和复用功能IO(GPIO和AFIO)1.
发表于 01-11 07:43
Bind源代码包安装
先到官方下载Bind的安装包 wgetftp://ftp.isc.org/isc/bind9/9.6.0-P1/bind-9.6.0-P1.tar.gz tar xzvf
发表于 04-04 20:30
•23次下载
STM32单片机端口复用和端口重映射
STM32单片机端口复用和端口重映射STM32单片机上有很多I/O口,也有很多的内置外设,比如I2C、ADC、DAC、USART等都属于内置外设。这些内置外设基本都是与I/O口共用管脚的,也就是I
发表于 12-28 19:23
•8次下载
什么是bind?你真的熟悉bind吗?
bind()方法创建一个新的函数,在bind()被调用时,这个新函数的this被指定 bind()的第一个参数,而其余参数将作为新函数的参数,供调用
网络系统调用网络套接字入口函数
网络套接字入口函数 //所有的网络套接字系统调用函数(socket bind listen connect )都使用一个共同的入口函数:sys_socketcall /* 第一个参数call表示被
Linux内核分析 bind端口选择
给 bind 传 递的地址参数中,port 字段为 0,那么就会自动选择参数。 如代码所示,当端口port没有指定时,调用inet_csk_find_open_port(sk, port): if (!port) { head
评论