linux socket高性能服務器處理框架

思考一種高性能的服務器處理框架

一、首先須要一個內存池,目的在於:
·減小頻繁的分配和釋放,提升性能的同時,還能避免內存碎片的問題;
·可以存儲變長的數據,不要很傻瓜地只能預分配一個最大長度;
·基於SLAB算法實現內存池是一個好的思路:分配不一樣大小的多個塊,請求時返回大於請求長度的最小塊便可,對於容器而言,處理固定塊的分配和回收,至關 容易實現。固然,還要記得須要設計成線程安全的,自旋鎖比較好,使用讀寫自旋鎖就更好了。
·分配內容的增加管理是一個問題,好比第一次須要1KB空間,隨着數據源源不斷的寫入,第二次就須要4KB空間了。擴充空間容易實現,但是擴充的時候必然 涉及數據拷貝。甚至,擴充的需求很大,上百兆的數據,這樣就很差辦了。暫時沒更好的想法,能夠像STL同樣,指數級增加的分配策略,拷貝數據雖不可避免, 可是起碼重分配的概率愈來愈小了。
·上面提到的,若是是上百兆的數據擴展須要,採用內存映射文件來管理是一個好的辦法:映射文件後,雖然佔了很大的虛擬內存,可是物理內存僅在寫入的時候才 會被分配,加上madvice()來加上順序寫的優化建議後,物理內存的消耗也會變小。
·用string或者vector去管理內存並不明智,雖然很簡單,但服務器軟件開發中不適合使用STL,特別是對穩定性和性能要求很高的狀況下。

二、第二個須要考慮的是對象池,與內存池相似:
·減小對象的分配和釋放。其實C++對象也就是struct,把構造和析構脫離出來手動初始化和清理,保持對同一個緩衝區的循環利用,也就不難了。
·能夠設計爲一個對象池只能存放一種對象,則對象池的實現實際就是固定內存塊的池化管理,很是簡單。畢竟,對象的數量很是有限。

三、第三個須要的是隊列:
·若是能夠預料到極限的處理能力,採用固定大小的環形隊列來做爲緩衝區是比較不錯的。一個生產者一個消費者是常見的應用場景,環形隊列有其經典的「鎖無 關」算法,在一個線程讀一個線程寫的場景下,實現簡單,性能還高,還不涉及資源的分配和釋放。好啊,實在是好!
·涉及多個生產者消費者的時候,tbb::concurent_queue是不錯的選擇,線程安全,併發性也好,就是不知道資源的分配釋放是否也管理得足 夠好。

四、第四個須要的是映射表,或者說hash表:
·由於epoll是事件觸發的,而一系列的流程多是分散在多個事件中的,所以,必須保留下中間狀態,使得下一個事件觸發的時候,可以接着上次處理的位置 繼續處理。要簡單的話,STL的hash_map還行,不過得本身處理鎖的問題,多線程環境下使用起來很麻煩。
·多線程環境下的hash表,最好的仍是tbb::concurent_hash_map。

五、核心的線程是事件線程:
·事件線程是調用epoll_wait()等待事件的線程。例子代碼裏面,一個線程幹了全部的事情,而須要開發一個高性能的服務器的時候,事件線程應該專 注於事件自己的處理,將觸發事件的socket句柄放到對應的處理隊列中去,由具體的處理線程負責具體的工做。

六、accept()單獨一個線程:
·服務端的socket句柄(就是調用bind()和listen()的這個)最好在單獨的一個線程裏面作accept(),阻塞仍是非阻塞都無所謂,相 比整個服務器的通信,用戶接入的動做只是很小一部分。並且,accept()不放在事件線程的循環裏面,減小了判斷。

七、接收線程單獨一個:
·接收線程從發生EPOLLIN事件的隊列中取出socket句柄,而後在這個句柄上調用recv接收數據,直到緩衝區沒有數據爲止。接收到的數據寫入以 socket爲鍵的hash表中,hash表中有一個自增加的緩衝區,保存了客戶端發過來的數據。
·這樣的處理方式適合於客戶端發來的數據很小的應用,好比HTTP服務器之類;假設是文件上傳的服務器,則接受線程會一直處理某個鏈接的海量數據,其餘客 戶端的數據處理產生了飢餓。因此,若是是文件上傳服務器一類的場景,就不能這樣設計。

八、發送線程單獨一個:
·發送線程從發送隊列獲取須要發送數據的SOCKET句柄,在這些句柄上調用send()將數據發到客戶端。隊列中指保存了SOCKET句柄,具體的信息 還須要經過socket句柄在hash表中查找,定位到具體的對象。如同上面所講,客戶端信息的對象不但有一個變長的接收數據緩衝區,還有一個變長的發送 數據緩衝區。具體的工做線程發送數據的時候並不直接調用send()函數,而是將數據寫到發送數據緩衝區,而後把SOCKET句柄放到發送線程隊列。
·SOCKET句柄放到發送線程隊列的另外一種狀況是:事件線程中發生了EPOLLOUT事件,說明TCP的發送緩衝區又有了可用的空間,這個時候能夠把 SOCKET句柄放到發送線程隊列,一邊觸發send()的調用;
·須要注意的是:發送線程發送大量數據的時候,當頻繁調用send()直到TCP的發送緩衝區滿後,便沒法再發送了。這個時候若是循環等待,則其餘用戶的 發送工做受到影響;若是不繼續發送,則EPOLL的ET模式可能不會再產生事件。解決這個問題的辦法是在發送線程內再創建隊列,或者在用戶信息對象上設置 標誌,等到線程空閒的時候,再去繼續發送這些未發送完成的數據。

九、須要一個定時器線程:
·一位將epoll使用的高手說道:「單純靠epoll來管理描述符不泄露幾乎是不可能的。徹底解決方案很簡單,就是對每一個fd設置超時時間,若是超過 timeout的時間,這個fd沒有活躍過,就close掉」。
·因此,定時器線程按期輪訓整個hash表,檢查socket是否在規定的時間內未活動。未活動的SOCKET認爲是超時,而後服務器主動關閉句柄,回收 資源。

十、多個工做線程:
·工做線程由接收線程去觸發:每次接收線程收到數據後,將有數據的SOCKET句柄放入一個工做隊列中;工做線程再從工做隊列獲取SOCKET句柄,查詢 hash表,定位到用戶信息對象,處理業務邏輯。
·工做線程若是須要發送數據,先把數據寫入用戶信息對象的發送緩衝區,而後把SOCKET句柄放到發送線程隊列中去。
·對於任務隊列,接收線程是生產者,多個工做線程是消費者;對於發送線程隊列,多個工做線程是生產者,發送線程是消費者。在這裏須要注意鎖的問題,若是採 用tbb::concurrent_queue,會輕鬆不少。

十一、僅僅只用scoket句柄做爲hash表的鍵,並不夠:
·假設這樣一種狀況:事件線程剛把某SOCKET因發生EPOLLIN事件放入了接收隊列,但是隨即客戶端異常斷開了,事件線程又由於EPOLLERR事 件刪除了hash表中的這一項。假設接收隊列很長,發生異常的SOCKET還在隊列中,等到接收線程處理到這個SOCKET的時候,並不能經過 SOCKET句柄索引到hash表中的對象。
·索引不到的狀況也好處理,難點就在於,這個SOCKET句柄當即被另外一個客戶端使用了,接入線程爲這個SCOKET創建了hash表中的某個對象。此 時,句柄相同的兩個SOCKET,其實已是不一樣的兩個客戶端了。極端狀況下,這種狀況是可能發生的。
·解決的辦法是,使用socket fd + sequence爲hash表的鍵,sequence由接入線程在每次accept()後將一個整型值累加而獲得。這樣,就算SOCKET句柄被重用,也 不會發生問題了。

十二、監控,須要考慮:
·框架中最容易出問題的是工做線程:工做線程的處理速度太慢,就會使得各個隊列暴漲,最終致使服務器崩潰。所以必需要限制每一個隊列容許的最大大小,且須要 監視每一個工做線程的處理時間,超過這個時間就應該採用某個辦法結束掉工做線程。

對於linux socket與epoll配合相關的一些心得記錄 2008-07-29 17:57

setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));
一、經過上面語句能夠簡單設置緩衝區大小,測試證實:跟epoll結合的時候只有當 單次發送的數據全被從緩衝區讀完畢以後纔會再次被觸發,屢次發送數據若是沒有 讀取完畢當緩衝區未滿的時候數據不會丟失,會累加到後面。
二、 若是緩衝區未滿,同一鏈接屢次發送數據會屢次收到EPOLLIN事件。 單次發送數據>socket緩衝區大小的數據數據會被阻塞分次發送,因此循環接收可 以用ENLIGE錯誤判斷。
三、若是緩衝區滿,新發送的數據不會觸發epoll事件(也無異常),每次recv 都會爲緩衝區騰出空間,只有當緩衝區空閒大小可以再次接收數據epollIN事件可 以再次被觸發 接收時接收大小爲0表示客戶端斷開(不可能有0數據包觸發EPOLLIN),-1表示異 常,針對errorno進行判斷能夠肯定是合理異常仍是須要終止的異常,>0而不等於 緩衝區大小表示單次發送結束。
四、 若是中途臨時調整接收緩存區大小,而且在上一次中數據沒有徹底接收到 用戶空間,數據不會丟失,會累加在一塊兒 因此總結起來,系統對於數據的完整性仍是作了至關的保正,至於穩定性沒有做更 深一步的測試

新增長:
五、若是主accept監聽的soctet fd也設置爲非阻塞,那麼單純靠epoll事件來驅 動的服務器模型會存在問題,併發壓力下發現,每次accept只從系統中取得第一 個,因此若是恰馮多個 鏈接同時觸發server fd的EPOLLIN事件,在返回的event數 組中體現不出來,會出現丟失事件的現象,因此當用ab等工具簡單的壓載就會發現 每次都會有最後幾條信息得 不處處理,緣由就在於此,我如今的解決辦法是將 server fd的監聽去掉,用一個線程阻塞監聽,accept成功就處理檢測client fd, 而後在主線程循環監聽client事件,這樣epoll在邊緣模式下出錯的機率就小,測 試代表效果明顯
六、對於SIG部分信號仍是要作屏蔽處理,否則對方socket中斷等正常事件都會引發 整個服務的退出
七、sendfile(fd, f->SL->sendBuffer.inFd, (off_t *)&f->SL->sendBuffer.offset, size_need);注意sendfile函數的地三個變量是傳 送地址,偏移量會自動增長,不須要手動再次增長,不然就會出現文件傳送丟失現象
八、單線程epoll驅動模型誤解:之前我一直認爲單線程是沒法處理web服務器這樣 的有嚴重網絡延遲的服務,但nginx等優秀服務器都是機遇事件驅動 模型,開始我 在些的時候也是擔憂這些問題,後來測試發現,當client socket設爲非阻塞模式 的時候,從讀取數據到解析http協議,到發送數據均在epoll的驅動下速度很是 快,沒有必要採用多線程,個人單核 cpu(奔三)就能夠達到 10000page/second,這在公網上是遠遠沒法達到的一個數字(網絡延遲更爲嚴 重),因此單線程的數據處理能力已經很 高了,就不須要多線程了,所不一樣的是 你在架構服務器的時候須要將全部阻塞的部分拆分開來,當epoll通知你能夠讀取 的時候,實際上部分數據已經到了 socket緩衝區,你所讀取用的事件是將數據從 內核空間拷貝到用戶空間,同理,寫也是同樣的,因此epoll重要的地方就是將這 兩個延時的部分作了相似 的異步處理,若是不須要處理更爲複雜的業務,那單線 程足以知足1000M網卡的最高要求,這纔是單線程的意義。 我之前構建的web服務器就沒有理解epoll,採用epoll的邊緣觸發之程處後怕事件 丟失,或者單線理阻塞,因此本身用多線程構建了一個任務調度器, 全部收 到的事件通通壓進任無調度器中,而後多任務處理,我還將read和write分別用兩 個調度器處理,並打算若是中間須要特殊的耗時的處理就增長一套 調度器,用少許線程+epoll的方法來題高性能,後來發現read和write部分調度器是多餘 的,epoll原本就是一個事件調度器,在後面再次緩存 事件分部處理還不如將 epoll設爲水平模式,因此畫蛇添足,可是這個調度起仍是有用處的 上面講到若是中間有耗時的工做,好比數據庫讀寫,外部資源請求(文 件,socket)等這些操做就不能阻塞在主線程裏面,因此我設計的這個任務調度器 就有 用了,在epoll能處理的事件驅動部分就借用epoll的,中間部分採用模塊化 的設計,用函數指針達到面相對象語言中的「委託」的做用,就能夠知足不一樣 的需 要將任務(fd標識)加入調度器,讓多線程循環執行,若是中間再次遇到阻塞就會 再次加入自定義的阻塞器,檢測完成就加入再次存入調度器,這樣就能夠將 多種 複雜的任務劃分開來,至關於在處理的中間環節在本身購置一個相似於epoll的事 件驅動器
九、多系統兼容:我如今卻是以爲與其構建一個多操做系統都支持的服務器不 如構建特定系統的,若是想遷移再次改動,由於一旦兼顧到多個系統的化會大大增 加系 統的複雜度,而且不能最優性能,每一個系統都有本身的獨有的優化選項,所 以我以爲遷移的工做量遠遠小於兼顧的工做量
10模塊化編程,雖然用c仍是要講求一些模塊化的設計的,我如今才發現幾乎面相 對想的語言所能實現的全部高級特性在c裏面幾乎都有對應的解決辦法(暫時發現 除了操做符重載),全部學太高級面相對象的語言的朋友不放把模式用c來實現, 也是一種樂趣,便於維護和本身閱讀
十一、養成註釋的好習慣


setsockopt -設置socket

1.closesocket(通常不會當即關閉而經歷TIME_WAIT的過程)後想繼續重用該socket:
BOOL bReuseaddr=TRUE;
setsockopt(s,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(BOOL));linux


2. 若是要已經處於鏈接狀態的soket在調用closesocket後強制關閉,不經歷
TIME_WAIT的過程:
BOOL bDontLinger = FALSE;
setsockopt(s,SOL_SOCKET,SO_DONTLINGER,(const char*)&bDontLinger,sizeof(BOOL));nginx


3.在send(),recv()過程當中有時因爲網絡情況等緣由,發收不能預期進行,而設置收發時限:
int nNetTimeout=1000;//1秒
//發送時限
setsockopt(socket,SOL_S0CKET,SO_SNDTIMEO,(char *)&nNetTimeout,sizeof(int));
//接收時限
setsockopt(socket,SOL_S0CKET,SO_RCVTIMEO,(char *)&nNetTimeout,sizeof(int));web


4.在send()的時候,返回的是實際發送出去的字節(同步)或發送到socket緩衝區的字節
(異步);系統默認的狀態發送和接收一次爲8688字節(約爲8.5K);在實際的過程當中發送數據
和接收數據量比較大,能夠設置socket緩衝區,而避免了send(),recv()不斷的循環收發:
// 接收緩衝區
int nRecvBuf=32*1024;//設置爲32K
setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));
//發送緩衝區
int nSendBuf=32*1024;//設置爲32K
setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));算法


5. 若是在發送數據的時,但願不經歷由系統緩衝區到socket緩衝區的拷貝而影響
程序的性能:
int nZero=0;
setsockopt(socket,SOL_S0CKET,SO_SNDBUF,(char *)&nZero,sizeof(nZero));數據庫


6.同上在recv()完成上述功能(默認狀況是將socket緩衝區的內容拷貝到系統緩衝區):
int nZero=0;
setsockopt(socket,SOL_S0CKET,SO_RCVBUF,(char *)&nZero,sizeof(int));編程


7.通常在發送UDP數據報的時候,但願該socket發送的數據具備廣播特性:
BOOL bBroadcast=TRUE;
setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char*)&bBroadcast,sizeof(BOOL));緩存


8.在client鏈接服務器過程當中,若是處於非阻塞模式下的socket在connect()的過程當中可
以設置connect()延時,直到accpet()被呼叫(本函數設置只有在非阻塞的過程當中有顯著的
做用,在阻塞的函數調用中做用不大)
BOOL bConditionalAccept=TRUE;
setsockopt(s,SOL_SOCKET,SO_CONDITIONAL_ACCEPT,(const char*)&bConditionalAccept,sizeof(BOOL));安全


若是在發送數據的過程當中(send()沒有完成,還有數據沒發送)而調用了closesocket(),之前咱們
通常採起的措施是"從容關閉"shutdown(s,SD_BOTH),可是數據是確定丟失了,如何設置讓程序知足具體
應用的要求(即讓沒發完的數據發送出去後在關閉socket)?
struct linger {
u_short l_onoff;
u_short l_linger;
};
linger m_sLinger;
m_sLinger.l_onoff=1;//(在closesocket()調用,可是還有數據沒發送完畢的時候允許逗留)
// 若是m_sLinger.l_onoff=0;則功能和2.)做用相同;
m_sLinger.l_linger=5;//(允許逗留的時間爲5秒)
setsockopt(s,SOL_SOCKET,SO_LINGER,(const char*)&m_sLinger,sizeof(linger));服務器

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////網絡

線程於進程的好處在於:
方便通訊,線程共享了代碼與數據空間,因此對共享空間提供了最原始的支持
能夠用線程運行完銷燬的方式而不須要回收線程資源,只要進程退出,全部線程就銷燬了,不須要擔憂有殭屍進程的出現,也就是資源不能回收的問題。
對於併發比較高的服務器,而且每一個處理時間又不是太長的狀況下,能夠採用線程池的方式
在同等狀況下,線程所佔資源略少於進程,由於線程在訪問一共享變量時,在物理內存中僅有一份此變量所佔空間,如果進程間須要改寫同一全局變量時,此時就會產生「寫時複製」,會產生兩份空間(對一個變量的改寫,會形成多佔用大於等於4K的物理空間)

進程於線程的好處在於:
不須要擔憂太多由於訪問共享資源而形成的各類同步與互斥問題,若是須要共享某部份內容,須要走專用的進程間通訊手段,也就是說對於共享空間是可控制的,不會出現隨機性
不用擔憂一不當心就形成函數重入的問題
在不一樣的進程中,可使用不一樣的ELF文件做爲執行體,固然,在線程中也能夠再進行fork+execv來實現這種方案

不管是線程仍是進程,其調度方式是同樣的

長鏈接,用poll/select/epoll作多路複用的方式優缺點:
因爲不會形成多線程與多進程,因此全部代碼都在一個執行體內,都在同一調度單元中,節省了資源的開銷,如內存的佔用,進程切換的開銷。
因爲全部處理都在同一個調度單元內,也就是多個鏈接共用一個進程的時間片,若是系統中還有不少其它優先級較高進程或者實時進程,平均下來每一個鏈接所佔用的 CPU時間就較少,且若是一個鏈接處於死循環中,若不加其它控制,其它鏈接就永遠得不到響應,也就是說每一個鏈接的響應實時性會受到其它鏈接的影響。
若是對於每一個鏈接的處理方式不一樣,會形成代碼的很差控制,由於會有太多的邏輯判斷。

以上三種方式,各有優缺點,主要是樓主的需求,這三種方式並不是是互斥的,能夠交叉使用,靈活控制,從而最優化你的軟件。固然,若是併發鏈接達到2000個 以上,併發處理也達到幾百上千個以上(且每一個處理過程會執行很長),那麼推薦你採用分佈式處理,單個PC機是沒法承受這種負荷的(若使用專用服務器,性能 會好一些,這個相對限制會寬一些),至少會形成響應時間過長

、、、、、、、、、、、、、、、、、、、、、、、、、、、

設置套接口的選項。    #include <winsock.h>    int PASCAL FAR setsockopt( SOCKET s, int level, int optname,    const char FAR* optval, int optlen);    s:標識一個套接口的描述字。    level:選項定義的層次;目前僅支持SOL_SOCKET和IPPROTO_TCP層次。    optname:需設置的選項。    optval:指針,指向存放選項值的緩衝區。    optlen:optval緩衝區的長度。 註釋: setsockopt()函數用於任意類型、任意狀態套接口的設置選項值。儘管在不一樣協議層上存在選項,但本函數僅定義了最高的「套接口」層次上的選項。選項影響套接口的操做,諸如加急數據是否在普通數據流中接收,廣播數據是否能夠從套接口發送等等。    有兩種套接口的選項:一種是布爾型選項,容許或禁止一種特性;另外一種是整形或結構選項。容許一個布爾型選項,則將optval指向非零整形數;禁止一個選 項optval指向一個等於零的整形數。對於布爾型選項,optlen應等於sizeof(int);對其餘選項,optval指向包含所需選項的整形數 或結構,而optlen則爲整形數或結構的長度。SO_LINGER選項用於控制下述狀況的行動:套接口上有排隊的待發送數據,且 closesocket()調用已執行。參見closesocket()函數中關於SO_LINGER選項對closesocket()語義的影響。應用 程序經過建立一個linger結構來設置相應的操做特性:    struct linger { int l_onoff; int l_linger;    };    爲了容許SO_LINGER,應用程序應將l_onoff設爲非零,將l_linger設爲零或須要的超時值(以秒爲單位),而後調用setsockopt()。爲了容許SO_DONTLINGER(亦即禁止SO_LINGER),l_onoff應設爲零,而後調用setsockopt()。    缺省條件下,一個套接口不能與一個已在使用中的本地地址捆綁(參見bind())。但有時會須要「重用」地址。由於每個鏈接都由本地地址和遠端地址的組 合惟一肯定,因此只要遠端地址不一樣,兩個套接口與一個地址捆綁並沒有大礙。爲了通知WINDOWS套接口實現不要由於一個地址已被一個套接口使用就不讓它與 另外一個套接口捆綁,應用程序可在bind()調用前先設置SO_REUSEADDR選項。請注意僅在bind()調用時該選項才被解釋;故此無需(但也無 害)將一個不會共用地址的套接口設置該選項,或者在bind()對這個或其餘套接口無影響狀況下設置或清除這一選項。    一個應用程序能夠經過打開SO_KEEPALIVE選項,使得WINDOWS套接口實如今TCP鏈接狀況下容許使用「保持活動」包。一個WINDOWS套 接口實現並非必需支持「保持活動」,可是若是支持的話,具體的語義將與實現有關,應遵照RFC1122「Internet主機要求-通信層」中第 4.2.3.6節的規範。若是有關鏈接因爲「保持活動」而失效,則進行中的任何對該套接口的調用都將以WSAENETRESET錯誤返回,後續的任何調用 將以WSAENOTCONN錯誤返回。    TCP_NODELAY選項禁止Nagle算法。Nagle算法經過將未確認的數據存入緩衝區直到蓄足一個包一塊兒發送的方法,來減小主機發送的零碎小數據 包的數目。但對於某些應用來講,這種算法將下降系統性能。因此TCP_NODELAY可用來將此算法關閉。應用程序編寫者只有在確切瞭解它的效果並確實需 要的狀況下,才設置TCP_NODELAY選項,由於設置後對網絡性能有明顯的負面影響。TCP_NODELAY是惟一使用IPPROTO_TCP層的選 項,其餘全部選項都使用SOL_SOCKET層。    若是設置了SO_DEBUG選項,WINDOWS套接口供應商被鼓勵(但不是必需)提供輸出相應的調試信息。但產生調試信息的機制以及調試信息的形式已超出本規範的討論範圍。 setsockopt()支持下列選項。其中「類型」代表optval所指數據的類型。 選項        類型   意義 SO_BROADCAST BOOL 容許套接口傳送廣播信息。 SO_DEBUG BOOL 記錄調試信息。 SO_DONTLINER BOOL 不要由於數據未發送就阻塞關閉操做。設置本選項至關於將SO_LINGER的l_onoff元素置爲零。 SO_DONTROUTE BOOL 禁止選徑;直接傳送。 SO_KEEPALIVE BOOL 發送「保持活動」包。 SO_LINGER struct linger FAR*   如關閉時有未發送數據,則逗留。 SO_OOBINLINE BOOL 在常規數據流中接收帶外數據。 SO_RCVBUF int 爲接收肯定緩衝區大小。 SO_REUSEADDR BOOL 容許套接口和一個已在使用中的地址捆綁(參見bind())。 SO_SNDBUF int 指定發送緩衝區大小。 TCP_NODELAY BOOL 禁止發送合併的Nagle算法。 setsockopt()不支持的BSD選項有: 選項名    類型 意義 SO_ACCEPTCONN BOOL 套接口在監聽。 SO_ERROR int 獲取錯誤狀態並清除。 SO_RCVLOWAT int 接收低級水印。 SO_RCVTIMEO int 接收超時。 SO_SNDLOWAT int 發送低級水印。 SO_SNDTIMEO int 發送超時。 SO_TYPE     int 套接口類型。 IP_OPTIONS    在IP頭中設置選項。 返回值:    若無錯誤發生,setsockopt()返回0。不然的話,返回SOCKET_ERROR錯誤,應用程序可經過WSAGetLastError()獲取相應錯誤代碼。 錯誤代碼:    WSANOTINITIALISED:在使用此API以前應首先成功地調用WSAStartup()。    WSAENETDOWN:WINDOWS套接口實現檢測到網絡子系統失效。    WSAEFAULT:optval不是進程地址空間中的一個有效部分。    WSAEINPROGRESS:一個阻塞的WINDOWS套接口調用正在運行中。    WSAEINVAL:level值非法,或optval中的信息非法。    WSAENETRESET:當SO_KEEPALIVE設置後鏈接超時。    WSAENOPROTOOPT:未知或不支持選項。其中,SOCK_STREAM類型的套接口不支持SO_BROADCAST選項,SOCK_DGRAM 類型的套接口不支持SO_DONTLINGER 、SO_KEEPALIVE、SO_LINGER和SO_OOBINLINE選項。    WSAENOTCONN:當設置SO_KEEPALIVE後鏈接被複位。    WSAENOTSOCK:描述字不是一個套接口。 參見:    bind(), getsockopt(), ioctlsocket(), socket(), WSAAsyncSelect().

相關文章
相關標籤/搜索