最近在項目中遇到一些小小的問題,犯過的錯希望不要犯二次。我Linux環境下開啟一個TCP服務和FTP客戶端用來升級系統,正常情況下是沒任何差錯的,如果斷網(拔網線)或者PC端升級軟件(QT軟件)崩潰(強行退出),系統就會出現問題,
流程有2個,一個是FTP下載過程,一個是Flash寫入過程,都涉及進度值的上傳,即無時無刻都在調用write函數,並且進度函數的返回值是不處理的,也是沒有任何函數來處理檢測此函數是否正常運行的,主要是沒法處理接收。
FTP下載CURL設置: 進度函數部分
TCP在正常網絡下是“三次握手,四次揮手”,服務器和客戶機分別正常退出處理是沒什么問題的。
一、如果突然斷網了,PC端TCP客戶機不會發任何信息,可能出現的問題有:
①此時如果還處於TCP命令交互過程,可添加keepalive機制(只開啟設置當前套接字的keepalive屬性),保證規定時間內沒有回響可關閉套接字,等待下一次連接。注意不能更改Linux整個系統的keepalive值
系統keepalive值可通過sysctl -a 或者 sysctl -a |grep tcp_keepalive* 查看
當前tcp套接字keepalive設置代碼:
②此時如果處於FTP下載過程中,curl庫會一直卡死在那里,無法自動退出來(curl設計機制問題,沒法更改),但是進度函數里面write不會報錯(斷網情況下返回值不為-1),導致此更新進程模塊廢掉,只能重啟,解決辦法可以添加curl下載超時限制CURLOPT_TIMEOUT,該延時表示多久到了文件還沒有下載完成curl就會自動斷開重新連接下載,由於斷網,此時連接超時(CURLOPT_CONNECTTIMEOUT)將會自動退出,從而可以正常處理,程序如下
curl_easy_setopt(pCurl, CURLOPT_TIMEOUT, 120);
此語句缺點就是不管多大文件,只給你120S的下載時間,如果文件過大,120S下載不完,那么一直死循環下載會出問題,所以不到萬不得已不要使用,由於項目實際文件最大的才6M多點,一般正常網絡情況下30s內可解決戰斗,遇到網絡不好的可能時間長點,
此延時時間設置太大也不行,沒有及時性,都出問題了,你不可能讓客戶等很長時間吧?客戶還以為更新失敗成磚了,直接報廢處理了。
③此時處於寫入過程中時。進度函數中write也不會出錯,也沒法接收,就讓它寫完再在FTP連接的過程中失敗退出吧。
二、若果上位機突然崩潰了,此時window 系統會回收套接字,給tcp服務器端發送一個RST信號,此時Linux下面處於集中情況:
①此時如果還處於TCP命令交互過程中,與上一樣,有keepalive機制,規定時間內可以自動斷開,等待下一次連接。
②此時如果處於FTP下載過程中,由於進度函數不斷被調用,不斷循環(curl設置1S時間調用進度函數一次)調用write函數上發數據,上面崩潰時也會不斷被調用,第一次調用write會返回-1,第二次再調用write就會阻塞卡死在write這里(這個地方吃了大虧),不得不吐槽curl庫,既然FTP服務器端都沒了,出問題了,客戶端怎么還是不停的調用進度函數?上面提到了curl中是不涉及處理進度函數返回值的,管你是不是出錯,這時候沒辦法啊,既然出錯了,我的要趕緊退到初始調用FTP下載到的地方吧?怎么辦?在函數之間進行跳轉的恐怕只有setjmp() / longjmp()吧?C語言錯誤異常跳轉特殊函數,與函數AR地址相關的,比goto還厲害的語句。
在最最開始頂層的地方設置如下:
AbnormalFlag = setjmp(Ftpdown_jb);
if(AbnormalFlag == 0 )
{
/* null */
}
else if(AbnormalFlag == 1 )
{
Enter setjmp Ftpdown_jb abnormar
FileSuccessAllFlag = 0;
goto end;
}
else
{
/* null */
}
ret = FtpDownload(***);
然后在進度函數中write中寫入如下
ret = write(G_sock_c,SendBuff,ret);
if(ret < 0)
{
****
longjmp(Ftpdown_jb,1);
}
write第一次出錯,保證能退出來,實現函數隔層之間的的跳轉,然后釋放當前套接字,等待下一次連接。
③此時處於flash過程與上同理處理。
好了,寫了這么多,有不嚴謹之處還請指教。