近日學習用C++實作Socket Server與Client 端的程式,線上有許多Code都蠻完整的,但多數並非Non Blocking Socket的實作
此篇僅記錄下學習C++編程的起手式:實作Server端Non Blocking Socket監聽接收UDP
一般使用Socket監聽接收數據都是用無窮迴圈來接收數據,TCP會用accept() 或recv()來接受連線與接收數據,UDP則是recvfrom() ,呼叫這些接口是會有Blocking的現象(亦即程式會停在該行,直至真的收到數據才會繼續往下一行執行)
當然我們多數情況是不希望程式Blocking起來,所以在C++則會使用select()來達到Non Blocking的效果
※select() 可同時監視多個 Socket 的權力,它會告訴你哪些 Socket 已經有資料可以讀取
接著是紀錄與分享程式碼的部分(如有錯誤再請各前輩多指導一下)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>//for sockaddr_in
#include <arpa/inet.h>//for socket
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/select.h>
using namespace std;
int main()
{
int sockfd=socket(PF_INET,SOCK_DGRAM,0);
/*
1.[domain]
PF_INET :IPv4 協定
PF_INET6:IPv6 協定
PF_UNIX 表示本地套接字(使用一個文件)
2. [type]
SOCK_STREAM:使用 TCP
SOCK_DGRAM: 使用 UDP
3.[protocol]指定實際使用的傳輸協定。
在<netinet/in.h>中有詳細說明。
如果0的話,即根據選定的domain和type選擇使用默認協定。
*/
if(sockfd==-1){
perror("Socket Create Error!\n");
exit(-1);
}
printf("Socket fd=%d\n",sockfd);
//設定位址資訊的資料
struct sockaddr_in addr;
/*
struct sockaddr_in:IP4 格式使用
struct sockaddr_in6:IP6 格式使用
struct sockaddr:通用格式
struct sockaddr_un:UNIX domain 格式
*/
addr.sin_family=AF_INET;
addr.sin_port=htons(20000);//設定 port
addr.sin_addr.s_addr=INADDR_ANY;//inet_addr("127.0.0.1");//設定 IP
//addr.sin_addr.s_addr = INADDR_ANY; 若設定INADDR_ANY 表示任何IP
int result;
result=bind(sockfd,(struct sockaddr*)&addr,sizeof(addr));
/*
int sockfd:socket() 函式回傳的 socket descriptor
struct sockaddr *addr:用來通訊的位址資料(IP、PORT)
int addrlen:位址長度 ,sizeof(addr)
發生錯誤回傳-1,否則為0
*/
if(result==-1){
printf("Bind error!\n");
close(sockfd);
exit(-1);
}
printf("Bind successfully.\n");
char buf[255];
struct sockaddr_in from;
socklen_t len;
len=sizeof(from);
int selectReturn;
fd_set rfd;
timeval timeout= {3,0};
/*
timeout.tv_sec = 3 單位:秒;
timeout.tv_usec = 0 單位:豪秒;
*/
while(true){
FD_ZERO(&rfd);
FD_SET(sockfd, &rfd);
selectReturn = select(sockfd+1, &rfd, NULL, NULL, &timeout);
if(selectReturn == -1){
printf("select()/n");
close(sockfd);
exit(-1);
}else{
if(FD_ISSET(sockfd, &rfd)) {
result = recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&from,&len);
if(result>0){
buf[result]=0;
printf("The message received from %s Data is :%s\n",inet_ntoa(from.sin_addr),buf);
//inet_ntoa():將二進位的數字轉換為IP
}else{
break;
}
}
}
}
close(sockfd);
return 0;
}
執行結果
參考資料:
http://beej-zhtw.netdpi.net/09-man-manual/9-18-recv-recvfrom
http://beej-zhtw.netdpi.net/07-advanced-technology/7-2-select
http://pubs.opengroup.org/onlinepubs/7908799/xns/netinetin.h.html
egan2608@gmail.com