多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php開源 > php教程 > Linux網絡編程:端口復用

Linux網絡編程:端口復用

來源:程序員人生   發布時間:2015-09-06 08:16:00 閱讀次數:3734次

在《綁定( bind )端口需要注意的問題》提到:1個網絡利用程序只能綁定1個端口( 1個套接字只能綁定1個端口 )。


實際上,默許的情況下,如果1個網絡利用程序的1個套接字 綁定了1個端口( 占用了 8000 ),這時候候,別的套接字就沒法使用這個端口( 8000 ), 驗證例子以下:

#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_DGRAM, 0); //創建UDP套接字one if(sockfd_one < 0) { perror("sockfd_one"); exit(⑴); } // 設置本地網絡信息 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(⑴); } int sockfd_two; sockfd_two = socket(AF_INET, SOCK_DGRAM, 0); //創建UDP套接字two if(sockfd_two < 0) { perror("sockfd_two"); exit(⑴); } // 新套接字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(⑴); } close(sockfd_one); close(sockfd_two); return 0; }

程序編譯運行后結果以下:



那如何讓sockfd_one, sockfd_two兩個套接字都能成功綁定8000端口呢?這時候候就需要要到端口復用了。端口復用允許在1個利用程序可以把 n 個套接字綁在1個端口上而不出錯。

設置socket的SO_REUSEADDR選項,便可實現端口復用:

int opt = 1; // sockfd為需要端口復用的套接字 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&opt, sizeof(opt));

SO_REUSEADDR可以用在以下4種情況下。 (摘自《Unix網絡編程》卷1,即UNPv1)

1、當有1個有相同本地地址和端口的socket1處于TIME_WAIT狀態時,而你啟動的程序的socket2要占用該地址和端口,你的程序就要用到該選項。
2、SO_REUSEADDR允許同1port上啟動同1服務器的多個實例(多個進程)。但每一個實例綁定的IP地址是不能相同的。在有多塊網卡或用IP Alias技術的機器可以測試這類情況。
 3、SO_REUSEADDR允許單個進程綁定相同的端口到多個socket上,但每一個socket綁定的ip地址不同。這和2很相似,區分請看UNPv1。
4、SO_REUSEADDR允許完全相同的地址和端口的重復綁定。但這只用于UDP的多播,不用于TCP。


需要注意的是,設置端口復用函數要在綁定之前調用,而且只要綁定到同1個端口的所有套接字都得設置復用:

// sockfd_one, sockfd_two都要設置端口復用 // 在sockfd_one綁定bind之前,設置其端口復用 int opt = 1; setsockopt( sockfd_one, SOL_SOCKET,SO_REUSEADDR, (const void *)&opt, sizeof(opt) ); err_log = bind(sockfd_one, (struct sockaddr*)&my_addr, sizeof(my_addr)); // 在sockfd_two綁定bind之前,設置其端口復用 opt = 1; setsockopt( sockfd_two, SOL_SOCKET,SO_REUSEADDR,(const void *)&opt, sizeof(opt) ); err_log = bind(sockfd_two, (struct sockaddr*)&my_addr, sizeof(my_addr));

端口復用完全代碼以下:

#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_DGRAM, 0); //創建UDP套接字one if(sockfd_one < 0) { perror("sockfd_one"); exit(⑴); } // 設置本地網絡信息 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(⑴); } int sockfd_two; sockfd_two = socket(AF_INET, SOCK_DGRAM, 0); //創建UDP套接字two if(sockfd_two < 0) { perror("sockfd_two"); exit(⑴); } // 在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(⑴); } close(sockfd_one); close(sockfd_two); return 0; }

端口復用允許在1個利用程序可以把 n 個套接字綁在1個端口上而不出錯。同時,這 n 個套接字發送信息都正常,沒有問題。但是,這些套接字其實不是所有都能讀取信息,只有最后1個套接字會正常接收數據。


下面,我們在之前的代碼上,添加兩個線程,分別負責接收sockfd_one,sockfd_two的信息:

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <pthread.h> // 線程1的回調函數 void *recv_one(void *arg) { printf("===========recv_one============== "); int sockfd = (int )arg; while(1){ int recv_len; char recv_buf[512] = ""; struct sockaddr_in client_addr; char cli_ip[INET_ADDRSTRLEN] = "";//INET_ADDRSTRLEN=16 socklen_t cliaddr_len = sizeof(client_addr); recv_len = recvfrom(sockfd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr*)&client_addr, &cliaddr_len); inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN); printf(" ip:%s ,port:%d ",cli_ip, ntohs(client_addr.sin_port)); printf("sockfd_one =========== data(%d):%s ",recv_len,recv_buf); } return NULL; } // 線程2的回調函數 void *recv_two(void *arg) { printf("+++++++++recv_two++++++++++++++ "); int sockfd = (int )arg; while(1){ int recv_len; char recv_buf[512] = ""; struct sockaddr_in client_addr; char cli_ip[INET_ADDRSTRLEN] = "";//INET_ADDRSTRLEN=16 socklen_t cliaddr_len = sizeof(client_addr); recv_len = recvfrom(sockfd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr*)&client_addr, &cliaddr_len); inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN); printf(" ip:%s ,port:%d ",cli_ip, ntohs(client_addr.sin_port)); printf("sockfd_two @@@@@@@@@@@@@@@ data(%d):%s ",recv_len,recv_buf); } return NULL; } int main(int argc, char *argv[]) { int err_log; /////////////////////////sockfd_one int sockfd_one; sockfd_one = socket(AF_INET, SOCK_DGRAM, 0); //創建UDP套接字one if(sockfd_one < 0) { perror("sockfd_one"); exit(⑴); } // 設置本地網絡信息 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 pthread_t tid_one; pthread_create(&tid_one, NULL, recv_one, (void *)sockfd_one); /////////////////////////sockfd_two int sockfd_two; sockfd_two = socket(AF_INET, SOCK_DGRAM, 0); //創建UDP套接字two if(sockfd_two < 0) { perror("sockfd_two"); exit(⑴); } // 在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(⑴); } //接收信息線程2 pthread_t tid_two; pthread_create(&tid_two, NULL, recv_two, (void *)sockfd_two); while(1){ // 讓程序阻塞在這,不結束 NULL; } close(sockfd_one); close(sockfd_two); return 0; }

接著,通過網絡調試助手給這個服務器發送數據,結果顯示,只有最后1個套接字sockfd_two會正常接收數據:



我們上面的用法,實際上沒有太大的意義。端口復用最經常使用的用處應當是避免服務器重啟時之前綁定的端口還未釋放或程序突然退出而系統沒有釋放端口。這類情況下如果設定了端口復用,則新啟動的服務器進程可以直接綁定端口。如果沒有設定端口復用,綁定會失敗,提示ADDR已在使用中――那只好等等再重試了,麻煩!



源代碼下載請點此處。

生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 视频一区色眯眯视频在线 | 日本成本人在线观看免费视频 | 久久婷婷人人澡人人爱91 | 亚洲最大网站 | 免费区欧美一级毛片 | 韩国三级做爰中文字幕 | 美国一级毛片在线观看 | 日韩欧美国产另类 | 最近中文字幕免费6 | 亚洲国产色综合有声小说 | 噜噜噜噜噜视频在线观看 | 69视频网站| 亚洲精品一区henhen色 | 亚洲精品视频在线免费 | 国产一区二区三区国产精品 | 国产一区二区在线免费观看 | 欧美大陆日韩 | 中国高清色视频www 中国国产成人精品久久 | a级艳片武则天 | 国产成人精品免费视频大全办公室 | 中文字幕亚洲视频 | 亚洲伊人久久在 | 日韩视频一 | 日本特黄特色aa大片免费 | 五月婷婷在线免费观看 | 国产肥妇| 国产亚洲精品一区999 | 午夜又黄又爽 | 亚洲韩国日本欧美一区二区三区 | 秋霞午夜限制土鳖免费观看 | 精品播放| 中国黄色网址大全 | 福利一区二区 | 男人天堂亚洲天堂 | 一区二区三区免费视频播放器 | 一区二区三区四区视频在线观看 | 永久网站| 日韩精品国产自在久久现线拍 | 91精品国产综合久久欧美 | 日本中文字幕一区二区有码在线 | free欧美xxxxvideo free欧美性杂交hd |