很久之前寫過以上:套接字socket的底層來龍去脈、sockfs文件系統(tǒng)的實(shí)現(xiàn),可以作為本文的前置知識(shí)進(jìn)行學(xué)習(xí)瀏覽。
先來一張本文中核心的一張圖,具體可以看后面文章的解釋:
本文從socket的bind系統(tǒng)調(diào)用進(jìn)行分析,主要是了解一下bind背后,Linux內(nèi)核是如何進(jìn)行端口綁定、如何管理本地眾多的端口號(hào)。
先直觀感受bind系統(tǒng)調(diào)用背后的端口管理、端口復(fù)用
#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); //創(chuàng)建TCP套接字one
if(sockfd_one < 0)
{
perror("sockfd_one");
exit(-1);
}
// 設(shè)置本地網(wǎng)絡(luò)信息
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); //創(chuàng)建TCP套接字two
if(sockfd_two < 0)
{
perror("sockfd_two");
exit(-1);
}
// 新套接字sockfd_two,繼續(xù)綁定8000端口,綁定失敗
// 因?yàn)?000端口已被占用,默認(rèn)情況下,端口沒有釋放,無法綁定
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;
}
可以看到端口重復(fù)綁定導(dǎo)致了第二個(gè)套接字創(chuàng)建失敗,我們通過setsockopt系統(tǒng)調(diào)用在創(chuàng)建socket后設(shè)置端口可復(fù)用:
int opt = 1;
// sockfd為需要端口復(fù)用的套接字
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); //創(chuàng)建UDP套接字one
if(sockfd_one < 0)
{
perror("sockfd_one");
exit(-1);
}
// 設(shè)置本地網(wǎng)絡(luò)信息
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之前,設(shè)置其端口復(fù)用
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); //創(chuàng)建UDP套接字two
if(sockfd_two < 0)
{
perror("sockfd_two");
exit(-1);
}
// 在sockfd_two綁定bind之前,設(shè)置其端口復(fù)用
opt = 1;
setsockopt( sockfd_two, SOL_SOCKET,SO_REUSEADDR,
(const void *)&opt, sizeof(opt) );
// 新套接字sockfd_two,繼續(xù)綁定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;
}
如上,兩個(gè)套接字綁定同一個(gè)端口都創(chuàng)建成功。下面將從bind出發(fā)分析bind是如何端口管理、復(fù)用的。
-
內(nèi)核
+關(guān)注
關(guān)注
3文章
1372瀏覽量
40282 -
Linux
+關(guān)注
關(guān)注
87文章
11296瀏覽量
209348 -
系統(tǒng)
+關(guān)注
關(guān)注
1文章
1015瀏覽量
21332
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論