1,http://blog.csdn.net/yuzhiyuxia/article/details/7857508css
心跳包就是在客戶端和服務器間定時通知對方本身狀態的一個本身定義的命令字,按照必定的時間間隔發送,相似於心跳,因此叫作心跳包。
用來判斷對方(設備,進程或其它網元)是否正常運行,採用定時發送簡單的通信包,若是在指定時間段內未收到對方響應,則判斷對方已經離線。用於檢測TCP的異常斷開。基本緣由是服務器端不能有效的判斷客戶端是否在線,也就是說,服務器沒法區分客戶端是長時間在空閒,仍是已經掉線的狀況。所謂的心跳包就是客戶端定時發送簡單的信息給服務器端告訴它我還在而已。代碼就是每隔幾分鐘發送一個固定信息給服務端,服務端收到後回覆一個固定信息若是服務端幾分鐘內沒有收到客戶端信息則視客戶端斷開。 好比有些通訊軟件長時間不使用,要想知道它的狀態是在線仍是離線就須要心跳包,定時發包收包。發包方:能夠是客戶也能夠是服務端,看哪邊實現方便合理,通常是客戶端。服務器也能夠定時發心跳下去。通常來講,出於效率的考慮,是由客戶端主動向服務器端發包,而不是服務器向客戶端發。客戶端每隔一段時間發一個包,使用TCP的,用send發,使用UDP的,用sendto發,服務器收到後,就知道當前客戶端還處於「活着」的狀態,不然,若是隔必定時間未收到這樣的包,則服務器認爲客戶端已經斷開,進行相應的客戶端斷開邏輯處理。
心跳包之因此叫心跳包是由於:它像心跳同樣每隔固定時間發一次,以此來告訴服務器,這個客戶端還活着。事實上這是爲了保持長鏈接,至於這個包的內容,是沒有什麼特別規定的,不過通常都是很小的包,或者只包含包頭的一個空包。
在TCP/IP的機制裏面,自己是存在有心跳包的機制的,也就是TCP的選項:SO_KEEPALIVE。系統默認是設置的2小時的心跳頻率。可是它檢查不到機器斷電、網線拔出、防火牆這些斷線。並且邏輯層處理斷線可能也不是那麼好處理。通常,若是隻是用於保活仍是能夠的。
心跳包通常來講都是在邏輯層發送空的echo包來實現的。下一個定時器,在必定時間間隔下發送一個空包給客戶端,而後客戶端反饋一個一樣的空包回來,服務器若是在必定時間內收不到客戶端發送過來的反饋包,那就只有認定說掉線了。
其實,要斷定掉線,只須要send或者recv一下,若是結果爲零,則爲掉線。可是,在長鏈接下,有可能很長一段時間都沒有數據往來。理論上說,這個鏈接是一直保持鏈接的,可是實際狀況中,若是中間節點出現什麼故障是難以知道的。更要命的是,有的節點(防火牆)會自動把必定時間以內沒有數據交互的鏈接給斷掉。在這個時候,就須要咱們的心跳包了,用於維持長鏈接,保活。
在獲知了斷線以後,服務器邏輯可能須要作一些事情,好比斷線後的數據清理呀,從新鏈接呀……固然,這個天然是要由邏輯層根據需求去作了。總的來講,心跳包主要也就是用於長鏈接的保活和斷線處理。通常的應用下,斷定時間在30-40秒比較不錯。若是實在要求高,那就在6-9秒.
2, http://blog.csdn.net/xuyuefei1988/article/details/8279812
心跳包的發送,一般有兩種技術
方法1:應用層本身實現的心跳包
由應用程序本身發送心跳包來檢測鏈接是否正常,大體的方法是:服務器在一個 Timer事件中定時 向客戶端發送一個短小精悍的數據包,而後啓動一個低級別的線程,在該線程中不斷檢測客戶端的迴應, 若是在必定時間內沒有收到客戶端的迴應,即認爲客戶端已經掉線;一樣,若是客戶端在必定時間內沒 有收到服務器的心跳包,則認爲鏈接不可用。
方法2:TCP的KeepAlive保活機制
由於要考慮到一個服務器一般會鏈接多個客戶端,所以由用戶在應用層本身實現心跳包,代碼較多 且稍顯複雜,而利用TCP/IP協議層爲內置的KeepAlive功能來實現心跳功能則簡單得多。 不管是服務端仍是客戶端,一方開啓KeepAlive功能後,就會自動在規定時間內向對方發送心跳包, 而另外一方在收到心跳包後就會自動回覆,以告訴對方我仍然在線。 由於開啓KeepAlive功能須要消耗額外的寬帶和流量,因此TCP協議層默認並不開啓KeepAlive功 能,儘管這微不足道,但在按流量計費的環境下增長了費用,另外一方面,KeepAlive設置不合理時可能會 由於短暫的網絡波動而斷開健康的TCP鏈接。而且,默認的KeepAlive超時須要7,200,000 MilliSeconds, 即2小時,探測次數爲5次。對於不少服務端應用程序來講,2小時的空閒時間太長。所以,咱們須要手工開啓KeepAlive功能並設置合理的KeepAlive參數。
心跳檢測步驟:
1客戶端每隔一個時間間隔發生一個探測包給服務器
2客戶端發包時啓動一個超時定時器
3服務器端接收到檢測包,應該回應一個包
4若是客戶機收到服務器的應答包,則說明服務器正常,刪除超時定時器
5若是客戶端的超時定時器超時,依然沒有收到應答包,則說明服務器掛了
轉自:http://blog.sina.com.cn/s/blog_a459dcf5010153m5.html
3,http://blog.chinaunix.net/uid-26000296-id-3758651.html
TCP鏈接簡介
當網絡通訊時採用TCP協議時,在真正的讀寫操做以前,server與client之間必須創建一個鏈接,
當讀寫操做完成後,雙方再也不須要這個鏈接時它們能夠釋放這個鏈接,
鏈接的創建是須要三次握手的,而釋放則須要4次握手,
因此說每一個鏈接的創建都是須要資源消耗和時間消耗的
經典的三次握手示意圖:
經典的四次握手關閉圖:
1、長鏈接與短鏈接
長鏈接: 指在一個TCP鏈接上能夠連續發送多個數據包,
在TCP鏈接保持期間,若是沒有數據包發送,須要雙方發檢測包以維持此鏈接;
通常須要本身作在線維持。
短鏈接: 指通訊雙方有數據交互時,就創建一個TCP鏈接,數據發送完成後,則斷開此TCP鏈接;
通常銀行都使用短鏈接。
它的優勢是:管理起來比較簡單,存在的鏈接都是有用的鏈接,不須要額外的控制手段
好比http的,只是鏈接、請求、關閉,過程時間較短,服務器如果一段時間內沒有收到請求便可關閉鏈接。
其實長鏈接是相對於一般的短鏈接而說的,也就是長時間保持客戶端與服務端的鏈接狀態。
長鏈接與短鏈接的操做過程
一般的短鏈接操做步驟是:
鏈接→數據傳輸→關閉鏈接;
而長鏈接一般就是:
鏈接→數據傳輸→保持鏈接(心跳)→數據傳輸→保持鏈接(心跳)→……→關閉鏈接;
這就要求長鏈接在沒有數據通訊時,定時發送數據包(心跳),以維持鏈接狀態,
短鏈接在沒有數據傳輸時直接關閉就好了
何時用長鏈接,短鏈接?
長鏈接多用於操做頻繁,點對點的通信,並且鏈接數不能太多狀況。
每一個TCP鏈接都須要三步握手,這須要時間,若是每一個操做都是先鏈接,再操做的話那麼處理速度會下降不少,
因此每一個操做完後都不斷開,下次次處理時直接發送數據包就OK了,不用創建TCP鏈接。
例如:數據庫的鏈接用長鏈接,
若是用短鏈接頻繁的通訊會形成socket錯誤,並且頻繁的socket 建立也是對資源的浪費。
2、發送接收方式
一、異步
報文發送和接收是分開的,相互獨立的,互不影響。這種方式又分兩種狀況:
(1)異步雙工:接收和發送在同一個程序中,由兩個不一樣的子進程分別負責發送和接收
(2)異步單工:接收和發送是用兩個不一樣的程序來完成。
二、同步
報文發送和接收是同步進行,既報文發送後等待接收返回報文。
同步方式通常須要考慮超時問題,即報文發出去後不能無限等待,須要設定超時時間,
超過該時間發送方再也不等待讀返回報文,直接通知超時返回。
在長鏈接中通常是沒有條件可以判斷讀寫何時結束,因此必需要加長度報文頭。
讀函數先是讀取報文頭的長度,再根據這個長度去讀相應長度的報文。
三. 單工、半雙工和全雙工
根據通訊雙方的分工和信號傳輸方向可將通訊分爲三種方式:
單工、
半雙工、
全雙工。
在計算機網絡中主要採用雙工方式,其中:
局域網採用半雙工方式,
城域網和廣域網採用全雙年方式。
1. 單工(Simplex)方式:
通訊雙方設備中發送器與接收器分工明確,只能在由發送器向接收器的單一固定方向上傳送數據。
採用單工通訊的典型發送設備如早期計算機的讀卡器,典型的接收設備如打印機。
2. 半雙工(Half Duplex)方式:
通訊雙方設備既是發送器,也是接收器,兩臺設備能夠相互傳送數據,但某一時刻則只能向一個方向傳送數據。
例如,步話機是半雙工設備,由於在一個時刻只能有一方說話。
3. 全雙工(Full Duplex)方式:
通訊雙方設備既是發送器,也是接收器,兩臺設備能夠同時在兩個方向上傳送數據。
例如,電話是全雙工設備,由於雙方可同時說話。
而像WEB網站的http服務通常都用短連接,由於長鏈接對於服務端來講會耗費必定的資源,
而像WEB網站這麼頻繁的成千上萬甚至上億客戶端的鏈接用短鏈接會更省一些資源,
若是用長鏈接,並且同時有成千上萬的用戶,若是每一個用戶都佔用一個鏈接的話,那可想而知吧。
因此併發量大,但每一個用戶無需頻繁操做狀況下需用短連好。
總之,長鏈接和短鏈接的選擇要視狀況而定。
4、一個最簡單的長鏈接與心跳保持的示例程序
/*!
******************************************************************************
* \File
*
* \Brief
*
* \Author
* Hank
******************************************************************************
*/
- #include <stdio.h>
- #include <string.h>
- #include <errno.h>
- #include <sys/socket.h>
- #include <resolv.h>
- #include <stdlib.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <arpa/inet.h>
- #include <unistd.h>
- #include <sys/time.h>
- #include <sys/types.h>
#define MAXBUF 1024int main(int argc, char **argv){ int sockfd, len; struct sockaddr_in dest; char buffer[MAXBUF]; char heartbeat[20] = "hello server"; fd_set rfds; struct timeval tv; int retval, maxfd = -1; if (argc != 3) { printf("error! the right format should be : \ \n\t\t%s IP port\n\t eg:\t%s127.0.0.1 80\n", argv[0], argv[0]); exit(0); } if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("Socket"); exit(errno); } bzero(&dest, sizeof(dest)); dest.sin_family = AF_INET; dest.sin_port = htons(atoi(argv[2])); memset(&(dest.sin_zero), 0, 8); if (inet_aton(argv[1], (struct in_addr*)&dest.sin_addr.s_addr) == 0) { perror(argv[1]); exit(errno); } if (connect(sockfd, (struct sockaddr*)&dest, sizeof(dest)) != 0) { perror("Connect"); exit(errno); } printf("\nReady to start chatting.\n\ Direct input messages and \n\ enter to send messages to the server\n"); while (1) { FD_ZERO(&rfds); FD_SET(0, &rfds); maxfd = 0; FD_SET(sockfd, &rfds); if (sockfd > maxfd) maxfd = sockfd; tv.tv_sec = 2; tv.tv_usec = 0; retval = select(maxfd+1, &rfds, NULL, NULL, &tv); if (retval == -1) { printf("Will exit and the select is error! %s", strerror(errno)); break; } else if (retval == 0) { //printf("No message comes, no buttons, continue to wait ...\n"); len = send(sockfd, heartbeat, strlen(heartbeat), 0); if (len < 0) { printf("Message '%s' failed to send ! \ The error code is %d, error message '%s'\n", heartbeat, errno, strerror(errno)); break; } else { printf("News: %s \t send, sent a total of %d bytes!\n", heartbeat, len); } continue; } else { if (FD_ISSET(sockfd, &rfds)) { bzero(buffer, MAXBUF+1); len = recv(sockfd, buffer, MAXBUF, 0); if (len > 0) { printf("Successfully received the message: '%s',%d bytes of data\n", buffer, len); } else { if (len < 0) printf("Failed to receive the message! \ The error code is %d, error message is '%s'\n", errno, strerror(errno)); else printf("Chat to terminate!\n"); break; } } if (FD_ISSET(0, &rfds)) { bzero(buffer, MAXBUF+1); fgets(buffer, MAXBUF, stdin); if (!strncasecmp(buffer, "quit", 4)) { printf("Own request to terminate the chat!\n"); break; } len = send(sockfd, buffer, strlen(buffer)-1, 0); if (len < 0) { printf("Message '%s' failed to send ! \ The error code is %d, error message '%s'\n", buffer, errno, strerror(errno)); break; } else { printf("News: %s \t send, sent a total of %d bytes!\n", buffer, len); } } } } close(sockfd); return 0;}