掃二維碼與項(xiàng)目經(jīng)理溝通
我們?cè)谖⑿派?4小時(shí)期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營(yíng)咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
在網(wǎng)絡(luò)通信中,端口是用來區(qū)分不同應(yīng)用程序的標(biāo)識(shí)符。每個(gè)應(yīng)用程序都需要偵聽一個(gè)端口來接收來自網(wǎng)絡(luò)的數(shù)據(jù)傳輸。對(duì)于Linux C 編程而言,實(shí)現(xiàn)端口監(jiān)聽是非常重要的一項(xiàng)功能。本文將介紹Linux C編程中常見的端口監(jiān)聽實(shí)現(xiàn)方法。

1. 使用socket系統(tǒng)調(diào)用
socket(套接字)是Linux系統(tǒng)中用于網(wǎng)絡(luò)通信的重要API之一。通過socket,應(yīng)用程序可以創(chuàng)建一個(gè)網(wǎng)絡(luò)套接字,并通過這個(gè)套接字與網(wǎng)絡(luò)進(jìn)行通信。
在Linux C編程中,我們可以使用socket系統(tǒng)調(diào)用來創(chuàng)建一個(gè)監(jiān)聽套接字,從而實(shí)現(xiàn)端口的監(jiān)聽。下面是具體的步驟:
① 創(chuàng)建監(jiān)聽套接字
通過socket()函數(shù)創(chuàng)建一個(gè)監(jiān)聽套接字。這個(gè)函數(shù)的之一個(gè)參數(shù)指定協(xié)議族(比如,IPv4或IPv6),第二個(gè)參數(shù)指定套接字的類型(比如,TCP或UDP),第三個(gè)參數(shù)指定協(xié)議類型(通常為0,表示使用默認(rèn)協(xié)議)。
int listening_socket = socket(AF_INET, SOCK_STREAM, 0);
② 綁定套接字到端口
使用bind()函數(shù)將監(jiān)聽套接字綁定到指定端口。這個(gè)函數(shù)的之一個(gè)參數(shù)是監(jiān)聽套接字,第二個(gè)參數(shù)是一個(gè)sockaddr結(jié)構(gòu)體指針,用來描述端口和IP地址等信息。需要注意的是,sockaddr結(jié)構(gòu)體的成員必須按照網(wǎng)絡(luò)字節(jié)序進(jìn)行填充。下面是一個(gè)示例:
struct sockaddr_in server_address;
memset(&server_address, 0, sizeof(server_address)); // 清空結(jié)構(gòu)體
server_address.sin_family = AF_INET;
server_address.sin_port = htons(port_number); // 將端口號(hào)轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序
server_address.sin_addr.s_addr = htonl(INADDR_ANY); // 等價(jià)于0.0.0.0,表示綁定到所有網(wǎng)卡上
int ret = bind(listening_socket, (struct sockaddr*)&server_address, sizeof(server_address));
③ 開始監(jiān)聽
使用listen()函數(shù)將監(jiān)聽套接字設(shè)置成被動(dòng)監(jiān)聽狀態(tài),等待客戶端發(fā)起連接請(qǐng)求。這個(gè)函數(shù)的之一個(gè)參數(shù)是監(jiān)聽套接字,第二個(gè)參數(shù)是等待連接請(qǐng)求的隊(duì)列長(zhǎng)度(通常為5)。
int ret = listen(listening_socket, 5);
④ 處理客戶端連接
使用accept()函數(shù)接收客戶端連接請(qǐng)求,并返回一個(gè)新的套接字,用于與客戶端進(jìn)行通信。這個(gè)函數(shù)的之一個(gè)參數(shù)是監(jiān)聽套接字,第二個(gè)參數(shù)是一個(gè)指向sockaddr結(jié)構(gòu)體的指針,用于存儲(chǔ)客戶端的IP地址和端口號(hào)。需要注意的是,accept()函數(shù)是一個(gè)阻塞函數(shù),如果沒有客戶端連接請(qǐng)求,程序?qū)⒁恢弊枞谶@里。
struct sockaddr_in client_address;
socklen_t client_address_len = sizeof(client_address);
int client_socket = accept(listening_socket, (struct sockaddr*)&client_address, &client_address_len);
2. 使用select系統(tǒng)調(diào)用
在多并發(fā)的網(wǎng)絡(luò)通信中,使用socket系統(tǒng)調(diào)用會(huì)帶來一個(gè)重要的問題:阻塞。如果只有一個(gè)客戶端連接請(qǐng)求,我們的程序仍然會(huì)阻塞在accept()函數(shù)處,無法響應(yīng)其他客戶端的請(qǐng)求。為了解決這個(gè)問題,可以使用select系統(tǒng)調(diào)用。
select()函數(shù)可以監(jiān)聽多個(gè)文件描述符,當(dāng)其中有文件描述符就緒(比如可以讀寫)時(shí),它會(huì)返回,并且可以通過FD_ISSET()宏判斷哪些文件描述符已經(jīng)就緒。下面是使用select()函數(shù)實(shí)現(xiàn)端口監(jiān)聽的基本步驟:
① 創(chuàng)建監(jiān)聽套接字
同樣地,通過socket()函數(shù)創(chuàng)建一個(gè)監(jiān)聽套接字。
② 綁定套接字到端口
同樣地,使用bind()函數(shù)將監(jiān)聽套接字綁定到指定端口。
③ 開始監(jiān)聽
同樣地,使用listen()函數(shù)將監(jiān)聽套接字設(shè)置成被動(dòng)監(jiān)聽狀態(tài),等待客戶端發(fā)起連接請(qǐng)求。但是這里我們不會(huì)阻塞在accept()函數(shù)處。
int max_fd = listening_socket;
fd_set read_set;
fd_set ready_set;
FD_ZERO(&read_set);
FD_SET(listening_socket, &read_set);
while (1) {
ready_set = read_set;
int ret = select(max_fd+1, &ready_set, NULL, NULL, NULL);
if (ret
perror(“select”);
exit(-1);
}
if (FD_ISSET(listening_socket, &ready_set)) {
// 有連接請(qǐng)求,處理連接
} else {
// 有其他數(shù)據(jù)到達(dá),處理數(shù)據(jù)
}
}
在循環(huán)中,我們先使用FD_ZERO()和FD_SET()宏將監(jiān)聽套接字添加到read_set中,然后不斷監(jiān)聽中的文件描述符是否就緒。如果有文件描述符就緒,則使用FD_ISSET()宏判斷具體是哪一個(gè)文件描述符就緒,然后進(jìn)行相應(yīng)的處理。需要注意的是,我們需要保證每次循環(huán)都重新賦值ready_set,因?yàn)檫@個(gè)會(huì)被select()函數(shù)修改。
3. 使用epoll系統(tǒng)調(diào)用
select系統(tǒng)調(diào)用雖然可以避免單線程、單進(jìn)程網(wǎng)絡(luò)編程中的阻塞問題,但是在高并況下性能和擴(kuò)展性較差。為了解決這個(gè)問題,Linux內(nèi)核引入了epoll系統(tǒng)調(diào)用。
epoll()函數(shù)可以同時(shí)監(jiān)聽多個(gè)文件描述符,并且只會(huì)將就緒的文件描述符加入到epoll實(shí)例中。這樣,我們可以輕松地實(shí)現(xiàn)高并發(fā)網(wǎng)絡(luò)編程。下面是使用epoll()函數(shù)實(shí)現(xiàn)端口監(jiān)聽的基本步驟:
① 創(chuàng)建epoll實(shí)例
使用epoll_create()函數(shù)創(chuàng)建一個(gè)epoll實(shí)例,返回一個(gè)文件描述符。這個(gè)函數(shù)的參數(shù)可以用來指定epoll實(shí)例中更大可以監(jiān)聽的文件描述符數(shù)量。
int epoll_fd = epoll_create(1024);
② 將監(jiān)聽套接字添加到epoll實(shí)例中
使用epoll_ctl()函數(shù)將監(jiān)聽套接字添加到epoll實(shí)例中,用于接收客戶端連接請(qǐng)求。這個(gè)函數(shù)的之一個(gè)參數(shù)是epoll實(shí)例的文件描述符,第二個(gè)參數(shù)是指令類型(比如添加、刪除、修改等),第三個(gè)參數(shù)是要監(jiān)聽的文件描述符,第四個(gè)參數(shù)是一個(gè)epoll_event結(jié)構(gòu)體,用于指定事件類型和處理方式。
struct epoll_event event;
memset(&event, 0, sizeof(event));
event.events = EPOLLIN | EPOLLET; // 監(jiān)聽可讀事件,邊緣觸發(fā)模式
event.data.fd = listening_socket; // 監(jiān)聽套接字的文件描述符
int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listening_socket, &event);
③ 循環(huán)監(jiān)聽套接字文件描述符
使用epoll_wt()函數(shù)循環(huán)監(jiān)聽epoll實(shí)例中的文件描述符,將就緒的文件描述符交給工作線程進(jìn)行處理。這個(gè)函數(shù)的參數(shù)可以用來指定最多等待多長(zhǎng)時(shí)間、最多等待多少文件描述符等。
struct epoll_event events[1024];
while (1) {
int n_ready = epoll_wt(epoll_fd, events, 1024, -1);
if (n_ready
perror(“epoll_wt”);
exit(-1);
}
for (int i = 0; i
int fd = events[i].data.fd;
if (fd == listening_socket) {
// 監(jiān)聽套接字就緒,處理連接請(qǐng)求
} else {
// 其他文件描述符就緒,處理數(shù)據(jù)
}
}
}
需要注意的是,epoll實(shí)例提供了兩種觸發(fā)模式:邊緣觸發(fā)模式和水平觸發(fā)模式。邊緣觸發(fā)模式在文件描述符從未就緒變?yōu)榫途w的瞬間觸發(fā),處理方式更為靈活。而水平觸發(fā)模式則在文件描述符已經(jīng)就緒的情況下持續(xù)觸發(fā),適用于穩(wěn)定傳輸數(shù)據(jù)的場(chǎng)景。
端口監(jiān)聽是Linux C編程中非常重要的一項(xiàng)功能。本文介紹了三種常見的端口監(jiān)聽實(shí)現(xiàn)方法,分別是使用socket系統(tǒng)調(diào)用、select系統(tǒng)調(diào)用和epoll系統(tǒng)調(diào)用。讀者可以根據(jù)自己的需求選擇合適的方法進(jìn)行開發(fā)。需要注意的是,網(wǎng)絡(luò)編程中容易出現(xiàn)阻塞和死鎖等問題,需要謹(jǐn)慎調(diào)試和處理,保證應(yīng)用程序的穩(wěn)定性和可靠性。
成都網(wǎng)站建設(shè)公司-創(chuàng)新互聯(lián),建站經(jīng)驗(yàn)豐富以策略為先導(dǎo)10多年以來專注數(shù)字化網(wǎng)站建設(shè),提供企業(yè)網(wǎng)站建設(shè),高端網(wǎng)站設(shè)計(jì),響應(yīng)式網(wǎng)站制作,設(shè)計(jì)師量身打造品牌風(fēng)格,熱線:028-86922220那你要做好久
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
int sockfd,new_fd;
struct sockaddr_in my_addr;
struct sockaddr_in their_addr;
int sin_size;
/埋衡/建立TCP套接口
if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1)
{
printf(“create socket error”);
perror(“socket”);
exit(1);
}
//初始化結(jié)構(gòu)體,并綁定8080端口
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(8080);
my_addr.sin_addr.s_addr = INADDR_ANY;
bzero(&(my_addr.sin_zero),8);
//綁定套接口
if(bind(sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))==-1)
{
perror(“bind socket error”);
exit(1);
}
//創(chuàng)建監(jiān)聽套接口
if(listen(sockfd,10)==-1)
{
perror(“l(fā)isten”);
exit(1);
}
//等待連接
while(1)
{
sin_size = sizeof(struct sockaddr_in);
printf(“server is run.\n”);
//如果建立連接,將產(chǎn)生一個(gè)全新的套接字
if((new_fd = accept(sockfd,(struct sockaddr *)&their_addr,&sin_size))==-1)
{
perror(“accept”);
exit(1);
}
printf(“accept success.\n”);
//生成一個(gè)子進(jìn)程來完成和客戶端的會(huì)話,父進(jìn)程繼續(xù)監(jiān)聽
if(!fork())
{
printf(“create new thred success.\n”);
//讀取客戶端發(fā)來的信息
int numbytes;
char buff;
memset(buff,0,256);
if((numbytes = recv(new_fd,buff,sizeof(buff),0))==-1)
{
perror(“recv”);
exit(1);
}
printf(“%s”,buff);
//將從客戶端接收到的信息再發(fā)回客戶端
if(send(new_fd,buff,strlen(buff),0)==-1)
perror(“send”);
close(new_fd);
exit(0);
}
close(new_fd);
}
close(sockfd);
在Linux虛擬機(jī)中查看tcp協(xié)議端口,可以使用netstat命令,它可以顯示當(dāng)前系統(tǒng)中所有正在使用的網(wǎng)絡(luò)端口以及網(wǎng)絡(luò)連接狀態(tài)。
1、查看所有TCP連接:
$ netstat -na | grep “tcp”
2、查看所有TCP監(jiān)聽端口:
$ netstat -na | grep “tcp” | grep “LISTEN”
3、查看指定端口的TCP連接:
$ netstat -na | grep “tcp” | grep “:80”
4、查看所有UDP端口
$ netstat -na | grep “udp”
5、查看指定端口的UDP連接
$ netstat -na | grep “udp” | grep “:53”
關(guān)于linux c 端口監(jiān)聽的介紹到此就結(jié)束了,不知道你從中找到你需要的信息了嗎 ?如果你還想了解更多這方面的信息,記得收藏關(guān)注本站。
成都創(chuàng)新互聯(lián)科技公司主營(yíng):網(wǎng)站設(shè)計(jì)、網(wǎng)站建設(shè)、小程序制作、成都軟件開發(fā)、網(wǎng)頁(yè)設(shè)計(jì)、微信開發(fā)、成都小程序開發(fā)、網(wǎng)站制作、網(wǎng)站開發(fā)等業(yè)務(wù),是專業(yè)的成都做小程序公司、成都網(wǎng)站建設(shè)公司、成都做網(wǎng)站的公司。創(chuàng)新互聯(lián)公司集小程序制作創(chuàng)意,網(wǎng)站制作策劃,畫冊(cè)、網(wǎng)頁(yè)、VI設(shè)計(jì),網(wǎng)站、軟件、微信、小程序開發(fā)于一體。

我們?cè)谖⑿派?4小時(shí)期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營(yíng)咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流