Linux IO模型漫談(5)- IO復用模型之select


首先需要了解的是select函數:

select函數

#include<sys/select.h>

#include<sys/time.h>

int select (int maxfd , fd_set *readset ,fd_set *writeset, fd_set *exceptionset , const struct timeval * timeout);

返回:就緒描述字的正數目,0——超時,-1——出錯

 

參數解釋:

maxfd: 最大的文件描述符(其值應該為最大的文件描述符字 + 1)

readset: 內核讀操作的描述符字集合

writeset:內核寫操作的描述符字集合

exceptionset:內核異常操作的描述符字集合

timeout:等待描述符就緒需要多少時間。NULL代表永遠等下去,一個固定值代表等待固定時間,0代表根本不等待,檢查描述字之后立即返回。

 

注意:readset,writeset,exceptionset都是值-結果參數,意思就是他們傳進入指針進去,函數根據指針可以修改對應的fd_set

 

fd_set集合操作

fd_set和名字一樣,是一個描述符的集合。有下面幾個操作:

void FD_ZERO(fd_set *fdset); /* 將所有fd清零 */

void FD_SET(int fd, fd_set *fdset); /* 增加一個fd */

void FD_CLR(int fd, fd_set *fdset); /* 刪除一個fd */

int FD_ISSET(int fd, fd_set *fdset); /* 判斷一個fd是否有設置 */

我們現在要做一個select使用的server,server監聽兩個端口(7778和7779)的socket。再使用兩個cli,一個client連接到7778端口,另一個client連接到7779端口。

 

服務器端代碼:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    //這個服務器同時監聽7777和7778兩個端口
    
    //綁定監聽7779端口的fd
    int listenfd1;
    struct sockaddr_in serv_addr1;
    listenfd1 = socket(AF_INET, SOCK_STREAM, 0);
    
    bzero((char *) &serv_addr1, sizeof(serv_addr1));
    serv_addr1.sin_family = AF_INET;
    serv_addr1.sin_port = htons(7777);
    serv_addr1.sin_addr.s_addr = INADDR_ANY;
    
    bind(listenfd1, (struct sockaddr *) &serv_addr1, sizeof(serv_addr1));
    listen(listenfd1, 5);
    
    //綁定監聽7778端口的fd
    int listenfd2;
    struct sockaddr_in serv_addr2;
    listenfd2 = socket(AF_INET, SOCK_STREAM, 0);
    
    bzero((char *) &serv_addr2, sizeof(serv_addr2));
    serv_addr2.sin_family = AF_INET;
    serv_addr2.sin_port = htons(7778);
    serv_addr2.sin_addr.s_addr = INADDR_ANY;
    
    bind(listenfd2, (struct sockaddr *) &serv_addr2, sizeof(serv_addr2));
    listen(listenfd2, 5);
    
    
    int maxfd;
    //為什么這里設置兩個fd_set?每次select的時候函數會把沒有事件發生的描述字清零,所以需要兩個集合
    fd_set allset, rset;
    maxfd = listenfd1;
    if(listenfd2 > maxfd) {
        maxfd = listenfd2;
    }
    
    FD_ZERO(&allset);
    FD_SET(listenfd1, &allset);
    FD_SET(listenfd2, &allset);
    
    int clifd, clilen;
    struct sockaddr_in cli_addr;
    char buffer[256];
    for(;;) {
        rset = allset;
        select(maxfd + 1, &rset, NULL, NULL, NULL);
        
        //如果是listenfd1 獲取消息
        if(FD_ISSET(listenfd1, &rset)) {
            clilen = sizeof(cli_addr);
            clifd = accept(listenfd1, (struct sockaddr *) &cli_addr, &clilen);
            
            bzero(buffer, 256);
            read(clifd, buffer, 255);
            printf("Listenfd1 Message is:%s\r\n", buffer);
        }
        
        //如果是listenfd1 獲取消息
        if(FD_ISSET(listenfd2, &rset)) {
            clilen = sizeof(cli_addr);
            clifd = accept(listenfd2, (struct sockaddr *) &cli_addr, &clilen);
            
            bzero(buffer, 256);
            read(clifd, buffer, 255);
            printf("Listenfd2 Message is:%s\r\n", buffer);
        }
        close(clifd);
    }
    
    close(listenfd1);
    close(listenfd2);

    return 0;
}

客戶端1 代碼:

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char* argv[])
{
    int socketfd, n;
    socketfd = socket(AF_INET, SOCK_STREAM, 0);
    
    struct sockaddr_in serv_addr;
		
    bzero((char *)&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(7778);
		
    connect(socketfd,(struct sockaddr *)  &serv_addr, sizeof(serv_addr));
		
    write(socketfd, "client message", 14);
    return 0;

}

客戶端2代碼:

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char* argv[])
{
    int socketfd, n;
    socketfd = socket(AF_INET, SOCK_STREAM, 0);
    
    struct sockaddr_in serv_addr;
		
    bzero((char *)&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(7779);
		
    connect(socketfd,(struct sockaddr *)  &serv_addr, sizeof(serv_addr));
		
    write(socketfd, "client message", 14);
    return 0;

}

調用步驟:

1 啟動服務器端

2 啟動客戶端1

3 啟動客戶端2

4 服務器端表

 

客戶端啟動:

clip_image001

服務端表現:

clip_image002

這里就是使用select函數對多個socket進行讀監聽

參考文章:

unix下網絡編程之I/O復用(二)

http://www.cnblogs.com/coser/archive/2012/02/29/2373478.html

(摘)I/O多路復用詳解(一)

http://konglingchun.is-programmer.com/posts/12146.html

《Unix網絡編程》

Linux/Unix IO多路復用之select網絡編程(含源碼)

http://www.cnblogs.com/ggjucheng/archive/2012/01/17/2324859.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM