linux socket服務器框架

轉自http://blog.csdn.net/challenge_c_plusplus/article/details/40784179

 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

新增加:
5、如果主accept監聽的soctet fd也設置爲非阻塞,那麼單純靠epoll事件來驅 動的服務器模型會存在問題,併發壓力下發現,每次accept只從系統中取得第一 個,所以如果恰馮多個 連接同時觸發server fd的EPOLLIN事件,在返回的event數 組中體現不出來,會出現丟失事件的現象,所以當用ab等工具簡單的壓載就會發現 每次都會有最後幾條信息得 不到處理,原因就在於此,我現在的解決辦法是將 server fd的監聽去掉,用一個線程阻塞監聽,accept成功就處理檢測client fd, 然後在主線程循環監聽client事件,這樣epoll在邊緣模式下出錯的概率就小,測 試表明效果明顯
6、對於SIG部分信號還是要做屏蔽處理,不然對方socket中斷等正常事件都會引起 整個服務的退出
7、sendfile(fd, f->SL->sendBuffer.inFd, (off_t *)&f->SL->sendBuffer.offset, size_need);注意sendfile函數的地三個變量是傳 送地址,偏移量會自動增加,不需要手動再次增加,否則就會出現文件傳送丟失現象
8、單線程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的事 件驅動器
9、多系統兼容:我現在倒是覺得與其構建一個多操作系統都支持的服務器不 如構建特定系統的,如果想遷移再次改動,因爲一旦兼顧到多個系統的化會大大增 加系 統的複雜度,並且不能最優性能,每個系統都有自己的獨有的優化選項,所 以我覺得遷移的工作量遠遠小於兼顧的工作量
10模塊化編程,雖然用c還是要講求一些模塊化的設計的,我現在才發現幾乎面相 對想的語言所能實現的所有高級特性在c裏面幾乎都有對應的解決辦法(暫時發現 除了操作符重載),所有學過高級面相對象的語言的朋友不放把模式用c來實現, 也是一種樂趣,便於維護和自己閱讀
11、養成註釋的好習慣


setsockopt -設置socket

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


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


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));


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().

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章