[C++]Non Blocking Socket (Server端)

近日學習用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