程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> socket select

socket select

編輯:關於C語言

select模型的中心思想就是利用select函數,實現對I/O的管理。利用select函數,我們可以判斷套接字上是否存在數據,或者能否向一個套接字寫入數據。利用這個函數可以防止應用程序在一次I/O的綁定調用中進入鎖定狀態。

select函數原型:
int select(
  __in     int nfds,    //忽略
  __inout  fd_set *readfds,   //檢查可讀性
  __inout  fd_set *writefds,   //檢查可寫性
  __inout  fd_set *exceptfds,   //用於例外數據
  __in     const struct timeval *timeout //超時時間,傳遞NULL會無限期等待下去,0會立刻返回
);


可讀性:
1.有數據可以讀入。
2.連接已經關閉,重設或者中止。
3.加入調用了listen,而且一個連接正在建立,那麼accept函數調用會成功。

關於連接已經關閉,重設或者中止的判斷:
當一個套接字在調用了select之後具有可讀性,那麼這個時候我們可以通過調用recv獲得數據。如果真的有數據發送過來,那麼這個調用會成功。如果是關閉,重設或者中止,那麼recv的調用會失敗,這個時候通wsagetlasterror就可以判斷連接是否已經中斷。

可寫性:
1.有數據可以發出。
2.如果已完成了對一個非鎖定連接調用處理,連接就會成功。

對於可寫性的檢查,最好放在需要寫數據的時候進行檢查。如果和可讀性放在同一個地方進行檢查,那麼select很可能每次都會因為可寫性檢查成功而返回。

例外數據:
1.加入已完成了對一個非鎖定連接調用的處理,連接嘗試就會失敗。
2.有帶外數據可供讀寫。

fd_set的幾個宏:
FD_SETSIZE    定義了fd_set所允許存放套接字的最大個數,默認是64。如果想修改這個默認值,那麼在包含winsock2.h之前重新

定義這個宏。最大不要超過1024
FD_CLR(s, *set):從set中刪除套接字s。
FD_ISSET(s, *set):檢查s是否set集合的一名成員;如答案是肯定的是,則返回TRUE。
FD_SET(s, *set):將套接字s加入集合set。
FDZERO(*set):將set初始化成空集合。

select調用流程:
1) 使用FDZERO宏,初始化自己感興趣的每一個fd_set。
2) 使用FDSET宏,將套接字句柄分配給自己感興趣的每個fd_set。
3) 調用select函數,然後等待在指定的fd_set集合中,I/O活動設置好一個或多個套接字句柄。select完成後,會返回在所有fd_set集合中設置的套接字句柄總數,它會修改每個fd_set結構,刪除那些不存在待決I/O操作的套接字句柄
4) 根據select的返回值,我們的應用程序便可判斷出哪些套接字存在著尚未完成(待決)的I/O操作—具體的方法是使用FD_ISSET宏,對每個fd_set集合進行檢查。
5) 知道了每個集合中“待決”的I/O操作之後,對I/O進行處理,然後返回步驟1 ),繼續進行select處理。

下面是利用之前封裝的socket類來實現的一個簡單的select服務器,接受用戶的連接和消息,在收到消息之後檢查可寫性,如果可寫將收到的消息發還給用戶:
 
Cpp代碼 
#include <stdlib.h> 
#include <map> 
#include "../../common/winsock/sock_accepter.h" 
#include "../../common/winsock/sock_stream.h" 
#include "../../common/winsock/sock_init.h" 
#include "../../common/log/log.h" 
 
int main(int argc, char** argv) 

    SockInit init(2, 2); 
 
    SockAccepter accepter; 
    if(!accepter.StartListen("0.0.0.0", 5000)) 
    { 
        return 0; 
    } 
 
    fd_set read_set; 
    std::map<SOCKET, SockStream> users; 
 
    while (true) 
    { 
        FD_ZERO(&read_set); 
        FD_SET(accepter.GetHandle(), &read_set); 
        std::map<SOCKET, SockStream>::iterator ite = users.begin(); 
        for (; ite != users.end(); ++ite) 
        { 
            FD_SET(ite->first, &read_set); 
        } 
 
        int res = select(0, &read_set, NULL, NULL, NULL); 
        if (res == SOCKET_ERROR) 
        { 
            Log::Instance().WriteLog(LOG_ERROR, "select failed. err = %d", WSAGetLastError()); 
            break; 
        } 
        else 
        { 
            if(FD_ISSET(accepter.GetHandle(), &read_set)) 
            { 
                SockStream stream; 
                if(accepter.Accept(stream)) 
                { 
                    users.insert(std::make_pair(stream.GetHandle(), stream)); 
                } 
            } 
 
            for (unsigned int i = 0; i < read_set.fd_count; ++i) 
            { 
                 
                std::map<SOCKET, SockStream>::iterator ite = users.find(read_set.fd_array[i]); 
                if(ite == users.end()) 
                { 
                    continue; 
                } 
 
                char buffer[1025]; 
                int length = ite->second.Read(buffer, 1024); 
                if(-1 != length) 
                { 
                    buffer[length] = 0; 
                    Log::Instance().WriteLog(LOG_INFO, "recv msg = %s", buffer); 
                    fd_set write_set; 
                    FD_ZERO(&write_set); 
                    FD_SET(ite->first, &write_set); 
                    int temp = select(0, NULL, &write_set, NULL, 0); 
                    if(SOCKET_ERROR == temp) 
                    { 
                        continue; 
                    } 
                    else 
                    { 
                        if(FD_ISSET(ite->first, &write_set)) 
                        { 
                            ite->second.Write(buffer, length); 
                        } 
                    } 
                } 
                else 
                { 
                    if (INVALID_SOCKET == ite->second.GetHandle()) 
                    { 
                        users.erase(ite); 
                    } 
                } 
            } 
        } 
    } 
 
    system("pause"); 
    return 0; 

作者“木頭城”
 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved