socket基本的API使用

1.socket初探

2.socket分析

3.socket內(nèi)核源碼分析

//1.生成內(nèi)核socket簿寂;2茬贵。與文件描述符綁定
socket(AF_UNIX, SOCK_STREAM, 0);
//建立連接练链,包含三次握手
connect(sockfd, (struct sockaddr *)&address, len);
//綁定一個(gè)IP地址和端口到socket套接字上
bind(server_sockfd, (struct sockaddr *)&server_address, server_len);
//半連接隊(duì)列储狭、全連接隊(duì)列 
int listen(int sockfd, int backlog)
//返回一個(gè)new的socket文件描述符(不占用端口號(hào))
accept(server_sockfd,(struct sockaddr *)&client_address, client_len);
//斷開連接胃榕,包含四次揮手(TCP將嘗試發(fā)送已排隊(duì)等待發(fā)送到對(duì)端的任何數(shù)據(jù)俘枫,發(fā)送完畢后發(fā)生的是正常的TCP連接終止序列译断。TIME_WAIT原因)
int close(int sockfd); 
//可選擇性的斷開連接

系統(tǒng)調(diào)用的過程:

1.int socket(int domain,int type,int protocol)

作用:根據(jù)用戶定義的網(wǎng)絡(luò)類型谦炒、協(xié)議類型、和具體的協(xié)議標(biāo)號(hào)于个,生成一個(gè)套接字文件描述符供用戶使用氛魁,實(shí)現(xiàn)各種初始化工作(文件系統(tǒng)初始化暮顺、socket初始化等)

  • //[sock_create]
  • [ ] 分配socket結(jié)構(gòu):1.在socket文件系統(tǒng)中創(chuàng)建i節(jié)點(diǎn);2.創(chuàng)建socket專用inode;
  • [ ] 根據(jù)inode取得socket對(duì)象:
  • [ ] 使用協(xié)議族來初始化socket:1) 注冊(cè)AF_INET協(xié)議域 2)套接字類型(如AF_INET域下存在流套接字(SOCK_STREAM)厅篓,數(shù)據(jù)報(bào)套接字(SOCK_DGRAM),原始套接字(SOCK_RAW)捶码,在這三種類型的套接字上建立的協(xié)議分別是TCP, UDP羽氮,ICMP/IGMP);3) 使用協(xié)議域來初始化socket
  • [ ] 分配sock結(jié)構(gòu):
  • [ ] 建立socket結(jié)構(gòu)與sock結(jié)構(gòu)的關(guān)系:
  • [ ] 使用tcp協(xié)議初始化sock:
  • //[sock_map_fd]
  • [ ] socket與文件系統(tǒng)關(guān)聯(lián);

2.int bind(int sockfd,const struct sockaddr *my_addr,socklen_t addrlen)

  • [ ] bind()的Socket層實(shí)現(xiàn)
  • [ ] bind()的tcp層實(shí)現(xiàn)、端口的沖突處理
  • Q: 什么情況下會(huì)出現(xiàn)沖突呢惫恼?

同時(shí)符合以下條件才會(huì)沖突:

  1. 綁定的設(shè)備相同(不允許自動(dòng)選擇設(shè)備)

  2. 綁定的IP地址相同(不允許自動(dòng)選擇IP)

3 以下條件有一個(gè)成立:

3.1 要綁定的socket不允許重用

3.2 已綁定的socket不允許重用

3.3 已綁定的socket處于監(jiān)聽狀態(tài)   

3.4 relax參數(shù)為false

端口區(qū)間(0--65535)

我們可以指定系統(tǒng)自動(dòng)分配端口號(hào)時(shí)档押,端口的區(qū)間:

/proc/sys/net/ipv4/ip_local_port_range,默認(rèn)為:32768 61000

也可以指定要保留的端口區(qū)間:

/proc/sys/net/ipv4/ip_local_reserved_ports祈纯,默認(rèn)為空

系統(tǒng)自動(dòng)選擇端口時(shí):不優(yōu)先選擇沒被使用過的端口令宿。只要沒有沖突,直接重用端口腕窥。

  • 一個(gè)網(wǎng)絡(luò)應(yīng)用程序只能綁定一個(gè)端口( 一個(gè)套接字只能 綁定一個(gè)端口 )
  • 一般情況下服務(wù)器需要綁定端口號(hào)粒没,而客戶端可以不綁定端口號(hào),在send的時(shí)候簇爆,系統(tǒng)隨機(jī)分配一個(gè)端口號(hào)癞松。
  • 端口復(fù)用技術(shù)//設(shè)置socket的SO_REUSEADDR選項(xiàng),即可實(shí)現(xiàn)端口復(fù)用
  • SO_REUSEADDR可以用在以下四種情況下入蛆。 (摘自《Unix網(wǎng)絡(luò)編程》卷一响蓉,即UNPv1)

1、當(dāng)有一個(gè)有相同本地地址和端口的socket1處于TIME_WAIT狀態(tài)時(shí)哨毁,而你啟動(dòng)的程序的socket2要占用該地址和端口枫甲,你的程序就要用到該選項(xiàng)。

2、SO_REUSEADDR允許同一port上啟動(dòng)同一服務(wù)器的多個(gè)實(shí)例(多個(gè)進(jìn)程)想幻。但每個(gè)實(shí)例綁定的IP地址是不能相同的软能。在有多塊網(wǎng)卡或用IP Alias技術(shù)的機(jī)器可以測(cè)試這種情況。

3举畸、SO_REUSEADDR允許單個(gè)進(jìn)程綁定相同的端口到多個(gè)socket上查排,但每個(gè)socket綁定的ip地址不同。這和2很相似抄沮,區(qū)別請(qǐng)看UNPv1跋核。

4、SO_REUSEADDR允許完全相同的地址和端口的重復(fù)綁定叛买。但這只用于UDP的多播砂代,不用于TCP

  • 端口復(fù)用最常用的用途應(yīng)該是防止服務(wù)器重啟時(shí)之前綁定的端口還未釋放或者程序突然退出而系統(tǒng)沒有釋放端口
  • 當(dāng)在一個(gè)應(yīng)用或是進(jìn)程中多個(gè)socket同時(shí)綁定到相同的端口時(shí),這些套接字并不是所有都能讀取信息率挣,只有最后一個(gè)套接字會(huì)正常接收數(shù)據(jù)刻伊。
    淺析套接字中SO_REUSEPORT和SO_REUSEADDR的區(qū)別

3.int listen(int sockfd,int backlog)

backlog的定義

Now it specifies the queue length for completely established sockets waiting to be accepted,instead of the number of incomplete connection requests. The maximum length of the queuefor incomplete sockets can be set using the tcp_max_syn_backlog sysctl. When syncookiesare enabled there is no logical maximum length and this sysctl setting is ignored.If the socket is of type AF_INET, and the backlog argument is greater than the constant SOMAXCONN(128 default), it is silently truncated to SOMAXCONN.

全連接隊(duì)列的最大長(zhǎng)度:

  • backlog保存的是完成三次握手、等待accept的全連接隊(duì)列
  • 負(fù)載不高時(shí)椒功,backlog不用太大捶箱。(For complete connections)
  • 系統(tǒng)最大的、未處理的全連接數(shù)量為:min(backlog,somaxconn)动漾,net.core.somaxconn默認(rèn)為128丁屎。這個(gè)值最終存儲(chǔ)于sk->sk_max_ack_backlog

半連接隊(duì)列的最大長(zhǎng)度:

  • tcp_max_syn_backlog默認(rèn)值為256。(For incomplete connections)
  • 當(dāng)使用SYN Cookie時(shí)旱眯,這個(gè)參數(shù)變?yōu)闊o效晨川。
  • 半連接隊(duì)列的最大長(zhǎng)度為backlog、somaxconn删豺、tcp_max_syn_backlog的最小值共虑。
  1. 檢查套接口的狀態(tài)、當(dāng)前連接的狀態(tài)是否合法呀页,然后調(diào)用inet_csk_listen_start()啟動(dòng)監(jiān)聽妈拌。
  2. 啟動(dòng)監(jiān)聽時(shí),做的工作主要包括:
  1. 創(chuàng)建半連接隊(duì)列的實(shí)例赔桌,初始化全連接隊(duì)列供炎。

  2. 初始化sock的一些變量,把它的狀態(tài)設(shè)為TCP_LISTEN疾党。

  3. 檢查端口是否可用音诫,防止bind()后其它進(jìn)程修改了端口信息。

  4. 把sock鏈接進(jìn)入監(jiān)聽哈希表listening_hash中雪位。

  • listen_sock結(jié)構(gòu)用于保存SYN_RECV狀態(tài)的連接請(qǐng)求塊竭钝,所以也叫半連接隊(duì)列
  1. 銷毀連接請(qǐng)求塊中的listen_sock實(shí)例,釋放半連接隊(duì)列
  2. inet_hash()用于把sock鏈入監(jiān)聽哈希表listening_hash,或者已建立連接的哈希表ehash香罐。

4.int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen)

It extracts the first connection request on the queue of pending connections (backlog), creates a newconnected socket, and returns a new file descriptor referring to that socket.If no pending connections are present on the queue, and the socket is not marked as non-blocking,accept() blocks the caller until a connection is present. If the socket is marked non-blocking and no pending connections are present on the queue, accept() fails with the error EAGAIN.

  • 在sys_socketcall()中會(huì)調(diào)用sys_accept4():
  1. 創(chuàng)建了一個(gè)新的socket和inode卧波,以及它所對(duì)應(yīng)的fd、file庇茫。
  2. 調(diào)用Socket層操作函數(shù)inet_accept()港粱。
  3. 保存對(duì)端地址到指定的用戶空間地址
  • SOCK_STREAM套接口的Socket層操作函數(shù)集實(shí)例為inet_stream_ops,連接接收函數(shù)為inet_accept():
  1. 調(diào)用TCP層的操作函數(shù)旦签,獲取已建立的連接sock查坪。
  2. 把新socket和sock關(guān)聯(lián)起來。
  3. 把新socket的狀態(tài)設(shè)為SS_CONNECTED宁炫。
  • SOCK_STREAM套接口的TCP層操作函數(shù)集實(shí)例為tcp_prot偿曙,其中連接接收函數(shù)為inet_csk_accept().inet_csk_accept()用于從backlog隊(duì)列(全連接隊(duì)列)中取出一個(gè)ESTABLISHED狀態(tài)的連接請(qǐng)求塊,返回它所對(duì)應(yīng)的連接sock,同時(shí)更新backlog隊(duì)列的全連接數(shù)羔巢,釋放取出的連接控制塊.
  1. 非阻塞的望忆,且當(dāng)前沒有已建立的連接,則直接退出竿秆,返回-EAGAIN启摄。
  2. 阻塞的,且當(dāng)前沒有已建立的連接:
    2.1 用戶沒有設(shè)置超時(shí)時(shí)間袍辞,則無限期阻塞鞋仍。
    2.2 用戶設(shè)置了超時(shí)時(shí)間,超時(shí)后會(huì)退出搅吁。

accept()是如何避免驚群現(xiàn)象(當(dāng)內(nèi)核接收到一個(gè)客戶連接后,只會(huì)喚醒等待隊(duì)列上的第一個(gè)進(jìn)程或線程)的:

初始化等待任務(wù)時(shí)落午,flags|=WQ_FLAG_EXCLUSIVE谎懦。傳入的nr_exclusive為1,表示只允許喚醒一個(gè)等待任務(wù)溃斋。
所以這里只會(huì)喚醒一個(gè)等待的進(jìn)程界拦,不會(huì)導(dǎo)致驚群現(xiàn)象。

Nginx中使用mutex互斥鎖解決這個(gè)問題梗劫,具體措施有使用全局互斥鎖享甸,每個(gè)子進(jìn)程在epoll_wait()之前先去申請(qǐng)鎖,申請(qǐng)到則繼續(xù)處理梳侨,獲取不到則等待蛉威,并設(shè)置了一個(gè)負(fù)載均衡的算法(當(dāng)某一個(gè)子進(jìn)程的任務(wù)量達(dá)到總設(shè)置量的7/8時(shí),則不會(huì)再嘗試去申請(qǐng)鎖)來均衡各個(gè)進(jìn)程的任務(wù)量走哺。使用mutex鎖住多個(gè)線程是不會(huì)驚群的蚯嫌,在某個(gè)線程解鎖后,只會(huì)有一個(gè)線程會(huì)獲得鎖,其它的繼續(xù)等待.

參考:accept與epoll驚群

5.int connect(int sockfd,struct sockaddr *,int addrlen)

  • SOCK_STREAM套接口的socket層操作函數(shù)集實(shí)例為inet_stream_ops择示,其中主動(dòng)建立連接的函數(shù)為inet_stream_connect()束凑。
  1. 檢查socket地址長(zhǎng)度和使用的協(xié)議族。

  2. 檢查socket的狀態(tài)栅盲,必須是SS_UNCONNECTE或SS_CONNECTING汪诉。

  3. 調(diào)用tcp_v4_connect()來發(fā)送SYN包。

  4. 等待后續(xù)握手的完成:

    如果socket是非阻塞的谈秫,那么就直接返回錯(cuò)誤碼-EINPROGRESS摩瞎。

    如果socket為阻塞的,就調(diào)用inet_wait_for_connect()孝常,通過睡眠來等待旗们。在以下三種情況下會(huì)被喚醒:

    (1) 使用SO_SNDTIMEO選項(xiàng)時(shí),睡眠時(shí)間超過設(shè)定值构灸,返回0上渴。connect()返回錯(cuò)誤碼-EINPROGRESS。

    (2) 收到信號(hào)喜颁,返回剩余的等待時(shí)間稠氮。connect()返回錯(cuò)誤碼-ERESTARTSYS或-EINTR。

    (3) 三次握手成功半开,sock的狀態(tài)從TCP_SYN_SENT或TCP_SYN_RECV變?yōu)門CP_ESTABLISHED隔披,sock I/O事件的狀態(tài)變化處理函數(shù)sock_def_wakeup()就會(huì)喚醒進(jìn)程。connect()返回0寂拆。

  5. 進(jìn)程的睡眠: connect()的超時(shí)時(shí)間為sk->sk_sndtimeo奢米,在sock_init_data()中初始化為MAX_SCHEDULE_TIMEOUT,表示無限等待纠永,可以通過SO_SNDTIMEO選項(xiàng)來修改鬓长。

  6. 進(jìn)程的喚醒:三次握手中,當(dāng)客戶端收到SYNACK尝江、發(fā)出ACK后涉波,連接就成功建立了。此時(shí)連接的狀態(tài)從TCP_SYN_SENT變?yōu)門CP_ESTABLISHED炭序,sock的狀態(tài)發(fā)生變化啤覆,會(huì)調(diào)用sock_def_wakeup()來處理連接狀態(tài)變化事件,喚醒進(jìn)程惭聂,connect()就能成功返回了窗声。

close()與shutdown()

int close(int sockfd); //返回成功為0,出錯(cuò)為-1.
int shutdown(int sockfd,int howto); //返回成功為0彼妻,出錯(cuò)為-1.

1.SHUT_RD:值為0嫌佑,關(guān)閉連接的讀這一半豆茫。

2.SHUT_WR:值為1,關(guān)閉連接的寫這一半屋摇。

3.SHUT_RDWR:值為2揩魂,連接的讀和寫都關(guān)閉。

  • close函數(shù)會(huì)關(guān)閉套接字ID炮温,如果有其他的進(jìn)程共享著這個(gè)套接字火脉,那么它仍然是打開的,這個(gè)連接仍然可以用來讀和寫柒啤,并且有時(shí)候這是非常重要的倦挂,特別是對(duì)于多進(jìn)程并發(fā)服務(wù)器來說。在多進(jìn)程并發(fā)服務(wù)器中担巩,父子進(jìn)程共享著套接字方援,套接字描述符引用計(jì)數(shù)記錄著共享著的進(jìn)程個(gè)數(shù),當(dāng)父進(jìn)程或某一子進(jìn)程close掉套接字時(shí)涛癌,描述符引用計(jì)數(shù)會(huì)相應(yīng)的減一犯戏,當(dāng)引用計(jì)數(shù)仍大于零時(shí),這個(gè)close調(diào)用就不會(huì)引發(fā)TCP的四路握手?jǐn)噙B過程拳话。

  • shutdown會(huì)切斷進(jìn)程共享的套接字的所有連接先匪,不管這個(gè)套接字的引用計(jì)數(shù)是否為零,那些試圖讀得進(jìn)程將會(huì)接收到EOF標(biāo)識(shí)弃衍,那些試圖寫的進(jìn)程將會(huì)檢測(cè)到SIGPIPE信號(hào)呀非,同時(shí)可利用shutdown的第二個(gè)參數(shù)選擇斷連的方式。利用shutdown()可以避免用close()過程出現(xiàn)死鎖現(xiàn)象

//close()

/* First  Sample client fragment, 
 * 多余的代碼及變量的聲明已略       */  
   s=connect(...);  
   if( fork() ){   /*      The child, it copies its stdin to the socket              */  
       while( gets(buffer) >0)  
           write(s,buf,strlen(buffer));  
           close(s);  
           exit(0);  
   }  
   else {          /* The parent, it receives answers  */  
        while( (n=read(s,buffer,sizeof(buffer)){  
            do_something(n,buffer);  
            /* Connection break from the server is assumed  */  
            /* ATTENTION: deadlock here                     */  
         wait(0); /* Wait for the child to exit          */  
         exit(0);  
    }  

//shutdown()

if( fork() ) {  /* The child                    */  
      while( gets(buffer)  
         write(s,buffer,strlen(buffer));  
      shutdown(s,1); /* Break the connection 
                        *for writing, The server will detect EOF now. Note: reading from 
                  *the socket is still allowed. The server may send some more data 
                  *after receiving EOF, why not? */  
      exit(0);  
 }  
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末镜盯,一起剝皮案震驚了整個(gè)濱河市岸裙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌形耗,老刑警劉巖哥桥,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異激涤,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)判呕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門倦踢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人侠草,你說我怎么就攤上這事辱挥。” “怎么了边涕?”我有些...
    開封第一講書人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵晤碘,是天一觀的道長(zhǎng)褂微。 經(jīng)常有香客問我,道長(zhǎng)园爷,這世上最難降的妖魔是什么宠蚂? 我笑而不...
    開封第一講書人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮童社,結(jié)果婚禮上求厕,老公的妹妹穿的比我還像新娘。我一直安慰自己扰楼,他們只是感情好呀癣,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著弦赖,像睡著了一般项栏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蹬竖,一...
    開封第一講書人閱讀 49,821評(píng)論 1 290
  • 那天沼沈,我揣著相機(jī)與錄音,去河邊找鬼案腺。 笑死庆冕,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的劈榨。 我是一名探鬼主播访递,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼同辣!你這毒婦竟也來了拷姿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤旱函,失蹤者是張志新(化名)和其女友劉穎响巢,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體棒妨,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡踪古,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了券腔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伏穆。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖纷纫,靈堂內(nèi)的尸體忽然破棺而出枕扫,到底是詐尸還是另有隱情,我是刑警寧澤辱魁,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布烟瞧,位于F島的核電站诗鸭,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏参滴。R本人自食惡果不足惜强岸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望卵洗。 院中可真熱鬧请唱,春花似錦、人聲如沸过蹂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽酷勺。三九已至本橙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間脆诉,已是汗流浹背甚亭。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留击胜,地道東北人亏狰。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像偶摔,于是被迫代替她去往敵國(guó)和親暇唾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容

  • 1三個(gè)相關(guān)數(shù)據(jù)結(jié)構(gòu). 關(guān)于socket的創(chuàng)建辰斋,首先需要分析socket這個(gè)結(jié)構(gòu)體策州,這是整個(gè)的核心。 104 str...
    ice_camel閱讀 2,808評(píng)論 1 8
  • socket通信原理 socket又被叫做套接字,它就像連接到兩端的插座孔一樣,通過建立管道宫仗,將兩個(gè)不同的進(jìn)程之間...
    jiodg45閱讀 1,126評(píng)論 0 1
  • 大綱 一.Socket簡(jiǎn)介 二.BSD Socket編程準(zhǔn)備 1.地址 2.端口 3.網(wǎng)絡(luò)字節(jié)序 4.半相關(guān)與全相...
    VD2012閱讀 2,295評(píng)論 0 5
  • 某日 下班 天色已黑 就是不想開車 索性把車鑰匙留在口袋 走向公交車站够挂。一輛公車緩緩駛過 熟悉數(shù)字漸漸清晰 近處瞧...
    月亭閱讀 264評(píng)論 2 2
  • 我最近參加了 Big Ivy University (BIU)的招聘會(huì),和大約50位計(jì)算機(jī)專業(yè)的本科生交談過藕夫。他們...
    任家輝閱讀 1,714評(píng)論 7 45