成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Linux網(wǎng)絡(luò)編程之綁定端口注意事項及端口復(fù)用

網(wǎng)絡(luò) 通信技術(shù)
所謂綁定(bind)是指別人連接我只能通過我所綁定的端口,相當(dāng)于,我買了一個手機,別人要想聯(lián)系我,必須要知道我的手機號碼,這時候,我需要怎么辦呢?我需要給手機插上電話卡,固定一個電話號碼,這樣別人就能通過這個電話號碼聯(lián)系我。手機插上電話卡,固定一個電話號碼,類似于綁定(bind)的過程。

所謂綁定(bind)是指別人連接我只能通過我所綁定的端口,相當(dāng)于,我買了一個手機,別人要想聯(lián)系我,必須要知道我的手機號碼,這時候,我需要怎么辦呢?我需要給手機插上電話卡,固定一個電話號碼,這樣別人就能通過這個電話號碼聯(lián)系我。手機插上電話卡,固定一個電話號碼,類似于綁定(bind)的過程,綁定(bind)為了固定一個端口號,別的網(wǎng)絡(luò)程序就可以找到這個端口號,找到這個端口號就能找到這個端口號所對應(yīng)的網(wǎng)絡(luò)應(yīng)用程序。

在網(wǎng)絡(luò)編程里,通常都是在服務(wù)器里綁定(bind)端口,這并不是說客戶端里不能綁定(bind)端口,但這里需要注意的是,一個網(wǎng)絡(luò)應(yīng)用程序只能綁定一個端口( 一個套接字只能 綁定一個端口 )。

一個套接字不能同時綁定多個端口,如下:

  1. #include <stdio.h>    
  2. #include <stdlib.h>    
  3. #include <string.h>    
  4. #include <unistd.h>    
  5. #include <sys/socket.h>    
  6. #include <netinet/in.h>    
  7. #include <arpa/inet.h>    
  8.     
  9. int main(int argc, charchar *argv[])    
  10. {    
  11.     char server_ip[30] = "10.221.20.12";    
  12.     
  13.     int sockfd;    
  14.     sockfd = socket(AF_INET, SOCK_DGRAM, 0);        //創(chuàng)建UDP套接字    
  15.     if(sockfd < 0)    
  16.     {    
  17.         perror("socket");    
  18.         exit(-1);    
  19.     }    
  20.     
  21.     // 初始化本地網(wǎng)絡(luò)信息    
  22.     struct sockaddr_in my_addr;    
  23.     bzero(&my_addr, sizeof(my_addr));    
  24.     my_addr.sin_family = AF_INET;    
  25.     my_addr.sin_port   = htons(8000);    
  26.     my_addr.sin_addr.s_addr = htonl(INADDR_ANY);    
  27.     
  28.     // ***次綁定端口8000    
  29.     int err_log;    
  30.     err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));    
  31.     if(err_log != 0)    
  32.     {    
  33.         perror("bind 8000");    
  34.         close(sockfd);          
  35.         exit(-1);    
  36.     }    
  37.     
  38.     // 又一次綁定別的端口9000, 會綁定失敗    
  39.     my_addr.sin_port = htons(9000);    
  40.     err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));    
  41.     if(err_log != 0)    
  42.     {    
  43.         perror("bind 9000");    
  44.         close(sockfd);          
  45.         exit(-1);    
  46.     }    
  47.     
  48.     close(sockfd);    
  49.     return 0;    
  50. }   

程序編譯運行后結(jié)果如下: 

[[133623]] 

如果客戶端想綁定端口號,一定要調(diào)用發(fā)送信息函數(shù)之前綁定( bind )端口,因為在發(fā)送信息函數(shù)( sendto, 或 write ),系統(tǒng)會自動給當(dāng)前網(wǎng)絡(luò)程序分配一個隨機端口號,這相當(dāng)于隨機綁定了一個端口號,這里只會分配一次,以后通信就以這個隨機端口通信,我們再綁定端口號的話,就會綁定失敗。如果我們放在發(fā)送信息函數(shù)( sendto, 或 write )之前綁定,那樣程序?qū)⒁晕覀兘壎ǖ亩丝谔柊l(fā)送信息,不會再隨機分配一個端口號。

綁定失敗例子( UDP )如下:

  1. #include <stdio.h>    
  2. #include <stdlib.h>    
  3. #include <string.h>    
  4. #include <unistd.h>    
  5. #include <sys/socket.h>    
  6. #include <netinet/in.h>    
  7. #include <arpa/inet.h>    
  8.     
  9. int main(int argc, charchar *argv[])    
  10. {    
  11.     char server_ip[30] = "10.221.20.12";    
  12.         
  13.     int sockfd;    
  14.     sockfd = socket(AF_INET, SOCK_DGRAM, 0);        //創(chuàng)建UDP套接字    
  15.     if(sockfd < 0)    
  16.     {    
  17.         perror("socket");    
  18.         exit(-1);    
  19.     }    
  20.         
  21.     struct sockaddr_in dest_addr;    
  22.     bzero(&dest_addr, sizeof(dest_addr));    
  23.     dest_addr.sin_family = AF_INET;    
  24.     dest_addr.sin_port   = htons(8080); // 服務(wù)器的端口    
  25.     inet_pton(AF_INET, server_ip, &dest_addr.sin_addr);    
  26.         
  27.     char send_buf[512] = "this is for test";    
  28.     // 如果前面沒有綁定端口,sendto()系統(tǒng)會隨機分配一個端口    
  29.     sendto(sockfd, send_buf, strlen(send_buf), 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr));//發(fā)送數(shù)據(jù)    
  30.         
  31.     // 初始化本地網(wǎng)絡(luò)信息    
  32.     struct sockaddr_in my_addr;    
  33.     bzero(&my_addr, sizeof(my_addr));    
  34.     my_addr.sin_family = AF_INET;    
  35.     my_addr.sin_port   = htons(8000);    
  36.     my_addr.sin_addr.s_addr = htonl(INADDR_ANY);    
  37.         
  38.     // sendto()后面綁定端口,綁定失敗    
  39.     int err_log;    
  40.     err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));    
  41.     if(err_log != 0)    
  42.     {    
  43.         perror("bind 8000");    
  44.         close(sockfd);          
  45.         exit(-1);    
  46.     }    
  47.     
  48.     close(sockfd);    
  49.     return 0;    
  50. }   

程序編譯運行后結(jié)果如下: 

[[133624]] #p#

在上面提到:一個網(wǎng)絡(luò)應(yīng)用程序只能綁定一個端口( 一個套接字只能綁定一個端口 )。

實際上,默認的情況下,如果一個網(wǎng)絡(luò)應(yīng)用程序的一個套接字 綁定了一個端口( 占用了 8000 ),這時候,別的套接字就無法使用這個端口( 8000 ), 驗證例子如下:

  1. #include <stdio.h>    
  2. #include <stdlib.h>    
  3. #include <string.h>    
  4. #include <unistd.h>    
  5. #include <sys/socket.h>    
  6. #include <netinet/in.h>    
  7. #include <arpa/inet.h>    
  8.     
  9. int main(int argc, charchar *argv[])    
  10. {    
  11.     int sockfd_one;    
  12.     int err_log;    
  13.     sockfd_one = socket(AF_INET, SOCK_DGRAM, 0); //創(chuàng)建UDP套接字one    
  14.     if(sockfd_one < 0)    
  15.     {    
  16.     perror("sockfd_one");    
  17.     exit(-1);    
  18.     }    
  19.     
  20.     // 設(shè)置本地網(wǎng)絡(luò)信息    
  21.     struct sockaddr_in my_addr;    
  22.     bzero(&my_addr, sizeof(my_addr));    
  23.     my_addr.sin_family = AF_INET;    
  24.     my_addr.sin_port = htons(8000);     // 端口為8000    
  25.     my_addr.sin_addr.s_addr = htonl(INADDR_ANY);    
  26.     
  27.     // 綁定,端口為8000    
  28.     err_log = bind(sockfd_one, (struct sockaddr*)&my_addr, sizeof(my_addr));    
  29.     if(err_log != 0)    
  30.     {    
  31.         perror("bind sockfd_one");    
  32.         close(sockfd_one);          
  33.         exit(-1);    
  34.     }    
  35.     
  36.     int sockfd_two;    
  37.     sockfd_two = socket(AF_INET, SOCK_DGRAM, 0);  //創(chuàng)建UDP套接字two    
  38.     if(sockfd_two < 0)    
  39.     {    
  40.         perror("sockfd_two");    
  41.         exit(-1);    
  42.     }    
  43.     
  44.     // 新套接字sockfd_two,繼續(xù)綁定8000端口,綁定失敗    
  45.     // 因為8000端口已被占用,默認情況下,端口沒有釋放,無法綁定    
  46.     err_log = bind(sockfd_two, (struct sockaddr*)&my_addr, sizeof(my_addr));    
  47.     if(err_log != 0)    
  48.     {    
  49.         perror("bind sockfd_two");    
  50.         close(sockfd_two);          
  51.         exit(-1);    
  52.     }    
  53.     
  54.     close(sockfd_one);    
  55.     close(sockfd_two);    
  56.     
  57.     return 0;    
  58. }   

程序編譯運行后結(jié)果如下: 

[[133625]] 

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

設(shè)置socket的SO_REUSEADDR選項,即可實現(xiàn)端口復(fù)用:

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

SO_REUSEADDR可以用在以下四種情況下。 (摘自《Unix網(wǎng)絡(luò)編程》卷一,即UNPv1)

1、當(dāng)有一個有相同本地地址和端口的socket1處于TIME_WAIT狀態(tài)時,而你啟動的程序的socket2要占用該地址和端口,你的程序就要用到該選項。

2、SO_REUSEADDR允許同一port上啟動同一服務(wù)器的多個實例(多個進程)。但每個實例綁定的IP地址是不能相同的。在有多塊網(wǎng)卡或用IP Alias技術(shù)的機器可以測試這種情況。

3、SO_REUSEADDR允許單個進程綁定相同的端口到多個socket上,但每個socket綁定的ip地址不同。這和2很相似,區(qū)別請看UNPv1。

4、SO_REUSEADDR允許完全相同的地址和端口的重復(fù)綁定。但這只用于UDP的多播,不用于TCP。

需要注意的是,設(shè)置端口復(fù)用函數(shù)要在綁定之前調(diào)用,而且只要綁定到同一個端口的所有套接字都得設(shè)置復(fù)用:

  1. // sockfd_one, sockfd_two都要設(shè)置端口復(fù)用    
  2. // 在sockfd_one綁定bind之前,設(shè)置其端口復(fù)用    
  3. int opt = 1;    
  4. setsockopt( sockfd_one, SOL_SOCKET,SO_REUSEADDR, (const voidvoid *)&opt, sizeof(opt) );    
  5. err_log = bind(sockfd_one, (struct sockaddr*)&my_addr, sizeof(my_addr));    
  6.     
  7. // 在sockfd_two綁定bind之前,設(shè)置其端口復(fù)用    
  8. opt = 1;    
  9. setsockopt( sockfd_two, SOL_SOCKET,SO_REUSEADDR,(const voidvoid *)&opt, sizeof(opt) );    
  10. err_log = bind(sockfd_two, (struct sockaddr*)&my_addr, sizeof(my_addr));   

端口復(fù)用完整代碼如下:

  1. #include <stdio.h>    
  2. #include <stdlib.h>    
  3. #include <string.h>    
  4. #include <unistd.h>    
  5. #include <sys/socket.h>    
  6. #include <netinet/in.h>    
  7. #include <arpa/inet.h>    
  8.     
  9. int main(int argc, charchar *argv[])    
  10. {    
  11.     int sockfd_one;    
  12.     int err_log;    
  13.     sockfd_one = socket(AF_INET, SOCK_DGRAM, 0); //創(chuàng)建UDP套接字one    
  14.     if(sockfd_one < 0)    
  15.     {    
  16.     perror("sockfd_one");    
  17.     exit(-1);    
  18.     }    
  19.     
  20.     // 設(shè)置本地網(wǎng)絡(luò)信息    
  21.     struct sockaddr_in my_addr;    
  22.     bzero(&my_addr, sizeof(my_addr));    
  23.     my_addr.sin_family = AF_INET;    
  24.     my_addr.sin_port = htons(8000);     // 端口為8000    
  25.     my_addr.sin_addr.s_addr = htonl(INADDR_ANY);    
  26.         
  27.     // 在sockfd_one綁定bind之前,設(shè)置其端口復(fù)用    
  28.     int opt = 1;    
  29.     setsockopt( sockfd_one, SOL_SOCKET,SO_REUSEADDR,     
  30.                     (const voidvoid *)&opt, sizeof(opt) );    
  31.     
  32.     // 綁定,端口為8000    
  33.     err_log = bind(sockfd_one, (struct sockaddr*)&my_addr, sizeof(my_addr));    
  34.     if(err_log != 0)    
  35.     {    
  36.         perror("bind sockfd_one");    
  37.         close(sockfd_one);          
  38.         exit(-1);    
  39.     }    
  40.     
  41.     int sockfd_two;    
  42.     sockfd_two = socket(AF_INET, SOCK_DGRAM, 0);  //創(chuàng)建UDP套接字two    
  43.     if(sockfd_two < 0)    
  44.     {    
  45.         perror("sockfd_two");    
  46.         exit(-1);    
  47.     }    
  48.     
  49.     // 在sockfd_two綁定bind之前,設(shè)置其端口復(fù)用    
  50.     opt = 1;    
  51.     setsockopt( sockfd_two, SOL_SOCKET,SO_REUSEADDR,     
  52.                     (const voidvoid *)&opt, sizeof(opt) );    
  53.         
  54.     // 新套接字sockfd_two,繼續(xù)綁定8000端口,成功    
  55.     err_log = bind(sockfd_two, (struct sockaddr*)&my_addr, sizeof(my_addr));    
  56.     if(err_log != 0)    
  57.     {    
  58.         perror("bind sockfd_two");    
  59.         close(sockfd_two);          
  60.         exit(-1);    
  61.     }    
  62.     
  63.     close(sockfd_one);    
  64.     close(sockfd_two);    
  65.     
  66.     return 0;    
  67. }    

端口復(fù)用允許在一個應(yīng)用程序可以把 n 個套接字綁在一個端口上而不出錯。同時,這 n 個套接字發(fā)送信息都正常,沒有問題。但是,這些套接字并不是所有都能讀取信息,只有***一個套接字會正常接收數(shù)據(jù)。

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

  1. #include <stdio.h>    
  2. #include <stdlib.h>    
  3. #include <string.h>    
  4. #include <unistd.h>    
  5. #include <sys/socket.h>    
  6. #include <netinet/in.h>    
  7. #include <arpa/inet.h>    
  8. #include <pthread.h>    
  9.     
  10. // 線程1的回調(diào)函數(shù)    
  11. voidvoid *recv_one(voidvoid *arg)    
  12. {    
  13.     printf("===========recv_one==============\n");    
  14.     int sockfd = (int )arg;    
  15.     while(1){    
  16.         int recv_len;    
  17.         char recv_buf[512] = "";    
  18.         struct sockaddr_in client_addr;    
  19.         char cli_ip[INET_ADDRSTRLEN] = "";//INET_ADDRSTRLEN=16    
  20.         socklen_t cliaddr_len = sizeof(client_addr);    
  21.             
  22.         recv_len = recvfrom(sockfd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr*)&client_addr, &cliaddr_len);    
  23.         inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);    
  24.         printf("\nip:%s ,port:%d\n",cli_ip, ntohs(client_addr.sin_port));    
  25.         printf("sockfd_one =========== data(%d):%s\n",recv_len,recv_buf);    
  26.         
  27.     }    
  28.     
  29.     return NULL;    
  30. }    
  31.     
  32. // 線程2的回調(diào)函數(shù)    
  33. voidvoid *recv_two(voidvoid *arg)    
  34. {    
  35.     printf("+++++++++recv_two++++++++++++++\n");    
  36.     int sockfd = (int )arg;    
  37.     while(1){    
  38.         int recv_len;    
  39.         char recv_buf[512] = "";    
  40.         struct sockaddr_in client_addr;    
  41.         char cli_ip[INET_ADDRSTRLEN] = "";//INET_ADDRSTRLEN=16    
  42.         socklen_t cliaddr_len = sizeof(client_addr);    
  43.             
  44.         recv_len = recvfrom(sockfd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr*)&client_addr, &cliaddr_len);    
  45.         inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);    
  46.         printf("\nip:%s ,port:%d\n",cli_ip, ntohs(client_addr.sin_port));    
  47.         printf("sockfd_two @@@@@@@@@@@@@@@ data(%d):%s\n",recv_len,recv_buf);    
  48.         
  49.     }    
  50.     
  51.     return NULL;    
  52. }    
  53.     
  54. int main(int argc, charchar *argv[])    
  55. {    
  56.     int err_log;    
  57.         
  58.     /////////////////////////sockfd_one    
  59.     int sockfd_one;    
  60.     sockfd_one = socket(AF_INET, SOCK_DGRAM, 0); //創(chuàng)建UDP套接字one    
  61.     if(sockfd_one < 0)    
  62.     {    
  63.     perror("sockfd_one");    
  64.     exit(-1);    
  65.     }    
  66.     
  67.     // 設(shè)置本地網(wǎng)絡(luò)信息    
  68.     struct sockaddr_in my_addr;    
  69.     bzero(&my_addr, sizeof(my_addr));    
  70.     my_addr.sin_family = AF_INET;    
  71.     my_addr.sin_port = htons(8000);     // 端口為8000    
  72.     my_addr.sin_addr.s_addr = htonl(INADDR_ANY);    
  73.         
  74.     // 在sockfd_one綁定bind之前,設(shè)置其端口復(fù)用    
  75.     int opt = 1;    
  76.     setsockopt( sockfd_one, SOL_SOCKET,SO_REUSEADDR,     
  77.                     (const voidvoid *)&opt, sizeof(opt) );    
  78.     
  79.     // 綁定,端口為8000    
  80.     err_log = bind(sockfd_one, (struct sockaddr*)&my_addr, sizeof(my_addr));    
  81.     if(err_log != 0)    
  82.     {    
  83.         perror("bind sockfd_one");    
  84.         close(sockfd_one);          
  85.         exit(-1);    
  86.     }    
  87.         
  88.     //接收信息線程1    
  89.     pthread_t tid_one;    
  90.     pthread_create(&tid_one, NULL, recv_one, (voidvoid *)sockfd_one);    
  91.         
  92.     /////////////////////////sockfd_two    
  93.     int sockfd_two;    
  94.     sockfd_two = socket(AF_INET, SOCK_DGRAM, 0);  //創(chuàng)建UDP套接字two    
  95.     if(sockfd_two < 0)    
  96.     {    
  97.         perror("sockfd_two");    
  98.         exit(-1);    
  99.     }    
  100.     
  101.     // 在sockfd_two綁定bind之前,設(shè)置其端口復(fù)用    
  102.     opt = 1;    
  103.     setsockopt( sockfd_two, SOL_SOCKET,SO_REUSEADDR,     
  104.                     (const voidvoid *)&opt, sizeof(opt) );    
  105.         
  106.     // 新套接字sockfd_two,繼續(xù)綁定8000端口,成功    
  107.     err_log = bind(sockfd_two, (struct sockaddr*)&my_addr, sizeof(my_addr));    
  108.     if(err_log != 0)    
  109.     {    
  110.         perror("bind sockfd_two");    
  111.         close(sockfd_two);          
  112.         exit(-1);    
  113.     }    
  114.     //接收信息線程2    
  115.     pthread_t tid_two;    
  116.     pthread_create(&tid_two, NULL, recv_two, (voidvoid *)sockfd_two);    
  117.         
  118.         
  119.     while(1){   // 讓程序阻塞在這,不結(jié)束    
  120.         NULL;    
  121.     }    
  122.     
  123.     close(sockfd_one);    
  124.     close(sockfd_two);    
  125.     
  126.     return 0;    
  127. }   

接著,通過網(wǎng)絡(luò)調(diào)試助手給這個服務(wù)器發(fā)送數(shù)據(jù),結(jié)果顯示,只有***一個套接字sockfd_two會正常接收數(shù)據(jù): 

[[133626]] 

我們上面的用法,實際上沒有太大的意義。端口復(fù)用最常用的用途應(yīng)該是防止服務(wù)器重啟時之前綁定的端口還未釋放或者程序突然退出而系統(tǒng)沒有釋放端口。這種情況下如果設(shè)定了端口復(fù)用,則新啟動的服務(wù)器進程可以直接綁定端口。如果沒有設(shè)定端口復(fù)用,綁定會失敗,提示ADDR已經(jīng)在使用中——那只好等等再重試了,麻煩!

責(zé)任編輯:藍雨淚 來源: CSDN博客
相關(guān)推薦

2015-05-04 14:51:49

SQL子查詢

2009-09-01 17:25:33

初學(xué)C#編程

2009-11-09 11:01:01

ibmdwPMP

2011-04-14 11:28:07

光纖

2011-06-23 11:15:25

SEO網(wǎng)站優(yōu)化

2014-07-29 10:12:38

LinuxC語言編程

2010-01-21 11:30:10

2009-07-15 16:14:36

iBATIS優(yōu)缺點

2011-07-22 13:25:10

復(fù)印機租賃技巧

2014-01-13 10:50:28

虛擬化存儲

2009-09-07 08:58:23

VMWare編譯lin

2011-05-26 11:22:04

SEO

2010-01-08 10:28:22

交換網(wǎng)絡(luò)技術(shù)

2009-12-15 17:47:17

VSIP

2011-05-19 14:29:50

Oracle存儲語法

2021-02-07 07:40:31

Synchronize用法

2021-12-14 14:50:12

synchronizeJava

2009-06-14 08:40:14

Linux平臺公司

2010-10-29 16:33:45

ORACLE存儲過程

2009-12-03 14:37:47

安裝phpMyAdmi
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 北条麻妃一区二区三区在线视频 | 欧美成人高清视频 | 国产日韩一区二区三区 | 精品久久久久久久久久久 | 美女天堂在线 | 精品国产一区二区三区四区在线 | 日韩精品久久 | 大学生a级毛片免费视频 | av日韩一区| 91色视频在线观看 | 中国一级特黄视频 | 欧美成人一区二区三区片免费 | 亚洲精品99999 | 欧美视频免费在线 | 亚洲国产精品人人爽夜夜爽 | 日本精品免费 | 91久久精品国产91久久性色tv | 精品在线观看一区二区 | 欧美一级二级视频 | 在线观看成年人视频 | 黄色成人在线观看 | 欧美日韩在线免费观看 | 亚洲精品电影 | 国产探花在线精品一区二区 | 热99精品视频 | 免费成年网站 | 久久一热 | 91精品国产92 | 福利精品在线观看 | 亚洲综合色 | 一级毛片视频 | 国产精品一区网站 | 四虎影院在线观看免费视频 | 亚洲精品乱码久久久久久蜜桃91 | 8x国产精品视频一区二区 | 国产精品污www一区二区三区 | 成人免费大片黄在线播放 | 91视频导航 | 久草热视频 | 成年免费大片黄在线观看一级 | 福利视频一区二区 |