1. 程式人生 > >socket之msghdr、select、setsockopt

socket之msghdr、select、setsockopt

內容都來自網上,整理如下。

一、msghdr

msghdr其結構定義如下:
 struct msghdr {
               void         *msg_name;       /* optional address */
               socklen_t     msg_namelen;    /* size of address */

               struct iovec *msg_iov;        /* scatter/gather array */
               size_t        msg_iovlen;     /* # elements in msg_iov */

               void         *msg_control;    /* ancillary data, see below */
               size_t        msg_controllen; /* ancillary data buffer len */

               int           msg_flags;      /* flags on received message */
           }; 
結構成員可以分為四組。他們是:
套介面地址成員:msg_name與msg_namelen。
I/O向量引用:msg_iov與msg_iovlen。
附屬資料緩衝區成員:msg_control與msg_controllen。
接收資訊標記位:msg_flags。

在我們將這個結構分為上面的幾類以後,結構看起來就不那樣巨大了。

成員msg_name與msg_namelen
這些成員只有當我們的套介面是一個數據報套介面時才需要。
msg_name成員指向我們要傳送或是接收資訊的套介面地址。
msg_namelen指明瞭這個套介面地址的長度。
當呼叫recvmsg時,msg_name會指向一個將要接收的地址的接收區域。當呼叫sendmsg時,這會指向一個數據報將要傳送到的目的地址。
注意,msg_name定義為一個(void *)資料型別。我們並不需要將我們的套介面地址轉換為(struct sockaddr *)。

成員msg_iov與msg_iovlen

這些成員指定了我們的I/O向量陣列的位置以及他包含多少項。msg_iov成員指向一個struct iovec陣列。
其中struct iovec的定義
struct iovec
  {
    void *iov_base;     /* Pointer to data.  */   //使用者sendto中的ptr就是賦值到此
    size_t iov_len;     /* Length of data.  */      //使用者sendto中的長度就是賦值到此
  };
我們將會回憶起I/O向量指向我們的緩衝區。成員msg_iov指明瞭在我們的I/O向量陣列中有多少元素。

成員msg_control與msg_controllen

這些成員指向了我們附屬資料緩衝區並且表明了緩衝區大小。
msg_control指向附屬資料緩衝區。
msg_controllen指明瞭緩衝區大小。

成員msg_flags
當使用recvmsg時,這個成員用於接收特定的標記位(他並不用於sendmsg)。在這個位置可以接收的標記位如下表所示:
標記位        描述
MSG_EOR        當接收到記錄結尾時會設定這一位。這通常對於SOCK_SEQPACKET套介面型別十分有用。
MSG_TRUNC    這個標記位表明資料的結尾被截短,因為接收緩衝區太小不足以接收全部的資料。
MSG_CTRUNC    這個標記位表明某些控制資料(附屬資料)被截短,因為緩衝區太小。
MSG_OOB        這個標記位表明接收了帶外資料。
MSG_ERRQUEUE    這個標記位表明沒有接收到資料,但是返回一個擴充套件錯誤。

這個屬於sendmsg中struct msghdr的用法問題,使用者空間的函式sendmsg是直接呼叫的核心中的sock_sendmsg。
int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t size);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
這兩個函式同時支援tcp和udp。

msg.msg_iovlen = 1
是指msg.msg_iov的指標指向的地方只有一個struct iovec結構體。
sendto一次只能從一個ptr傳送長度為len的東西,故只用定義一個struct iovec iov[1];
msg_iovlen就是指struct iovec的個數。

例子:
如果你想用sendmsg傳送ptr1 , len1 ; ptr2, len2的資料。  傳統做法是將ptr1 和 ptr2的資料都拷貝到一起。
如:
char *ptr = (char*)malloc(len1 + len2);
memcpy(ptr , ptr1 , len1) ;
memcpy(ptr , ptr2 , len2)
sendto(sockfd , ptr , len1 + len2,  0 , addr , addrlen);
現在用sendmsg就不用拷貝。如下:
struct msghdr msg;
struct iovec iov[2];
iov[0].iov_base = ptr1;
iov[0].iov_len = len1;
iov[1].iov_base = ptr2;
iov[1].iov_len = len2;
memset(&msg , 0 , sizeof(msg));
msg.msg_name = &addr;    //tcp 時為NULL
msg.msg_namelen = sizeof(addr); //tcp時為0
msg.msg_iov = iov;
msg.msg_iovlen = 2 ;    //!!!!!此處為2!
sendmsg(sockfd , &msg , 0);
這樣就不省了拷貝資料的開鎖!
理解struct msghdr中msg_iovlen的意義了嗎?

二、select

int select(int maxfd + 1,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
用於確定一個或多個套介面的狀態,對每一個套介面,呼叫者可查詢它的可讀性、可寫性及錯誤狀態資訊。
引數一:最大的檔案描述符加1。
引數二:用於檢查可讀性,fd_set*readfds是指向fd_set結構的指標,這個集合中應該包括檔案描述符,我們是要監視這些檔案描述符的讀變化的,即我們關心是否可以從這些檔案中讀取資料了,如果這個集合中有一個檔案可讀,select就會返回一個大於0的值,表示有檔案可讀,如果沒有可讀的檔案,則根據timeout引數再判斷是否超時,若超出timeout的時間,select返回0,若發生錯誤返回負值。可以傳入NULL值,表示不關心任何檔案的讀變化。
如果該套介面正處於監聽linsten()狀態,則若有連線請求到達,該套介面便被標識為可讀,這樣一個accept()呼叫保證可以無阻塞完成,對其他套介面而言,可讀性意味著有排隊資料供讀取。
引數三:用於檢查可寫性,同rendset.
引數四:fd_set *errorfds同上面兩個引數的意圖,用來監視檔案錯誤異常。
引數五:一個指向timeval結構的指標,用於決定select等待I/o的最長時間。如果為空將一直等待。
第一,若將NULL以形參傳入,即不傳入時間結構,就是將select置於阻塞狀態,一定等到監視檔案描述符集合中某個檔案描述符發生變化為止;
第二,若將時間值設為0秒0毫秒,就變成一個純粹的非阻塞函式,不管檔案描述符是否有變化,都立刻返回繼續執行,檔案無變化返回0,有變化返回一個正值;
第三,timeout的值大於0,這就是等待的超時時間,即 select在timeout時間內阻塞,超時時間之內有事件到來就返回了,否則在超時後不管怎樣一定返回,返回值同上述.

如果對readfds、writefds或errorfds中任一個組類不感興趣,可將它置為空NULL

timeval結構的定義:

struct timeval
{
long tv_sec; // seconds
long tv_usec; // microseconds
}

select返回值:
負值:select錯誤
正值:某些檔案可讀寫或出錯
 0:     等待超時,沒有可讀寫或錯誤的檔案

struct fd_set可以理解為一個集合,這個集合中存放的是檔案描述符(file descriptor),即檔案控制代碼,這可以是我們所說的普通意義的檔案,當然Unix下任何裝置、管道、FIFO等都是檔案形式,全部包括在內,所以毫無疑問一個socket就是一個檔案,socket控制代碼就是一個檔案描述符。fd_set集合可以通過一些巨集由人為來操作,比如清空集合 FD_ZERO(fd_set *),將一個給定的檔案描述符加入集合之中FD_SET(int ,fd_set *),將一個給定的檔案描述符從集合中刪除FD_CLR(int ,fd_set*),檢查集合中指定的檔案描述符是否可以讀寫FD_ISSET(int ,fd_set* ),如下:
void FD_ZERO (fd_set *fdset);
// clear all bits in fdset,清空集合

 void FD_SET (int fd,fd_set *fdset);
// turn on the bit for fd in fdset,將一個給定的檔案描述符加入集合之中,即新增描述符

void FD_CLR (int fd,fd_set *fdset);
// turn off the bit for fd in fdset,將一個給定的檔案描述符從集合中刪除

int    FD_ISSET(int fd,fd_set *fdset);
// is the bit for fd on in fdset,檢查集合中指定的檔案描述符是否可以讀寫,若s為集合中一員,非零;否則為零。
伺服器的幾個主要動作如下:
     1.建立監聽套接字,繫結,監聽;
     2.建立工作者執行緒;
     3.建立一個套接字陣列,用來存放當前所有活動的客戶端套接字,每accept一個連線就更新一次陣列;
     4.接受客戶端的連線。
     這裡有一點需要注意的,就是我沒有重新定義FD_SETSIZE巨集,所以伺服器最多支援的併發連線數為64。而且,這裡決不能無條件的accept,伺服器應該根據當前的連線數來決定是否接受來自某個客戶端的連線

工作者執行緒裡面是一個死迴圈,一次迴圈完成的動作是:
     1.將當前所有的客戶端套接字加入到讀集fdread中;
     2.呼叫select函式;
     3.檢視某個套接字是否仍然處於讀集中,如果是,則接收資料。如果接收的資料長度為0,或者發生WSAECONNRESET錯誤,則表示客戶端套接字主動關閉,這時需要將伺服器中對應的套接字所繫結的資源釋放掉,然後調整

我們的套接字陣列(將陣列中最後一個套接字挪到當前的位置上)。
     除了需要有條件接受客戶端的連線外,還需要在連線數為0的情形下做特殊處理,因為如果讀集中沒有任何套接字,select函式會立刻返回,這將導致工作者執行緒成為一個毫無停頓的死迴圈,CPU的佔用率馬上達到100%。
     關係到套接字列表的操作都需要使用迴圈,在輪詢的時候,需要遍歷一次,再新的一輪開始時,將列表加入佇列又需要遍歷一次.也就是說,Select在工作一次時,需要至少遍歷2次列表,這是它效率較低的原因之一.

3、setsockopt

int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
sockfd:標識一個套介面的描述字。
level:選項定義的層次;支援SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP和IPPROTO_IPV6。
optname:需設定的選項。
optval:指標,指向存放選項待設定的新值的緩衝區。
optlen:optval緩衝區長度。

1. 如果在已經處於 ESTABLISHED狀態下的socket(一般由埠號和標誌符區分)呼叫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));

9.如果在傳送資料的過程中(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));
Note:1.在設定了逗留延時,用於一個非阻塞的socket是作用不大的,最好不用;
     2.如果想要程式不經歷SO_LINGER需要設定SO_DONTLINGER,或者設定l_onoff=0;

10.還一個用的比較少的是在SDI或者是Dialog的程式中,可以記錄socket的除錯資訊:
(前不久做過這個函式的測試,調式資訊可以儲存,包括socket建立時候的引數,採用的具體協議,以及出錯的程式碼都可以記錄下來)
BOOL bDebug=TRUE;
setsockopt(s,SOL_SOCKET,SO_DEBUG,(const char*)&bDebug,sizeof(BOOL));











相關推薦

socketmsghdrselectsetsockopt

內容都來自網上,整理如下。一、msghdrmsghdr其結構定義如下: struct msghdr { void *msg_name; /* optional address */ soc

socket--socket()bind()listen()connect()accept()recv()send()select()close()shutdown(

轉載自 http://blog.csdn.net/amaowolf/article/details/8008575  轉載自 http://www.cnblogs.com/jianqiang2010/archive/2010/08/20/1804598.html  轉

angularjs表單相關封裝radioselectfilter

在這幾個月裡,一直一邊學習angular,一邊重新把我們公司的後臺重寫。想象確實是美好的,但是時間起來真是苦難重重,不過功夫不負有心人,終於寫出一點靠譜的東西,而且應用到實踐上,就一個字,爽!!以前費半天勁想寫一個介面,還得考慮這個,還得考慮那個,真是費死勁了。現在似乎問題

利用jquery mobiscroll插件選擇日期selecttreeList的具體運用

order 移動 cancel fault ons val margin image ios mobiscroll是個很好用的用於移動端滑動選擇的插件,可以用來選擇日期,也可以用來選擇簡單選項或者一些復雜的樹形結構的選項,簡單分享下。 依賴也比較簡單,引入一個mob

iview+vue 使用中遇到的問題(表格selectradio)

數據 方式 center 遇到 自定義 情況 head time 沒有 1、iview+vue中,對表頭的動態設置:   iview表頭若是需要動態設置,可以有兩個方法,第一種: children: [ { title

Go語言程式設計基礎 併發(二)(完結)——rangecloseselectsync.Mutex

4 range和close 傳送者可以通過close關閉一個通道來表示沒有需要傳送的值了。接收者可以通過為接收表示式分配第二個引數來測試通道是否被關閉:如果沒有值可以接收且通道已被關閉,那麼執行完 v, ok := <-ch 之後,ok會被設定為false 迴圈for

iview的tabe中加入inputselect時間外掛和table的編輯刪除操作

  如圖,實現的目標table能有編輯的input、能選擇的下拉框、還有日期選擇器、最後的操作裡面可以點選編輯和刪除當前的行操作。 自己解說的,怕有些地方講的不好,放上demo的連結地址 對應的github的demo連結地址:https://github.com

inputselecttextarea相容不同瀏覽器樣式統一

在FF下出現的情況是:點選input時,輸入游標其實上跟input的height一樣高,但當開始輸入文字時,游標又變得跟文字一樣高, chrome下游標跟input的height一樣高, 而IE下游標跟文字的大小一致。 初步結論如下: IE:不管該行有沒有文字,游標高度與font-size一致。 FF:該行無

樸素SelectPoll和Epoll網路程式設計模型實現和分析——樸素模型

        做Linux網路開發,一般繞不開標題中幾種網路程式設計模型。網上已有很多寫的不錯的分析文章,它們的基本論點是差不多的。但是我覺得他們講的還不夠詳細,在一些關鍵論點上缺乏資料支援。所以我決定好好研究這幾個模型。(轉載請指明出於breaksoftware的csdn

jQuery設定radioselectcheckbox只讀屬性後,如何在後臺得到資料

1 設定表單的readonly屬性 對於radio、select、checkbox來說,readonly屬性對這三個標籤不起什麼作用。 2 設定表單的disabled屬性

python sqlite3 連線到資料庫,建立表,INSERT SELECTUPDATE DELETE等簡單操作

Python sqlite3 模組它提供了一個SQL介面相容。不需要單獨安裝這個模組,因為它預設情況下隨著Python版本在2.5.x 一起安裝。 要使用sqlite3模組,必須首先建立一個連線物件,表示資料庫中,然後可以選擇建立遊標物件,這將幫助在執行的所有

Sql型別轉換selectset賦值區別

declare @Name varchar(12),@Id int select @Name = 'smf',@Id = 1 set @Name = 'sun' set @Id = 2 print @Name print @Id ----------------------

樸素SelectPoll和Epoll網路程式設計模型實現和分析——Select模型

        和樸素模型一樣,我們首先要建立一個監聽socket,然後呼叫listen去監聽伺服器埠。不同的是,我們要對make_socket方法傳遞1,因為我們要建立一個非同步的socket。 listen_sock = make_socket(1); if (

layui,checkbokselectradio,無法渲染的問題解決

當我們複製了layui上的程式碼,並且引用了layui.css,和layui.all.js。但是相對應的效果還是無法顯示的時候。 請看你的js程式碼是否放在了最後。 js程式碼如下:

JQuery實現radioselectcheckbox禁用

<script> $(document).ready(function(){    <!-- radio的禁用 -->     var input = $("#appDI

Socket程式設計】篇六IO多路複用——selectpollepoll

在上一篇中,我簡單學習了 IO多路複用的基本概念,這裡我將初學其三種實現手段:select,poll,epoll。 I/O 多路複用是為了解決程序或執行緒阻塞到某個 I/O 系統呼叫而出現的技術,使程序或執行緒不阻塞於某個特定的 I/O 系統呼叫。 select()

Hive 編程DDLDMLUDFSelect總結

jar包 dfs 5.1 hand let 算術運算 oca 創建表 需要   Hive的基本理論與安裝可參看作者上一篇博文《Apache Hive 基本理論與安裝指南》。 一、Hive命令行   所有的hive命令都可以通過hive命令行去執行,hive命令行中仍有許

網絡編程基礎:網絡基礎網絡協議socket模塊

網絡 作用 技術 ast 內存空間 封裝 class 揮手 window 操作系統(簡稱OS)基礎: 應用軟件不能直接操作硬件,能直接操作硬件的只有操作系統;所以,應用軟件可以通過操作系統來間接操作硬件 網絡基礎之網絡協議: 網絡通訊原理:   連接兩臺計算機之間的I

shell編程if語法case語法while語句until語句for語句select語句

指令 AD lse while 循環 語句 while else 表達 select 主要介紹shell基本語句的語法 if語句語法1 單分支結構 (如果,那麽)if <條件測試> ;then 指令;fi 或者如下:if <條件測試>? then?

前端基礎BOM和DOM和三個小示例(計時器搜尋框select聯動)

一、BOM和DOM JavaScript分為 ECMAScript,DOM,BOM。 BOM(Browser Object Model)是指瀏覽器物件模型,它使 JavaScript 有能力與瀏覽器進行“對話”。 DOM (Document Object Model)是指文件物件模型,通過它,