网络编程——进阶篇
網(wǎng)絡(luò)超時
:網(wǎng)絡(luò)通信中,好多超時會使進(jìn)程阻塞,這里共有三種處理方法
方法一:設(shè)置sock的超時屬性SO_RCVTIMEO
參考代碼:struct timeval tv;
tv.tv_sec=5
tv.tv_usec=0?
setsockopt(fd,SOL_SOCKET,SO_RECVTIMEO,&tv,sizeof(tv))//設(shè)置5s后超時
recv/recvfrom //5s后若還在阻塞,則返回,同時置errno=11
方法二:并發(fā)IO的select,設(shè)置超時屬性
參考代碼:struct timeval tv;
fd_set rdfs;
tv.tv_sec=5
tv.tv_usec=0
FD_ZERO(&rdfs);
FD_SET(sockfd,&rdfs);
if(select(sockfd+1,&rdfs,NULL,NULL,&tv)>0)
{recv/recvfrom}
方法三、SIGALRM信號捕捉(最常用)
在讀操作前(可能阻塞),啟動定時器,過一定的時刻發(fā)送SIGALRM信號,設(shè)置信號屬性,信號函數(shù)處理完后,從阻塞的下一句開始執(zhí)行
信號處理函數(shù):void handler(int signo){return }
設(shè)置信號屬性:struct sigaction act //定義sigaction 結(jié)構(gòu)體變量,臨時保存信號屬性
signaction(SIGALRM,NULL,&act)//讀取當(dāng)前屬性到act中
act.sa_handler=handler;
act.sa_flags &=~SA_RESTART;//清零,設(shè)置信號函數(shù)執(zhí)行完后,從系統(tǒng)調(diào)用(讀時阻塞)的下一句開始執(zhí)行
sigaction(SIGALRM,&act,NULL);
使用:alarm(5)
if(recv()<0)如果在這里阻塞5s,則系統(tǒng)發(fā)出SIGALRM,默認(rèn)殺死進(jìn)程,經(jīng)過設(shè)置后,可以從這條語句的后面開始執(zhí)行
思考:其實信號默認(rèn)的SA_RESTART為0,即信號處理函數(shù)執(zhí)行完后,就從下一個語句開始執(zhí)行,當(dāng)然,前提是信號沒有殺死進(jìn)程,所以,是不是可以用 signal(SIGALRM,hander)來代替?
答案:不可以,signal會修該SA_RESTART,所以,執(zhí)行完后,會繼續(xù)返回到阻塞的地方執(zhí)行
廣播 :可以向局域網(wǎng)中所有的主機(jī)發(fā)送消息(根據(jù)ip和port),數(shù)據(jù)報的特點就是以太網(wǎng)頭的目標(biāo)MAC為FFFFFFF。
發(fā)送端:建立socket,通過setsockopt設(shè)置為可以發(fā)廣播,設(shè)定sockaddr,然后就sendto發(fā)送
接收端:建立socket,綁定ip(發(fā)送端可以不用綁定ip,但是接收端需要知道自己所綁定的ip后才能接收)
若綁定的是自己主機(jī)的ip,則只能接收單播,綁定為廣播ip,則只可以接收廣播,綁定0,單播組播廣播都可以接收
組播 :向特定的組發(fā)消息,它的數(shù)據(jù)幀以太頭的目標(biāo)MAC格式:10:00:5e:*:*:*,后面三字節(jié)是組播地址的后幾位有關(guān)。
發(fā)送端:建立socket,指定組播地址,向組播發(fā)送消息。(socket默認(rèn)是可以發(fā)送組播消息的)
接收端:建立socket,綁定ip(ip選擇和上述一樣),允許數(shù)據(jù)鏈路層接收組播消息,接收消息。
允許鏈路層接收組播消息是難點:默認(rèn)的以太網(wǎng)卡只能接收兩種類型的目標(biāo)MAC地址:自身MAC(單播),FFFFFF(組播)。
而組播數(shù)據(jù)幀目標(biāo)MAC的地址為:10:00:5e:*:*:*,可以通過setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mrep,sizeof(mrep))里的mrep來指定以太網(wǎng)卡可以接收的組播。
問題:組播接受程序可以接受廣播包嗎?那反過來呢?(老師說,組播接受程序可以接收廣播包,不甚理解,甚至覺得老師說錯了)
(后來發(fā)現(xiàn)是我錯了!在教室坐的久了腦袋秀逗了。在吃飯的路上才恍然大悟)
原因:鏈路層可以接收并向上傳遞兩個類型的數(shù)據(jù)幀:目標(biāo)MAC是自身(單播)、FFFFFF(廣播)。其余的數(shù)據(jù)幀雖然接收,但是在鏈路層比較后都已經(jīng)丟棄。
在組播中,接收程序需要設(shè)定鏈路層網(wǎng)卡可以處理組播的數(shù)據(jù)幀。所以他不僅能接收廣播,還能接收組播(就是因為它比廣播多設(shè)定了接收端MAC)。
發(fā)送端:廣播的發(fā)送端
發(fā)送端:編譯:gcc -o sender sender.c
方法一:設(shè)置sock的超時屬性SO_RCVTIMEO
參考代碼:struct timeval tv;
tv.tv_sec=5
tv.tv_usec=0?
setsockopt(fd,SOL_SOCKET,SO_RECVTIMEO,&tv,sizeof(tv))//設(shè)置5s后超時
recv/recvfrom //5s后若還在阻塞,則返回,同時置errno=11
方法二:并發(fā)IO的select,設(shè)置超時屬性
參考代碼:struct timeval tv;
fd_set rdfs;
tv.tv_sec=5
tv.tv_usec=0
FD_ZERO(&rdfs);
FD_SET(sockfd,&rdfs);
if(select(sockfd+1,&rdfs,NULL,NULL,&tv)>0)
{recv/recvfrom}
方法三、SIGALRM信號捕捉(最常用)
在讀操作前(可能阻塞),啟動定時器,過一定的時刻發(fā)送SIGALRM信號,設(shè)置信號屬性,信號函數(shù)處理完后,從阻塞的下一句開始執(zhí)行
信號處理函數(shù):void handler(int signo){return }
設(shè)置信號屬性:struct sigaction act //定義sigaction 結(jié)構(gòu)體變量,臨時保存信號屬性
signaction(SIGALRM,NULL,&act)//讀取當(dāng)前屬性到act中
act.sa_handler=handler;
act.sa_flags &=~SA_RESTART;//清零,設(shè)置信號函數(shù)執(zhí)行完后,從系統(tǒng)調(diào)用(讀時阻塞)的下一句開始執(zhí)行
sigaction(SIGALRM,&act,NULL);
使用:alarm(5)
if(recv()<0)如果在這里阻塞5s,則系統(tǒng)發(fā)出SIGALRM,默認(rèn)殺死進(jìn)程,經(jīng)過設(shè)置后,可以從這條語句的后面開始執(zhí)行
思考:其實信號默認(rèn)的SA_RESTART為0,即信號處理函數(shù)執(zhí)行完后,就從下一個語句開始執(zhí)行,當(dāng)然,前提是信號沒有殺死進(jìn)程,所以,是不是可以用 signal(SIGALRM,hander)來代替?
答案:不可以,signal會修該SA_RESTART,所以,執(zhí)行完后,會繼續(xù)返回到阻塞的地方執(zhí)行
廣播 :可以向局域網(wǎng)中所有的主機(jī)發(fā)送消息(根據(jù)ip和port),數(shù)據(jù)報的特點就是以太網(wǎng)頭的目標(biāo)MAC為FFFFFFF。
發(fā)送端:建立socket,通過setsockopt設(shè)置為可以發(fā)廣播,設(shè)定sockaddr,然后就sendto發(fā)送
接收端:建立socket,綁定ip(發(fā)送端可以不用綁定ip,但是接收端需要知道自己所綁定的ip后才能接收)
若綁定的是自己主機(jī)的ip,則只能接收單播,綁定為廣播ip,則只可以接收廣播,綁定0,單播組播廣播都可以接收
組播 :向特定的組發(fā)消息,它的數(shù)據(jù)幀以太頭的目標(biāo)MAC格式:10:00:5e:*:*:*,后面三字節(jié)是組播地址的后幾位有關(guān)。
發(fā)送端:建立socket,指定組播地址,向組播發(fā)送消息。(socket默認(rèn)是可以發(fā)送組播消息的)
接收端:建立socket,綁定ip(ip選擇和上述一樣),允許數(shù)據(jù)鏈路層接收組播消息,接收消息。
允許鏈路層接收組播消息是難點:默認(rèn)的以太網(wǎng)卡只能接收兩種類型的目標(biāo)MAC地址:自身MAC(單播),FFFFFF(組播)。
而組播數(shù)據(jù)幀目標(biāo)MAC的地址為:10:00:5e:*:*:*,可以通過setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mrep,sizeof(mrep))里的mrep來指定以太網(wǎng)卡可以接收的組播。
問題:組播接受程序可以接受廣播包嗎?那反過來呢?(老師說,組播接受程序可以接收廣播包,不甚理解,甚至覺得老師說錯了)
(后來發(fā)現(xiàn)是我錯了!在教室坐的久了腦袋秀逗了。在吃飯的路上才恍然大悟)
原因:鏈路層可以接收并向上傳遞兩個類型的數(shù)據(jù)幀:目標(biāo)MAC是自身(單播)、FFFFFF(廣播)。其余的數(shù)據(jù)幀雖然接收,但是在鏈路層比較后都已經(jīng)丟棄。
在組播中,接收程序需要設(shè)定鏈路層網(wǎng)卡可以處理組播的數(shù)據(jù)幀。所以他不僅能接收廣播,還能接收組播(就是因為它比廣播多設(shè)定了接收端MAC)。
在執(zhí)行過程中,組播的接收程序不能再綁定組播地址,而是0.
實例:
接收端:組播接收端
#include<stdio.h> #include<unistd.h> #include<sys/types.h> #include<sys/socket.h> #include<arpa/inet.h> #include<string.h> #include<stdlib.h> #include<sys/select.h> #define N 64int main(int argc,char *argv[]) {int fd;struct sockaddr_in recad,peerad;char buf[N]={};int len;len=sizeof(peerad);memset(&recad,0,sizeof(recad));memset(&peerad,0,sizeof(peerad));recad.sin_family=PF_INET;recad.sin_port=htons(atoi(argv[2]));recad.sin_addr.s_addr=inet_addr(argv[1]);if((fd=socket(PF_INET,SOCK_DGRAM,0))<0){perror("socket");exit(-1);}if((bind(fd,(struct sockaddr*)&recad,sizeof(recad)))<0){printf("bind");exit(-1);}struct ip_mreq mreq;memset(&mreq,0,sizeof(mreq));mreq.imr_multiaddr.s_addr=inet_addr("224.10.10.1");mreq.imr_interface.s_addr=htons(INADDR_ANY);if(setsockopt(fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq))<0){perror("setsockopt");exit(-1);}while(1){memset(buf,0,N);recvfrom(fd,buf,N,0,(struct sockaddr*)&peerad,&len);printf("[%s %d] %s\n",inet_ntoa(peerad.sin_addr),ntohs(peerad.sin_port),buf);}}
發(fā)送端:廣播的發(fā)送端
#include<stdio.h> #include<unistd.h> #include<sys/types.h> #include<sys/socket.h> #include<arpa/inet.h> #include<string.h> #include<stdlib.h> #include<sys/select.h> #define N 64int main(int argc,char *argv[]) {int fd;struct sockaddr_in broad;char buf[N]={"hello everyone!"};int val=1;int len;len=sizeof(val);memset(&broad,0,sizeof(broad));broad.sin_family=PF_INET;broad.sin_port=htons(atoi(argv[2]));broad.sin_addr.s_addr=inet_addr(argv[1]);if((fd=socket(PF_INET,SOCK_DGRAM,0))<0){perror("socket");exit(-1);}if(setsockopt(fd,SOL_SOCKET,SO_BROADCAST,&val,len)<0){printf("getsockopt");exit(-1);}while(1){sendto(fd,buf,N,0,(struct sockaddr*)&broad,sizeof(broad));sleep(1);}}
發(fā)送端:編譯:gcc -o sender sender.c
./send 192.168.1.255 8888
接收端:編譯:gcc -o recever recever.c
./recever 0 8888
接收端執(zhí)行時ip一定為0,這樣,雖然他是一個組播接收程序,但是還可以接收廣播。
總結(jié)
- 上一篇: 常见邮箱的SMTP设置
- 下一篇: js编写弹出模态框