????????作為服務(wù)器必須得具備監(jiān)測(cè)客戶(hù)端狀態(tài)得機(jī)制,以保證客戶(hù)端處于不同的狀態(tài),服務(wù)器進(jìn)行不同得狀態(tài)處理,依次來(lái)提高實(shí)時(shí)性,可控性,并且有利于服務(wù)器得內(nèi)存管理。其中客戶(hù)端得異常處理就屬于其中得一種。
????????客戶(hù)端得斷開(kāi)情形無(wú)非就兩種情況:
????????1.客戶(hù)端能夠發(fā)送狀態(tài)給服務(wù)器;正常斷開(kāi),強(qiáng)制關(guān)閉客戶(hù)端等,客戶(hù)端能夠做出反應(yīng)。
????????2.客戶(hù)端不能發(fā)送狀態(tài)給服務(wù)器;突然斷網(wǎng),斷電,客戶(hù)端卡死等,客戶(hù)端根本沒(méi)機(jī)會(huì)做出反應(yīng),服務(wù)器更不了解客戶(hù)端狀態(tài)。
????????客戶(hù)端異常斷開(kāi)的監(jiān)測(cè)手段及使用狀態(tài):
方法1:
????????getsockopt函數(shù)獲取套接字狀態(tài),根據(jù)狀態(tài)判斷客戶(hù)端的連接情況。
函數(shù)原型:
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
參數(shù):
sockfd:要監(jiān)測(cè)的客戶(hù)端的套接字
level:協(xié)議層次
SOL_SOCKET 套接字層次
IPPROTO_IP ip層次
IPPROTO_TCP TCP層次
option_name:選項(xiàng)的名稱(chēng)(套接字層次)
SO_BROADCAST 是否允許發(fā)送廣播信息
SO_REUSEADDR 是否允許重復(fù)使用本地地址
SO_SNDBUF 獲取發(fā)送緩沖區(qū)長(zhǎng)度
SO_RCVBUF 獲取接收緩沖區(qū)長(zhǎng)度
SO_RCVTIMEO 獲取接收超時(shí)時(shí)間
SO_SNDTIMEO 獲取發(fā)送超時(shí)時(shí)間
option_value:獲取到的選項(xiàng)的值
option_len:value的長(zhǎng)度
代碼實(shí)現(xiàn):
struct tcp_info info;
int len=sizeof(info);
getsockopt(client_fd, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len);
if(info.tcpi_state == TCP_CLOSE_WAIT && info.tcpi_state != TCP_ESTABLISHED)
{
printf("Client disconnection!\n");
}
????????TCP_CLOSE_WAIT:是服務(wù)器收到客戶(hù)端發(fā)來(lái)的FIN包以后進(jìn)入的狀態(tài),F(xiàn)IN包是客戶(hù)端斷開(kāi)連接進(jìn)行四次揮手的第一次揮手,收到 TCP_CLOSE_WAIT狀態(tài)代表客戶(hù)端已經(jīng)想要斷開(kāi)連接或者已經(jīng)斷開(kāi)連接。
????????TCP_ESTABLISHED:表示客戶(hù)端,服務(wù)器雙方處于建立連接的狀態(tài),可以進(jìn)行交互,相反則不處于連接狀態(tài)。
????????可以單獨(dú)使用TCP_CLOSE_WAIT或者TCP_ESTABLISHED狀態(tài)進(jìn)行連接狀態(tài)的監(jiān)測(cè),我喜歡一起使用。該方法適合客戶(hù)端斷開(kāi)的第一種情況。
方法2:
????????心跳包的實(shí)現(xiàn),心跳包就是服務(wù)器定時(shí)向客戶(hù)端發(fā)送查詢(xún)信息,如果客戶(hù)端有回應(yīng)就代表連接正常,類(lèi)似于linux系統(tǒng)的看門(mén)狗機(jī)制。心跳包的實(shí)現(xiàn)有兩種:TCP自帶的心跳包機(jī)制keeplive,和自定義心跳包。
????????TCP自帶的心跳包:KEEPLIVE保活機(jī)制
????????使用setsockope函數(shù)啟動(dòng)和設(shè)置心跳時(shí)間的機(jī)制:
int RTSP_SESSION::set_keeplive(void)
{
int keep_alive = 1;//啟動(dòng)心跳保活機(jī)制
int keep_idle = 10;
//10s內(nèi)沒(méi)收到數(shù)據(jù)開(kāi)始發(fā)送心跳包
int keep_interval = 3;
//每次發(fā)送心跳包的時(shí)間間隔
int keep_count = 3;
//每個(gè)3s發(fā)送一次心跳包
if (setsockopt(client_fd, SOL_SOCKET, SO_KEEPALIVE, &keep_alive, sizeof(keep_alive)))
{
perror("Error setsockopt(SO_KEEPALIVE) failed");
return -1;
}
if (setsockopt(client_fd, IPPROTO_TCP, TCP_KEEPIDLE, &keep_idle, sizeof(keep_idle)))
{
perror("Error setsockopt(TCP_KEEPIDLE) failed");
return -1;
}
if (setsockopt(client_fd, SOL_TCP, TCP_KEEPINTVL, (void *)&keep_interval, sizeof(keep_interval)))
{
perror("Error setsockopt(TCP_KEEPINTVL) failed");
return -1;
}
if (setsockopt(client_fd, SOL_TCP, TCP_KEEPCNT, (void *)&keep_count, sizeof(keep_count)))
{
perror("Error setsockopt(TCP_KEEPCNT) failed");
return -1;
}
return 0;
}
????????上述代碼的意思就是:10s沒(méi)收到客戶(hù)端的數(shù)據(jù)就開(kāi)始發(fā)送心跳包,如果客戶(hù)端沒(méi)回應(yīng),則導(dǎo)致client_fd失效,所有調(diào)用client_fd的讀寫(xiě)函數(shù)都會(huì)立即返回(read write recv send等),并且錯(cuò)誤碼是ETIMEDOUT。
????????通過(guò)判斷讀寫(xiě)函數(shù)的狀態(tài)就可以判斷客戶(hù)端的連接狀態(tài):
int ret = recv(client_fd, buf, len, MSG_PEEK);
if(ret <= 0 && errno == ETIMEDOUT)
printf("Client disconnection!\n");
MSG_PEEK:查看緩存內(nèi)容,但是不從緩存中讀取,不會(huì)干擾程序的正常讀寫(xiě),可以利用該方法寫(xiě)個(gè)線程進(jìn)行監(jiān)測(cè),或者直接使用自己讀寫(xiě)函數(shù)的返回值進(jìn)行判斷。
????????自己定義心跳包:
????????這個(gè)必須是服務(wù)器,客戶(hù)端都是自己寫(xiě)的才可以,在服務(wù)器中每隔一段時(shí)間向服務(wù)器發(fā)送一個(gè)心跳包,客戶(hù)端收到后進(jìn)行回復(fù),心跳包的協(xié)議可以自己定,以此監(jiān)測(cè)客戶(hù)端狀態(tài)。
????????心跳包適合客戶(hù)端斷開(kāi)的情形1,情形2,都適用。
番外:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-696153.html
????????如果使用的是select的話,無(wú)論是正常中斷還是異常中斷,select都會(huì)返回1:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-696153.html
ret = select(client_fd+1, &rfd, NULL, &efd, &timeout);
if(ret > 0)
{
if (FD_ISSET(client_fd, &rfd) != 0)
{
//客戶(hù)端中斷會(huì)進(jìn)入這里,在這里判斷recv狀態(tài)進(jìn)行監(jiān)測(cè)
if ((ret = recv(client_fd, buf, len, 0)) <= 0)
{
if(errno == ETIMEDOUT)
{
printf("The client is disconnected abnormally. Check the cause!\n");
}
}
}
}
到了這里,關(guān)于TCP服務(wù)器監(jiān)測(cè)客戶(hù)端異常退出方法的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!